lmtnodelib.c /size: 465 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5/*tex TODO: update |[get|set]field| for added node entries */
6
7/*tex
8
9    This module is one of the backbones on \LUAMETATEX. It has gradually been extended based on
10    experiences in \CONTEXT\ \MKIV\ and later \LMTX. There are many helpers here and the main
11    reason is that the more callbacks one enables and the more one does in them, the larger the
12    impact on performance.
13
14    After doing lots of tests with \LUATEX\ and \LUAJITTEX, with and without jit, and with and
15    without ffi, I came to the conclusion that userdata prevents a speedup. I also found that the
16    checking of metatables as well as assignment comes with overhead that can't be neglected. This
17    is normally not really a problem but when processing fonts for more complex scripts it's quite
18    some  overhead. So \unknown\ direct nodes were introduced (we call them nuts in \CONTEXT).
19
20    Because the userdata approach has some benefits, we keep that interface too. We did some
21    experiments with fast access (assuming nodes), but eventually settled for the direct approach.
22    For code that is proven to be okay, one can use the direct variants and operate on nodes more
23    directly. Currently these are numbers but don't rely on that property; treat them aslhmin
24
25    abstractions. An important aspect is that one cannot mix both methods, although with |tonode|
26    and |todirect| one can cast representations.
27
28    So the advice is: use the indexed (userdata) approach when possible and investigate the direct
29    one when speed might be an issue. For that reason we also provide some get* and set* functions
30    in the top level node namespace. There is a limited set of getters for nodes and a generic
31    getfield to complement them. The direct namespace has a few more.
32
33    Keep in mind that such speed considerations only make sense when we're accessing nodes millions
34    of times (which happens in font processing for instance). Setters are less important as
35    documents have not that many content related nodes and setting many thousands of properties is
36    hardly a burden contrary to millions of consultations. And with millions, we're talking of tens
37    of millions which is not that common.
38
39    Another change is that |__index| and |__newindex| are (as expected) exposed to users but do no
40    checking. The getfield and setfield functions do check. In fact, a fast mode can be simulated
41    by fast_getfield = __index but the (measured) benefit on average runs is not that large (some
42    5\% when we also use the other fast ones) which is easily nilled by inefficient coding. The
43    direct variants on the other hand can be significantly faster but with the drawback of lack of
44    userdata features. With respect to speed: keep in mind that measuring a speedup on these
45    functions is not representative for a normal run, where much more happens.
46
47    A user should beware of the fact that messing around with |prev|, |next| and other links can
48    lead to crashes. Don't complain about this: you get what you ask for. Examples are bad loops
49    in nodes lists that make the program run out of stack space.
50
51    The code below differs from the \LUATEX\ code in that it drops some userdata related
52    accessors. These can easily be emulates in \LUA, which is what we do in \CONTEXT\ \LMTX. Also,
53    some optimizations, like using macros and dedicated |getfield| and |setfield| functions for
54    userdata and direct nodes were removed because on a regular run there is not much impact and
55    the less code we have, the better. In the early days of \LUATEX\ it really did improve the
56    overall performance but computers (as well as compilers) have become better. But still, it
57    could be that \LUATEX\ has a better performance here; so be it. A performance hit can also be
58    one of the side effects of the some more rigourous testing of direct node validity introduced
59    here.
60
61    Attribute nodes are special as their prev and subtype fields are used for other purposes.
62    Setting them can confuse the checkers but we don't check each case for performance reasons.
63    Messing a list up is harmless and only affects functionality which is the users responsibility
64    anyway.
65
66    In \LUAMETATEX\ nodes can have different names and properties as in \LUATEX. Some might be
67    backported but that is kind of dangerous as macro packages other than \CONTEXT\ depend on
68    stability of \LUATEX. (It's one of the reasons for \LUAMETATEX\ being around: it permits us
69    to move on).
70
71    Todo: getters/setters for leftovers.
72
73*/
74
75/*
76    direct_prev_id(n) => returns prev and id
77    direct_next_id(n) => returns next and id
78*/
79
80# include "luametatex.h"
81
82/* # define NODE_METATABLE_INSTANCE   "node.instance" */
83/* # define NODE_PROPERTIES_DIRECT    "node.properties" */
84/* # define NODE_PROPERTIES_INDIRECT  "node.properties.indirect" */
85/* # define NODE_PROPERTIES_INSTANCE  "node.properties.instance" */
86
87# define hlist_usage          ((lua_Integer) 1 << (hlist_node          + 1))
88# define vlist_usage          ((lua_Integer) 1 << (vlist_node          + 1))
89# define rule_usage           ((lua_Integer) 1 << (rule_node           + 1))
90# define insert_usage         ((lua_Integer) 1 << (insert_node         + 1))
91# define mark_usage           ((lua_Integer) 1 << (mark_node           + 1))
92# define adjust_usage         ((lua_Integer) 1 << (adjust_node         + 1))
93# define boundary_usage       ((lua_Integer) 1 << (boundary_node       + 1))
94# define disc_usage           ((lua_Integer) 1 << (disc_node           + 1))
95# define whatsit_usage        ((lua_Integer) 1 << (whatsit_node        + 1))
96# define par_usage            ((lua_Integer) 1 << (par_node            + 1))
97# define dir_usage            ((lua_Integer) 1 << (dir_node            + 1))
98# define math_usage           ((lua_Integer) 1 << (math_node           + 1))
99# define glue_usage           ((lua_Integer) 1 << (glue_node           + 1))
100# define kern_usage           ((lua_Integer) 1 << (kern_node           + 1))
101# define penalty_usage        ((lua_Integer) 1 << (penalty_node        + 1))
102# define style_usage          ((lua_Integer) 1 << (style_node          + 1))
103# define choice_usage         ((lua_Integer) 1 << (choice_node         + 1))
104# define parameter_usage      ((lua_Integer) 1 << (parameter_node      + 1))
105# define simple_usage         ((lua_Integer) 1 << (simple_noad         + 1))
106# define radical_usage        ((lua_Integer) 1 << (radical_noad        + 1))
107# define fraction_usage       ((lua_Integer) 1 << (fraction_noad       + 1))
108# define accent_usage         ((lua_Integer) 1 << (accent_noad         + 1))
109# define fence_usage          ((lua_Integer) 1 << (fence_noad          + 1))
110# define math_char_usage      ((lua_Integer) 1 << (math_char_node      + 1))
111# define math_text_char_usage ((lua_Integer) 1 << (math_text_char_node + 1))
112# define sub_box_usage        ((lua_Integer) 1 << (sub_box_node        + 1))
113# define sub_mlist_usage      ((lua_Integer) 1 << (sub_mlist_node      + 1))
114# define delimiter_usage      ((lua_Integer) 1 << (delimiter_node      + 1))
115# define glyph_usage          ((lua_Integer) 1 << (glyph_node          + 1))
116# define unset_usage          ((lua_Integer) 1 << (unset_node          + 1))
117# define align_record_usage   ((lua_Integer) 1 << (align_record_node   + 1))
118# define attribute_usage      ((lua_Integer) 1 << (attribute_node      + 1))
119# define glue_spec_usage      ((lua_Integer) 1 << (glue_spec_node      + 1))
120
121# define common_usage         ((lua_Integer) 1 << (last_nodetype       + 2))
122# define generic_usage        ((lua_Integer) 1 << (last_nodetype       + 3))
123
124# define no_usage             ((lua_Integer) 0)
125
126/*tex
127
128    There is a bit of checking for validity of direct nodes but of course one can still create
129    havoc by using flushed nodes, setting bad links, etc.
130
131    Although we could gain a little by moving the body of the valid checker into the caller (that
132    way the field variables might be shared) there is no real measurable gain in that on a regular
133    run. So, in the end I settled for function calls.
134
135*/
136
137halfword lmt_check_isdirect(lua_State *L, int i)
138{
139    halfword n = lmt_tohalfword(L, i);
140    return n && _valid_node_(n) ? n : null;
141}
142
143static inline halfword nodelib_valid_direct_from_index(lua_State *L, int i)
144{
145    halfword n = lmt_tohalfword(L, i);
146    return n && _valid_node_(n) ? n : null;
147}
148
149static inline void nodelib_push_direct_or_nil(lua_State *L, halfword n)
150{
151    if (n) {
152        lua_pushinteger(L, n);
153    } else {
154        lua_pushnil(L);
155    }
156}
157
158static inline void nodelib_push_direct_or_nil_node_prev(lua_State *L, halfword n)
159{
160    if (n) {
161        node_prev(n) = null;
162        lua_pushinteger(L, n);
163    } else {
164        lua_pushnil(L);
165    }
166}
167
168static inline void nodelib_push_node_on_top(lua_State *L, halfword n)
169{
170     *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
171     lua_getmetatable(L, -2);
172     lua_setmetatable(L, -2);
173}
174
175/*tex
176    Many of these small functions used to be macros but that no longer pays off because compilers
177    became better (for instance at deciding when to inline small functions). We could have explicit
178    inline variants of these too but normally the compiler will inline small functions anyway.
179
180*/
181
182static halfword lmt_maybe_isnode(lua_State *L, int i)
183{
184    halfword *p = lua_touserdata(L, i);
185    halfword n = null;
186    if (p && lua_getmetatable(L, i)) {
187        lua_get_metatablelua(node_instance);
188        if (lua_rawequal(L, -1, -2)) {
189            n = *p;
190        }
191        lua_pop(L, 2);
192    }
193    return n;
194}
195
196halfword lmt_check_isnode(lua_State *L, int i)
197{
198    halfword n = lmt_maybe_isnode(L, i);
199    if (! n) {
200     // formatted_error("node lib", "lua <node> expected, not an object with type %s", luaL_typename(L, i));
201        luaL_error(L, "invalid node");
202    }
203    return n;
204}
205
206halfword lmt_optional_isnode(lua_State *L, int i)
207{
208    return lmt_maybe_isnode(L, i);
209}
210
211
212/* helpers */
213
214static void nodelib_push_direct_or_node(lua_State *L, int direct, halfword n)
215{
216    if (n) {
217        if (direct) {
218            lua_pushinteger(L, n);
219        } else {
220            *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
221            lua_getmetatable(L, 1);
222            lua_setmetatable(L, -2);
223        }
224    } else {
225        lua_pushnil(L);
226    }
227}
228
229static void nodelib_push_direct_or_node_node_prev(lua_State *L, int direct, halfword n)
230{
231    if (n) {
232        node_prev(n) = null;
233        if (direct) {
234            lua_pushinteger(L, n);
235        } else {
236            *(halfword *) lua_newuserdatauv(L, sizeof(halfword), 0) = n;
237            lua_getmetatable(L, 1);
238            lua_setmetatable(L, -2);
239        }
240    } else {
241        lua_pushnil(L);
242    }
243}
244
245static halfword nodelib_direct_or_node_from_index(lua_State *L, int direct, int i)
246{
247    if (direct) {
248        return nodelib_valid_direct_from_index(L, i);
249    } else if (lua_isuserdata(L, i)) {
250        return lmt_check_isnode(L, i);
251    } else {
252        return null;
253    }
254}
255
256halfword lmt_check_isdirectornode(lua_State *L, int i, int *isdirect)
257{
258    *isdirect = ! lua_isuserdata(L, i);
259    return *isdirect ? nodelib_valid_direct_from_index(L, i) : lmt_check_isnode(L, i);
260}
261
262static void nodelib_push_attribute_data(lua_State *L, halfword n)
263{
264    if (node_subtype(n) == attribute_list_subtype) {
265        lua_newtable(L);
266        n = node_next(n);
267        while (n) {
268            lua_pushinteger(L, attribute_value(n));
269            lua_rawseti(L, -2, attribute_index(n));
270            n = node_next(n);
271        }
272    } else {
273        lua_pushnil(L);
274    }
275}
276
277/*tex maybe: Floyd's cycle finding: */
278
279# define isloop_usage common_usage
280
281static int nodelib_direct_isloop(lua_State *L)
282{
283    halfword head = nodelib_valid_direct_from_index(L, 1);
284    if (head) {
285        halfword slow = head;
286        halfword fast = head;
287        while (slow && fast && node_next(fast)) {
288            slow = node_next(slow);
289            fast = node_next(node_next(fast));
290            if (slow == fast) {
291                lua_pushboolean(L, 1);
292                return 1;
293            }
294        }
295    }
296    lua_pushboolean(L, 0);
297    return 0;
298}
299
300/*tex Another shortcut: */
301
302static inline singleword nodelib_getdirection(lua_State *L, int i)
303{
304    return ((lua_type(L, i) == LUA_TNUMBER) ? (singleword) checked_direction_value(lmt_tohalfword(L, i)) : direction_def_value);
305}
306
307/*tex
308
309    This routine finds the numerical value of a string (or number) at \LUA\ stack index |n|. If it
310    is not a valid node type |-1| is returned.
311
312*/
313
314static quarterword nodelib_aux_get_node_type_id_from_name(lua_State *L, int n, node_info *data, int all)
315{
316    if (data) {
317        const char *s = lua_tostring(L, n);
318        for (int j = 0; data[j].id != -1; j++) {
319            if (s == data[j].name) {
320                if (all || data[j].visible) {
321                    return (quarterword) j;
322                } else {
323                    break;
324                }
325            }
326        }
327    }
328    return unknown_node;
329}
330
331static quarterword nodelib_aux_get_node_subtype_id_from_name(lua_State *L, int n, value_info *data)
332{
333    if (data) {
334        const char *s = lua_tostring(L, n);
335        for (quarterword j = 0; data[j].id != -1; j++) {
336            if (s == data[j].name) {
337                return j;
338            }
339        }
340    }
341    return unknown_subtype;
342}
343
344static quarterword nodelib_aux_get_field_index_from_name(lua_State *L, int n, value_info *data)
345{
346    if (data) {
347        const char *s = lua_tostring(L, n);
348        for (quarterword j = 0; data[j].name; j++) {
349            if (s == data[j].name) {
350                return j;
351            }
352        }
353    }
354    return unknown_field;
355}
356
357static quarterword nodelib_aux_get_valid_node_type_id(lua_State *L, int n)
358{
359    quarterword i = unknown_node;
360    switch (lua_type(L, n)) {
361        case LUA_TSTRING:
362            i = nodelib_aux_get_node_type_id_from_name(L, n, lmt_interface.node_data, 0);
363            if (i == unknown_node) {
364                luaL_error(L, "invalid node type id: %s", lua_tostring(L, n));
365            }
366            break;
367        case LUA_TNUMBER:
368            i = lmt_toquarterword(L, n);
369            if (! tex_nodetype_is_visible(i)) {
370                luaL_error(L, "invalid node type id: %d", i);
371            }
372            break;
373        default:
374            luaL_error(L, "invalid node type id");
375    }
376    return i;
377}
378
379int lmt_get_math_style(lua_State *L, int n, int dflt)
380{
381    int i = -1;
382    switch (lua_type(L, n)) {
383        case LUA_TNUMBER:
384            i = lmt_tointeger(L, n);
385            break;
386        case LUA_TSTRING:
387            i = nodelib_aux_get_field_index_from_name(L, n, lmt_interface.math_style_values);
388            break;
389    }
390    if (i >= 0 && i <= cramped_script_script_style) {
391        return i;
392    } else {
393        return dflt;
394    }
395}
396
397int lmt_get_math_parameter(lua_State *L, int n, int dflt)
398{
399    int i;
400    switch (lua_type(L, n)) {
401        case LUA_TNUMBER:
402            i = lmt_tointeger(L, n);
403            break;
404        case LUA_TSTRING:
405            i = nodelib_aux_get_field_index_from_name(L, n, lmt_interface.math_parameter_values);
406            break;
407        default:
408            i = -1;
409            break;
410    }
411    if (i >= 0 && i < math_parameter_last) {
412        return i;
413    } else {
414        return dflt;
415    }
416}
417
418/*tex
419
420    Creates a userdata object for a number found at the stack top, if it is representing a node
421    (i.e. an pointer into |varmem|). It replaces the stack entry with the new userdata, or pushes
422    |nil| if the number is |null|, or if the index is definately out of range. This test could be
423    improved.
424
425*/
426
427// void lmt_push_node(lua_State *L)
428// {
429//     halfword n = null;
430//     if (lua_type(L, -1) == LUA_TNUMBER) {
431//         n = lmt_tohalfword(L, -1);
432//     }
433//     lua_pop(L, 1);
434//     if ((! n) || (n > lmt_node_memory_state.nodes_data.allocated)) {
435//         lua_pushnil(L);
436//     } else {
437//         halfword *a = lua_newuserdatauv(L, sizeof(halfword), 0);
438//         *a = n;
439//         lua_get_metatablelua(node_instance);
440//         lua_setmetatable(L, -2);
441//     }
442// }
443
444void lmt_push_node(lua_State *L)
445{
446    halfword n = null;
447    if (lua_type(L, -1) == LUA_TNUMBER) {
448        n = lmt_tohalfword(L, -1);
449    }
450    lua_pop(L, 1);
451    if (n && n <= lmt_node_memory_state.nodes_data.allocated) {
452        halfword *a = lua_newuserdatauv(L, sizeof(halfword), 0);
453        *a = n;
454        lua_get_metatablelua(node_instance);
455        lua_setmetatable(L, -2);
456    } else {
457        lua_pushnil(L);
458    }
459}
460
461void lmt_push_node_fast(lua_State *L, halfword n)
462{
463    if (n) {
464        halfword *a = lua_newuserdatauv(L, sizeof(halfword), 0);
465        *a = n;
466        lua_get_metatablelua(node_instance);
467        lua_setmetatable(L, -2);
468    } else {
469        lua_pushnil(L);
470    }
471}
472
473void lmt_push_directornode(lua_State *L, halfword n, int isdirect)
474{
475    if (! n) {
476        lua_pushnil(L);
477    } else if (isdirect) {
478        lua_push_integer(L, n);
479    } else {
480        lmt_push_node_fast(L, n);
481    }
482}
483
484/*tex getting and setting fields (helpers) */
485
486static int nodelib_getlist(lua_State *L, int n)
487{
488    if (lua_isuserdata(L, n)) {
489        return lmt_check_isnode(L, n);
490    } else {
491        return null;
492    }
493}
494
495/*tex converts type strings to type ids */
496
497# define id_usage common_usage
498
499static int nodelib_shared_id(lua_State *L)
500{
501    if (lua_type(L, 1) == LUA_TSTRING) {
502        int i = nodelib_aux_get_node_type_id_from_name(L, 1, lmt_interface.node_data, 0);
503        if (i >= 0) {
504            lua_pushinteger(L, i);
505        } else {
506            lua_pushnil(L);
507        }
508    } else {
509        lua_pushnil(L);
510    }
511    return 1;
512}
513
514/* node.direct.getid */
515
516# define getid_usage common_usage
517
518static int nodelib_direct_getid(lua_State *L)
519{
520    halfword n = nodelib_valid_direct_from_index(L, 1);
521    if (n) {
522        lua_pushinteger(L, node_type(n));
523    } else {
524        lua_pushnil(L);
525    }
526    return 1;
527}
528
529/* node.direct.getidsubtype */
530
531# define getidsubtype_usage common_usage
532
533static int nodelib_direct_getidsubtype(lua_State *L)
534{
535    halfword n = nodelib_valid_direct_from_index(L, 1);
536    if (n) { /* no check for subtype */
537        lua_pushinteger(L, node_type(n));
538        lua_pushinteger(L, node_subtype(n));
539    } else {
540        lua_pushnil(L);
541        lua_pushnil(L);
542    }
543    return 2;
544}
545
546/* node.direct.getsubtype */
547/* node.direct.setsubtype */
548
549# define getsubtype_usage common_usage
550# define setsubtype_usage common_usage
551
552static int nodelib_direct_getsubtype(lua_State *L)
553{
554    halfword n = nodelib_valid_direct_from_index(L, 1);
555    if (n) { /* no check for subtype */
556        lua_pushinteger(L, node_subtype(n));
557    } else {
558        lua_pushnil(L);
559    }
560    return 1;
561}
562
563static int nodelib_direct_setsubtype(lua_State *L)
564{
565    halfword n = nodelib_valid_direct_from_index(L, 1);
566    if (n && lua_type(L, 2) == LUA_TNUMBER) {
567        node_subtype(n) = lmt_toquarterword(L, 2);
568    }
569    return 0;
570}
571
572/* node.direct.getexpansion */
573/* node.direct.setexpansion */
574
575# define getexpansion_usage (glyph_usage | kern_usage)
576# define setexpansion_usage getexpansion_usage
577
578static int nodelib_direct_getexpansion(lua_State *L)
579{
580    halfword n = nodelib_valid_direct_from_index(L, 1);
581    if (n) {
582        switch (node_type(n)) {
583            case glyph_node:
584                lua_pushinteger(L, glyph_expansion(n));
585                break;
586            case kern_node:
587                lua_pushinteger(L, kern_expansion(n));
588                break;
589            default:
590                lua_pushnil(L);
591                break;
592        }
593    } else {
594        lua_pushnil(L);
595    }
596    return 1;
597}
598
599static int nodelib_direct_setexpansion(lua_State *L)
600{
601    halfword n = nodelib_valid_direct_from_index(L, 1);
602    if (n) {
603        halfword e = 0;
604        if (lua_type(L, 2) == LUA_TNUMBER) {
605            e = (halfword) lmt_roundnumber(L, 2);
606        }
607        switch (node_type(n)) {
608            case glyph_node:
609                glyph_expansion(n) = e;
610                break;
611            case kern_node:
612                kern_expansion(n) = e;
613                break;
614        }
615    }
616    return 0;
617}
618
619/* node.direct.getfont */
620/* node.direct.setfont */
621
622# define getfont_usage (glyph_usage | glue_usage | math_char_usage | math_text_char_usage | delimiter_usage)
623# define setfont_usage (glyph_usage | rule_usage | glue_usage)
624
625static int nodelib_direct_getfont(lua_State *L)
626{
627    halfword n = nodelib_valid_direct_from_index(L, 1);
628    if (n) {
629        switch (node_type(n)) {
630            case glyph_node:
631                lua_pushinteger(L, glyph_font(n));
632                break;
633            case glue_node:
634                lua_pushinteger(L, glue_font(n));
635                break;
636            case math_char_node:
637            case math_text_char_node:
638                lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
639                break;
640            case delimiter_node:
641                lua_pushinteger(L, tex_fam_fnt(delimiter_small_family(n), 0));
642                break;
643            default:
644                lua_pushnil(L);
645                break;
646        }
647    } else {
648        lua_pushnil(L);
649    }
650    return 1;
651}
652
653static int nodelib_direct_setfont(lua_State *L)
654{
655    halfword n = nodelib_valid_direct_from_index(L, 1);
656    if (n) {
657        switch (node_type(n)) {
658            case glyph_node:
659                glyph_font(n) = tex_checked_font(lmt_tohalfword(L, 2));
660                if (lua_type(L, 3) == LUA_TNUMBER) {
661                    glyph_character(n) = lmt_tohalfword(L, 3);
662                }
663                break;
664            case rule_node:
665                tex_set_rule_font(n, lmt_tohalfword(L, 2));
666                if (lua_type(L, 3) == LUA_TNUMBER) {
667                    rule_strut_character(n) = lmt_tohalfword(L, 3);
668                }
669                break;
670            case glue_node:
671                glue_font(n) = tex_checked_font(lmt_tohalfword(L, 2));
672                break;
673        }
674    }
675    return 0;
676}
677
678# define getchardict_usage (glyph_usage | math_char_usage | math_text_char_usage | delimiter_usage)
679# define setchardict_usage getchardict_usage
680
681static int nodelib_direct_getchardict(lua_State *L)
682{
683    halfword n = nodelib_valid_direct_from_index(L, 1);
684    if (n) {
685        switch (node_type(n)) {
686            case glyph_node:
687                lua_pushinteger(L, glyph_properties(n));
688                lua_pushinteger(L, glyph_group(n));
689                lua_pushinteger(L, glyph_index(n));
690                lua_pushinteger(L, glyph_font(n));
691                lua_pushinteger(L, glyph_character(n));
692                return 5;
693            case math_char_node:
694            case math_text_char_node:
695                lua_pushinteger(L, kernel_math_properties(n));
696                lua_pushinteger(L, kernel_math_group(n));
697                lua_pushinteger(L, kernel_math_index(n));
698                lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n),0));
699                lua_pushinteger(L, kernel_math_character(n));
700                return 5;
701            case delimiter_node:
702                lua_pushinteger(L, delimiter_math_properties(n));
703                lua_pushinteger(L, delimiter_math_group(n));
704                lua_pushinteger(L, delimiter_math_index(n));
705                lua_pushinteger(L, tex_fam_fnt(delimiter_small_family(n),0));
706                lua_pushinteger(L, delimiter_small_character(n));
707                return 5;
708        }
709    }
710    return 0;
711}
712
713static int nodelib_direct_setchardict(lua_State *L)
714{
715    halfword n = nodelib_valid_direct_from_index(L, 1);
716    if (n) {
717        switch (node_type(n)) {
718            case glyph_node:
719                glyph_properties(n) = lmt_optquarterword(L, 2, 0);
720                glyph_group(n) = lmt_optquarterword(L, 3, 0);
721                glyph_index(n) = lmt_opthalfword(L, 4, 0);
722                break;
723            case math_char_node:
724            case math_text_char_node:
725                kernel_math_properties(n) = lmt_optquarterword(L, 2, 0);
726                kernel_math_group(n) = lmt_optquarterword(L, 3, 0);
727                kernel_math_index(n) = lmt_opthalfword(L, 4, 0);
728                break;
729            case delimiter_node:
730                delimiter_math_properties(n) = lmt_optquarterword(L, 2, 0);
731                delimiter_math_group(n) = lmt_optquarterword(L, 3, 0);
732                delimiter_math_index(n) = lmt_opthalfword(L, 4, 0);
733                break;
734        }
735    }
736    return 0;
737}
738
739/* node.direct.getchar */
740/* node.direct.setchar */
741
742# define getchar_usage (glyph_usage | rule_usage | math_char_usage | math_text_char_usage | delimiter_usage)
743# define setchar_usage getchar_usage
744
745static int nodelib_direct_getchar(lua_State *L)
746{
747    halfword n = nodelib_valid_direct_from_index(L, 1);
748    if (n) {
749        switch(node_type(n)) {
750            case glyph_node:
751                lua_pushinteger(L, glyph_character(n));
752                break;
753            case rule_node:
754                lua_pushinteger(L, rule_strut_character(n));
755                break;
756            case math_char_node:
757            case math_text_char_node:
758                lua_pushinteger(L, kernel_math_character(n));
759                break;
760            case delimiter_node:
761                 /* used in wide fonts */
762                lua_pushinteger(L, delimiter_small_character(n));
763                break;
764            default:
765                lua_pushnil(L);
766                break;
767        }
768    } else {
769        lua_pushnil(L);
770    }
771    return 1;
772}
773
774static int nodelib_direct_setchar(lua_State *L)
775{
776    halfword n = nodelib_valid_direct_from_index(L, 1);
777    if (n && lua_type(L, 2) == LUA_TNUMBER) {
778        switch (node_type(n)) {
779            case glyph_node:
780                glyph_character(n) = lmt_tohalfword(L, 2);
781                break;
782            case rule_node:
783                rule_strut_character(n) = lmt_tohalfword(L, 2);
784                break;
785            case math_char_node:
786            case math_text_char_node:
787                kernel_math_character(n) = lmt_tohalfword(L, 2);
788                break;
789            case delimiter_node:
790                /* used in wide fonts */
791                delimiter_small_character(n) = lmt_tohalfword(L, 2);
792                break;
793        }
794    }
795    return 0;
796}
797
798/* bonus */
799
800# define getcharspec_usage (glyph_usage | rule_usage | simple_usage | math_char_usage | math_text_char_usage | delimiter_usage)
801# define setcharspec_usage getcharspec_usage
802
803static int nodelib_direct_getcharspec(lua_State *L)
804{
805    halfword n = nodelib_valid_direct_from_index(L, 1);
806    if (n) {
807      AGAIN:
808        switch (node_type(n)) {
809            case glyph_node:
810                lua_pushinteger(L, glyph_character(n));
811                lua_pushinteger(L, glyph_font(n));
812                return 2;
813            case rule_node:
814                lua_pushinteger(L, rule_strut_character(n));
815                lua_pushinteger(L, tex_get_rule_font(n, text_style));
816                break;
817            case simple_noad:
818                n = noad_nucleus(n);
819                if (n) {
820                    goto AGAIN;
821                } else {
822                    break;
823                }
824            case math_char_node:
825            case math_text_char_node:
826                lua_pushinteger(L, kernel_math_character(n));
827                lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
828                lua_pushinteger(L, kernel_math_family(n));
829                return 3;
830            case delimiter_node:
831                lua_pushinteger(L, delimiter_small_character(n));
832                lua_pushinteger(L, tex_fam_fnt(delimiter_small_family(n), 0));
833                lua_pushinteger(L, delimiter_small_family(n));
834                return 3;
835        }
836    }
837    return 0;
838}
839
840/* node.direct.getfam */
841/* node.direct.setfam */
842
843# define getfam_usage (math_char_usage | math_text_char_usage | simple_usage | radical_usage | fraction_usage | accent_usage | fence_usage | rule_usage)
844# define setfam_usage getfam_usage
845
846static int nodelib_direct_getfam(lua_State *L)
847{
848    halfword n = nodelib_valid_direct_from_index(L, 1);
849    if (n) {
850        switch(node_type(n)) {
851            case math_char_node:
852            case math_text_char_node:
853                lua_pushinteger(L, kernel_math_family(n));
854                break;
855            case delimiter_node:
856                lua_pushinteger(L, delimiter_small_family(n));
857                break;
858            case simple_noad:
859            case radical_noad:
860            case fraction_noad:
861            case accent_noad:
862            case fence_noad:
863                /*tex Not all are used or useful at the tex end! */
864                lua_pushinteger(L, noad_family(n));
865                break;
866            case rule_node:
867                lua_pushinteger(L, tex_get_rule_family(n));
868                break;
869            default:
870                lua_pushnil(L);
871                break;
872        }
873    } else {
874        lua_pushnil(L);
875    }
876    return 1;
877}
878
879static int nodelib_direct_setfam(lua_State *L)
880{
881    halfword n = nodelib_valid_direct_from_index(L, 1);
882    if (n && lua_type(L, 2) == LUA_TNUMBER) {
883        switch (node_type(n)) {
884            case math_char_node:
885            case math_text_char_node:
886                kernel_math_family(n) = lmt_tohalfword(L, 2);
887                break;
888            case delimiter_node:
889                delimiter_small_family(n) = lmt_tohalfword(L, 2);
890                delimiter_large_family(n) = delimiter_small_family(n);
891                break;
892            case simple_noad:
893            case radical_noad:
894            case fraction_noad:
895            case accent_noad:
896            case fence_noad:
897                /*tex Not all are used or useful at the tex end! */
898                set_noad_family(n, lmt_tohalfword(L, 2));
899                break;
900            case rule_node:
901                tex_set_rule_family(n, lmt_tohalfword(L, 2));
902                break;
903        }
904    }
905    return 0;
906}
907
908/* node.direct.getstate(n) */
909/* node.direct.setstate(n) */
910
911/*tex
912    A zero state is considered to be false or basically the same as \quote {unset}. That way we
913    can are compatible with an unset property. This is cheaper on testing too. But I might
914    reconsider this at some point. (In which case I need to adapt the context source but by then
915    we have a lua/lmt split.)
916*/
917
918# define getstate_usage (glyph_usage | hlist_usage | vlist_usage)
919# define setstate_usage getstate_usage
920
921static int nodelib_direct_getstate(lua_State *L)
922{
923    halfword n = nodelib_valid_direct_from_index(L, 1);
924    if (n) {
925        int state = 0;
926        switch (node_type(n)) {
927            case glyph_node:
928                state = get_glyph_state(n);
929                break;
930            case hlist_node:
931            case vlist_node:
932                state = box_package_state(n);
933                break;
934            default:
935                goto NOPPES;
936        }
937        if (lua_type(L, 2) == LUA_TNUMBER) {
938            lua_pushboolean(L, lua_tointeger(L, 2) == state);
939            return 1;
940        } else if (state) {
941            lua_pushinteger(L, state);
942            return 1;
943        } else {
944            /*tex Indeed, |nil|. */
945        }
946    }
947  NOPPES:
948    lua_pushnil(L);
949    return 1;
950}
951
952static int nodelib_direct_setstate(lua_State *L)
953{
954    halfword n = nodelib_valid_direct_from_index(L, 1);
955    if (n) {
956        switch (node_type(n)) {
957            case glyph_node:
958                set_glyph_state(n, lmt_opthalfword(L, 2, 0));
959                break;
960            case hlist_node:
961            case vlist_node:
962                box_package_state(n) = (singleword) lmt_opthalfword(L, 2, 0);
963                break;
964        }
965    }
966    return 0;
967}
968
969/* node.direct.getclass(n,main,left,right) */
970/* node.direct.setclass(n,main,left,right) */
971
972# define getclass_usage (simple_usage | radical_usage | fraction_usage | accent_usage | fence_usage | glyph_usage | disc_usage | hlist_usage | vlist_usage | delimiter_usage)
973# define setclass_usage getclass_usage
974
975static int nodelib_direct_getclass(lua_State *L)
976{
977    halfword n = nodelib_valid_direct_from_index(L, 1);
978    if (n) {
979        switch (node_type(n)) {
980            case simple_noad:
981            case radical_noad:
982            case fraction_noad:
983            case accent_noad:
984            case fence_noad:
985                lua_push_integer(L, get_noad_main_class(n));
986                lua_push_integer(L, get_noad_left_class(n));
987                lua_push_integer(L, get_noad_right_class(n));
988                return 3;
989            case glyph_node:
990                {
991                    halfword mathclass = node_subtype(n) - glyph_math_ordinary_subtype;
992                    if (mathclass >= 0) {
993                        lua_push_integer(L, mathclass);
994                        return 1;
995                    } else {
996                        break;
997                    }
998                }
999            case disc_node:
1000                lua_push_integer(L, disc_class(n));
1001                return 1;
1002            case hlist_node:
1003            case vlist_node:
1004                {
1005                    halfword mathclass = node_subtype(n) - noad_class_list_base;
1006                    if (mathclass >= 0) {
1007                        lua_push_integer(L, mathclass);
1008                        return 1;
1009                    } else {
1010                        break;
1011                    }
1012                }
1013            case delimiter_node:
1014                lua_push_integer(L, node_subtype(n));
1015                return 1;
1016        }
1017    }
1018    lua_pushnil(L);
1019    return 1;
1020}
1021
1022static int nodelib_direct_setclass(lua_State *L)
1023{
1024    halfword n = nodelib_valid_direct_from_index(L, 1);
1025    if (n) {
1026        switch (node_type(n)) {
1027            case simple_noad:
1028            case radical_noad:
1029            case fraction_noad:
1030            case accent_noad:
1031            case fence_noad:
1032                if (lua_type(L, 2) == LUA_TNUMBER) {
1033                    set_noad_main_class(n, lmt_tosingleword(L, 2));
1034                }
1035                if (lua_type(L, 3) == LUA_TNUMBER) {
1036                    set_noad_left_class(n, lmt_tosingleword(L, 3));
1037                }
1038                if (lua_type(L, 4) == LUA_TNUMBER) {
1039                    set_noad_right_class(n, lmt_tosingleword(L, 4));
1040                }
1041                break;
1042            case glyph_node:
1043                node_subtype(n) = glyph_math_ordinary_subtype + lmt_tosingleword(L, 2);
1044                break;
1045            case disc_node:
1046                disc_class(n) = lmt_tosingleword(L, 2);
1047                break;
1048            case hlist_node:
1049            case vlist_node:
1050                node_subtype(n) = noad_class_list_base + lmt_tosingleword(L, 2);
1051                break;
1052            case delimiter_node:
1053                node_subtype(n) = lmt_tosingleword(L, 2);
1054                return 1;
1055        }
1056    }
1057    return 0;
1058}
1059
1060/* node.direct.getscript(n) */
1061/* node.direct.setscript(n) */
1062
1063# define getscript_usage (glyph_usage)
1064# define setscript_usage getscript_usage
1065
1066static int nodelib_direct_getscript(lua_State *L)
1067{
1068    halfword n = nodelib_valid_direct_from_index(L, 1);
1069    if (n && node_type(n) == glyph_node && get_glyph_script(n)) {
1070        if (lua_type(L, 2) == LUA_TNUMBER) {
1071            lua_pushboolean(L, lua_tointeger(L, 2) == get_glyph_script(n));
1072        } else {
1073            lua_pushinteger(L, get_glyph_script(n));
1074        }
1075    } else {
1076        lua_pushnil(L);
1077    }
1078    return 1;
1079}
1080
1081static int nodelib_direct_setscript(lua_State *L)
1082{
1083    halfword n = nodelib_valid_direct_from_index(L, 1);
1084    if (n && node_type(n) == glyph_node) {
1085        set_glyph_script(n, lmt_opthalfword(L, 2, 0));
1086    }
1087    return 0;
1088}
1089
1090/* node.direct.getlanguage */
1091/* node.direct.setlanguage */
1092
1093# define getlanguage_usage (glyph_usage)
1094# define setlanguage_usage getlanguage_usage
1095
1096static int nodelib_direct_getlanguage(lua_State *L)
1097{
1098    halfword n = nodelib_valid_direct_from_index(L, 1);
1099    if (n && node_type(n) == glyph_node) {
1100        lua_pushinteger(L, get_glyph_language(n));
1101    } else {
1102        lua_pushnil(L);
1103    }
1104    return 1;
1105}
1106
1107static int nodelib_direct_setlanguage(lua_State *L)
1108{
1109    halfword n = nodelib_valid_direct_from_index(L, 1);
1110    if (n && node_type(n) == glyph_node) {
1111        set_glyph_language(n, lmt_opthalfword(L, 2, 0));
1112    }
1113    return 0;
1114}
1115
1116/* node.direct.getcontrol */
1117/* node.direct.setcontrol */
1118
1119# define getcontrol_usage (glyph_usage)
1120# define setcontrol_usage getcontrol_usage
1121
1122static int nodelib_direct_getcontrol(lua_State *L)
1123{
1124    halfword n = nodelib_valid_direct_from_index(L, 1);
1125    if (n && node_type(n) == glyph_node) {
1126        lua_pushinteger(L, get_glyph_control(n));
1127    } else {
1128        lua_pushnil(L);
1129    }
1130    return 1;
1131}
1132
1133static int nodelib_direct_setcontrol(lua_State *L)
1134{
1135    halfword n = nodelib_valid_direct_from_index(L, 1);
1136    if (n && node_type(n) == glyph_node) {
1137        set_glyph_control(n, lmt_opthalfword(L, 2, 0));
1138    }
1139    return 0;
1140}
1141
1142/* node.direct.getattributelist */
1143/* node.direct.setattributelist */
1144
1145# define getattributelist_usage common_usage
1146# define setattributelist_usage getattributelist_usage
1147
1148static int nodelib_direct_getattributelist(lua_State *L)
1149{
1150    halfword n = nodelib_valid_direct_from_index(L, 1);
1151    if (n && tex_nodetype_has_attributes(node_type(n)) && node_attr(n)) {
1152        if (lua_toboolean(L, 2)) {
1153            nodelib_push_attribute_data(L, node_attr(n));
1154        } else {
1155            lua_pushinteger(L, node_attr(n));
1156        }
1157    } else {
1158        lua_pushnil(L);
1159    }
1160    return 1;
1161}
1162
1163static void nodelib_aux_setattributelist(lua_State *L, halfword n, int index)
1164{
1165    if (n && tex_nodetype_has_attributes(node_type(n))) {
1166        halfword a = null;
1167        switch (lua_type(L, index)) {
1168            case LUA_TNUMBER:
1169                {
1170                    halfword m = nodelib_valid_direct_from_index(L, index);
1171                    if (m) {
1172                        quarterword t = node_type(m);
1173                        if (t == attribute_node) {
1174                            if (node_subtype(m) == attribute_list_subtype) {
1175                              a = m;
1176                            } else {
1177                                /* invalid list, we could make a proper one if needed */
1178                            }
1179                        } else if (tex_nodetype_has_attributes(t)) {
1180                            a = node_attr(m);
1181                        }
1182                    }
1183                }
1184                break;
1185            case LUA_TBOOLEAN:
1186                if (lua_toboolean(L, index)) {
1187                    a = tex_current_attribute_list();
1188                }
1189                break;
1190            case LUA_TTABLE:
1191                {
1192                    /* kind of slow because we need a sorted inject */
1193                    lua_pushnil(L); /* push initial key */
1194                    while (lua_next(L, index)) {
1195                        halfword key = lmt_tohalfword(L, -2);
1196                        halfword val = lmt_tohalfword(L, -1);
1197                        a = tex_patch_attribute_list(a, key, val);
1198                        lua_pop(L, 1); /* pop value, keep key */
1199                    }
1200                    lua_pop(L, 1); /* pop key */
1201                }
1202                break;
1203        }
1204        tex_attach_attribute_list_attribute(n, a);
1205    }
1206}
1207
1208static int nodelib_direct_setattributelist(lua_State *L)
1209{
1210    nodelib_aux_setattributelist(L, nodelib_valid_direct_from_index(L, 1), 2);
1211    return 0;
1212}
1213
1214/* node.direct.getpenalty */
1215/* node.direct.setpenalty */
1216
1217# define getpenalty_usage (penalty_usage | disc_usage | math_node)
1218# define setpenalty_usage getpenalty_usage
1219
1220static int nodelib_direct_getpenalty(lua_State *L)
1221{
1222    halfword n = nodelib_valid_direct_from_index(L, 1);
1223    if (n) {
1224        switch (node_type(n)) {
1225            case penalty_node:
1226                lua_pushinteger(L, lua_toboolean(L, 2) ? penalty_tnuoma(n) : penalty_amount(n));
1227                break;
1228            case disc_node:
1229                lua_pushinteger(L, disc_penalty(n));
1230                break;
1231            case math_node:
1232                lua_pushinteger(L, math_penalty(n));
1233                break;
1234            default:
1235                lua_pushnil(L);
1236                break;
1237        }
1238    } else {
1239        lua_pushnil(L);
1240    }
1241    return 1;
1242}
1243
1244static int nodelib_direct_setpenalty(lua_State *L)
1245{
1246    halfword n = nodelib_valid_direct_from_index(L, 1);
1247    if (n) {
1248        switch (node_type(n)) {
1249            case penalty_node:
1250                penalty_amount(n) = (halfword) luaL_optinteger(L, 2, 0);
1251                break;
1252            case disc_node:
1253                disc_penalty(n) = (halfword) luaL_optinteger(L, 2, 0);
1254                break;
1255            case math_node:
1256                math_penalty(n) = (halfword) luaL_optinteger(L, 2, 0);
1257                break;
1258        }
1259    }
1260    return 0;
1261}
1262
1263/* node.direct.getnucleus */
1264/* node.direct.setnucleus */
1265
1266# define atomlike_usage (simple_usage | accent_usage | radical_usage)
1267
1268# define getnucleus_usage atomlike_usage
1269# define setnucleus_usage atomlike_usage
1270
1271static int nodelib_direct_getnucleus(lua_State *L)
1272{
1273    halfword n = nodelib_valid_direct_from_index(L, 1);
1274    if (n) {
1275        switch (node_type(n)) {
1276            case simple_noad:
1277            case accent_noad:
1278            case radical_noad:
1279                nodelib_push_direct_or_nil(L, noad_nucleus(n));
1280                if (lua_toboolean(L, 2)) {
1281                    nodelib_push_direct_or_nil(L, noad_prime(n));
1282                    nodelib_push_direct_or_nil(L, noad_supscr(n));
1283                    nodelib_push_direct_or_nil(L, noad_subscr(n));
1284                    nodelib_push_direct_or_nil(L, noad_supprescr(n));
1285                    nodelib_push_direct_or_nil(L, noad_subprescr(n));
1286                    return 6;
1287                } else {
1288                    return 1;
1289                }
1290        }
1291    }
1292    lua_pushnil(L);
1293    return 1;
1294}
1295
1296static int nodelib_direct_setnucleus(lua_State *L)
1297{
1298    halfword n = nodelib_valid_direct_from_index(L, 1);
1299    if (n) {
1300        switch (node_type(n)) {
1301            case simple_noad:
1302            case accent_noad:
1303            case radical_noad:
1304                noad_nucleus(n) = nodelib_valid_direct_from_index(L, 2);
1305                break;
1306        }
1307    }
1308    return 0;
1309}
1310
1311/* node.direct.getscripts */
1312/* node.direct.setscripts */
1313
1314# define getscripts_usage atomlike_usage
1315# define setscripts_usage atomlike_usage
1316
1317static int nodelib_direct_getscripts(lua_State *L)
1318{
1319    halfword n = nodelib_valid_direct_from_index(L, 1);
1320    if (n) {
1321        switch (node_type(n)) {
1322            case simple_noad:
1323            case accent_noad:
1324            case radical_noad:
1325                nodelib_push_direct_or_nil(L, noad_prime(n));
1326                nodelib_push_direct_or_nil(L, noad_supscr(n));
1327                nodelib_push_direct_or_nil(L, noad_subscr(n));
1328                nodelib_push_direct_or_nil(L, noad_supprescr(n));
1329                nodelib_push_direct_or_nil(L, noad_subprescr(n));
1330                return 5;
1331        }
1332    }
1333    return 0;
1334}
1335
1336static int nodelib_direct_setscripts(lua_State *L)
1337{
1338    halfword n = nodelib_valid_direct_from_index(L, 1);
1339    if (n) {
1340        switch (node_type(n)) {
1341            case simple_noad:
1342            case accent_noad:
1343            case radical_noad:
1344                noad_prime(n) = nodelib_valid_direct_from_index(L, 2);
1345                noad_supscr(n) = nodelib_valid_direct_from_index(L, 3);
1346                noad_subscr(n) = nodelib_valid_direct_from_index(L, 4);
1347                noad_supprescr(n) = nodelib_valid_direct_from_index(L, 5);
1348                noad_subprescr(n) = nodelib_valid_direct_from_index(L, 6);
1349                break;
1350        }
1351    }
1352    return 0;
1353}
1354
1355/* node.direct.getsub */
1356/* node.direct.setsub */
1357
1358# define getsub_usage atomlike_usage
1359# define setsub_usage atomlike_usage
1360
1361static int nodelib_direct_getsub(lua_State *L)
1362{
1363    halfword n = nodelib_valid_direct_from_index(L, 1);
1364    if (n) {
1365        switch (node_type(n)) {
1366            case simple_noad:
1367            case accent_noad:
1368            case radical_noad:
1369                nodelib_push_direct_or_nil(L, noad_subscr(n));
1370                return 1;
1371        }
1372    }
1373    lua_pushnil(L);
1374    return 1;
1375}
1376
1377static int nodelib_direct_setsub(lua_State *L)
1378{
1379    halfword n = nodelib_valid_direct_from_index(L, 1);
1380    if (n) {
1381        switch (node_type(n)) {
1382            case simple_noad:
1383            case accent_noad:
1384            case radical_noad:
1385                noad_subscr(n) = nodelib_valid_direct_from_index(L, 2);
1386             // if (lua_gettop(L) > 2) {
1387             //     noad_subprescr(n) = nodelib_valid_direct_from_index(L, 3);
1388             // }
1389                break;
1390        }
1391    }
1392    return 0;
1393}
1394
1395/* node.direct.getsubpre */
1396/* node.direct.setsubpre */
1397
1398# define getsubpre_usage atomlike_usage
1399# define setsubpre_usage atomlike_usage
1400
1401static int nodelib_direct_getsubpre(lua_State *L)
1402{
1403    halfword n = nodelib_valid_direct_from_index(L, 1);
1404    if (n) {
1405        switch (node_type(n)) {
1406            case simple_noad:
1407            case accent_noad:
1408            case radical_noad:
1409                nodelib_push_direct_or_nil(L, noad_subprescr(n));
1410                return 1;
1411        }
1412    }
1413    lua_pushnil(L);
1414    return 1;
1415}
1416
1417static int nodelib_direct_setsubpre(lua_State *L)
1418{
1419    halfword n = nodelib_valid_direct_from_index(L, 1);
1420    if (n) {
1421        switch (node_type(n)) {
1422            case simple_noad:
1423            case accent_noad:
1424            case radical_noad:
1425                noad_subprescr(n) = nodelib_valid_direct_from_index(L, 2);
1426                break;
1427        }
1428    }
1429    return 0;
1430}
1431
1432/* node.direct.getsup */
1433/* node.direct.setsup */
1434
1435# define getsup_usage atomlike_usage
1436# define setsup_usage atomlike_usage
1437
1438static int nodelib_direct_getsup(lua_State *L)
1439{
1440    halfword n = nodelib_valid_direct_from_index(L, 1);
1441    if (n) {
1442        switch (node_type(n)) {
1443            case simple_noad:
1444            case accent_noad:
1445            case radical_noad:
1446                nodelib_push_direct_or_nil(L, noad_supscr(n));
1447                return 1;
1448        }
1449    }
1450    lua_pushnil(L);
1451    return 1;
1452}
1453
1454static int nodelib_direct_setsup(lua_State *L)
1455{
1456    halfword n = nodelib_valid_direct_from_index(L, 1);
1457    if (n) {
1458        switch (node_type(n)) {
1459            case simple_noad:
1460            case accent_noad:
1461            case radical_noad:
1462                noad_supscr(n) = nodelib_valid_direct_from_index(L, 2);
1463             // if (lua_gettop(L) > 2) {
1464             //     supprescr(n) = nodelib_valid_direct_from_index(L, 3);
1465             // }
1466                break;
1467        }
1468    }
1469    return 0;
1470}
1471
1472/* node.direct.getsuppre */
1473/* node.direct.setsuppre */
1474
1475# define getsuppre_usage atomlike_usage
1476# define setsuppre_usage atomlike_usage
1477
1478static int nodelib_direct_getsuppre(lua_State *L)
1479{
1480    halfword n = nodelib_valid_direct_from_index(L, 1);
1481    if (n) {
1482        switch (node_type(n)) {
1483            case simple_noad:
1484            case accent_noad:
1485            case radical_noad:
1486                nodelib_push_direct_or_nil(L, noad_supprescr(n));
1487                return 1;
1488        }
1489    }
1490    lua_pushnil(L);
1491    return 1;
1492}
1493
1494static int nodelib_direct_setsuppre(lua_State *L)
1495{
1496    halfword n = nodelib_valid_direct_from_index(L, 1);
1497    if (n) {
1498        switch (node_type(n)) {
1499            case simple_noad:
1500            case accent_noad:
1501            case radical_noad:
1502                noad_supprescr(n) = nodelib_valid_direct_from_index(L, 2);
1503                break;
1504        }
1505    }
1506    return 0;
1507}
1508
1509/* node.direct.getprime */
1510/* node.direct.setprime */
1511
1512# define getprime_usage atomlike_usage
1513# define setprime_usage atomlike_usage
1514
1515static int nodelib_direct_getprime(lua_State *L)
1516{
1517    halfword n = nodelib_valid_direct_from_index(L, 1);
1518    if (n) {
1519        switch (node_type(n)) {
1520            case simple_noad:
1521            case accent_noad:
1522            case radical_noad:
1523                nodelib_push_direct_or_nil(L, noad_prime(n));
1524                return 1;
1525        }
1526    }
1527    lua_pushnil(L);
1528    return 1;
1529}
1530
1531static int nodelib_direct_setprime(lua_State *L)
1532{
1533    halfword n = nodelib_valid_direct_from_index(L, 1);
1534    if (n) {
1535        switch (node_type(n)) {
1536            case simple_noad:
1537            case accent_noad:
1538            case radical_noad:
1539                noad_prime(n) = nodelib_valid_direct_from_index(L, 2);
1540                break;
1541        }
1542    }
1543    return 0;
1544}
1545
1546/* node.direct.getkern (overlaps with getwidth) */
1547/* node.direct.setkern (overlaps with getwidth) */
1548
1549# define getkern_usage (kern_usage | math_usage)
1550# define setkern_usage getkern_usage
1551
1552static int nodelib_direct_getkern(lua_State *L)
1553{
1554    halfword n = nodelib_valid_direct_from_index(L, 1);
1555    if (n) {
1556        switch (node_type(n)) {
1557            case kern_node:
1558                lua_pushnumber(L, kern_amount(n));
1559                if (lua_toboolean(L, 2)) {
1560                    lua_pushinteger(L, kern_expansion(n));
1561                    return 2;
1562                } else {
1563                    break;
1564                }
1565            case math_node:
1566                lua_pushinteger(L, math_surround(n));
1567                break;
1568            default:
1569                lua_pushnil(L);
1570                break;
1571        }
1572    } else {
1573        lua_pushnil(L);
1574    }
1575    return 1;
1576}
1577
1578static int nodelib_direct_setkern(lua_State *L)
1579{
1580    halfword n = nodelib_valid_direct_from_index(L, 1);
1581    if (n) {
1582        switch (node_type(n)) {
1583            case kern_node:
1584                kern_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L, 2) : 0;
1585                if (lua_type(L, 3) == LUA_TNUMBER) {
1586                    node_subtype(n) = lmt_toquarterword(L, 3);
1587                }
1588                break;
1589            case math_node:
1590                math_surround(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L, 2) : 0;
1591                break;
1592        }
1593    }
1594    return 0;
1595}
1596
1597/* node.direct.getdirection */
1598/* node.direct.setdirection */
1599
1600# define getdirection_usage (dir_usage | hlist_usage | vlist_usage | par_usage)
1601# define setdirection_usage getdirection_usage
1602
1603static int nodelib_direct_getdirection(lua_State *L)
1604{
1605    halfword n = nodelib_valid_direct_from_index(L, 1);
1606    if (n) {
1607        switch (node_type(n)) {
1608            case dir_node:
1609                lua_pushinteger(L, dir_direction(n));
1610                lua_pushboolean(L, node_subtype(n));
1611                return 2;
1612            case hlist_node:
1613            case vlist_node:
1614                lua_pushinteger(L, checked_direction_value(box_dir(n)));
1615                break;
1616            case par_node:
1617                lua_pushinteger(L, par_dir(n));
1618                break;
1619            default:
1620                lua_pushnil(L);
1621                break;
1622        }
1623    } else {
1624        lua_pushnil(L);
1625    }
1626    return 1;
1627}
1628
1629static int nodelib_direct_setdirection(lua_State *L)
1630{
1631    halfword n = nodelib_valid_direct_from_index(L, 1);
1632    if (n) {
1633        switch (node_type(n)) {
1634            case dir_node:
1635                dir_direction(n) = nodelib_getdirection(L, 2);
1636                if (lua_type(L, 3) == LUA_TBOOLEAN) {
1637                    if (lua_toboolean(L, 3)) {
1638                        node_subtype(n) = (quarterword) (lua_toboolean(L, 3) ? cancel_dir_subtype : normal_dir_subtype);
1639                    }
1640                }
1641                break;
1642            case hlist_node:
1643            case vlist_node:
1644                box_dir(n) = (singleword) nodelib_getdirection(L, 2);
1645                break;
1646            case par_node:
1647                par_dir(n) = nodelib_getdirection(L, 2);
1648                break;
1649        }
1650    }
1651    return 0;
1652}
1653
1654/* node.direct.getanchors */
1655/* node.direct.setanchors */
1656
1657# define getanchors_usage (hlist_usage | vlist_usage | simple_usage | radical_usage | fraction_usage | accent_usage | fence_usage)
1658# define setanchors_usage getanchors_usage
1659
1660static int nodelib_direct_getanchors(lua_State *L)
1661{
1662    halfword n = nodelib_valid_direct_from_index(L, 1);
1663    if (n) {
1664        switch (node_type(n)) {
1665            case hlist_node:
1666            case vlist_node:
1667                if (box_anchor(n)) {
1668                    lua_pushinteger(L, box_anchor(n));
1669                } else {
1670                    lua_pushnil(L);
1671                }
1672                if (box_source_anchor(n)) {
1673                    lua_pushinteger(L, box_source_anchor(n));
1674                } else {
1675                    lua_pushnil(L);
1676                }
1677                if (box_target_anchor(n)) {
1678                    lua_pushinteger(L, box_target_anchor(n));
1679                } else {
1680                    lua_pushnil(L);
1681                }
1682                /* bonus detail: source, target */
1683                if (box_anchor(n)) {
1684                    lua_pushinteger(L, box_anchor(n) & 0x0FFF);
1685                } else {
1686                    lua_pushnil(L);
1687                }
1688                if (box_anchor(n)) {
1689                    lua_pushinteger(L, (box_anchor(n) >> 16) & 0x0FFF);
1690                } else {
1691                    lua_pushnil(L);
1692                }
1693                return 5;
1694            case simple_noad:
1695            case radical_noad:
1696            case fraction_noad:
1697            case accent_noad:
1698            case fence_noad:
1699                if (noad_source(n)) {
1700                    lua_pushinteger(L, noad_source(n));
1701                } else {
1702                    lua_pushnil(L);
1703                }
1704                return 1;
1705        }
1706    }
1707    return 0;
1708}
1709
1710static int nodelib_direct_setanchors(lua_State *L)
1711{
1712    halfword n = nodelib_valid_direct_from_index(L, 1);
1713    if (n) {
1714        switch (node_type(n)) {
1715            case hlist_node:
1716            case vlist_node:
1717                switch (lua_type(L, 2)) {
1718                    case LUA_TNUMBER:
1719                        box_anchor(n) = lmt_tohalfword(L, 2);
1720                        break;
1721                    case LUA_TBOOLEAN:
1722                        if (lua_toboolean(L, 2)) {
1723                            break;
1724                        }
1725                    default:
1726                        box_anchor(n) = 0;
1727                        break;
1728                }
1729                switch (lua_type(L, 3)) {
1730                    case LUA_TNUMBER :
1731                        box_source_anchor(n) = lmt_tohalfword(L, 3);
1732                        break;
1733                    case LUA_TBOOLEAN:
1734                        if (lua_toboolean(L, 3)) {
1735                            break;
1736                        }
1737                    default:
1738                        box_source_anchor(n) = 0;
1739                        break;
1740                }
1741                switch (lua_type(L, 4)) {
1742                    case LUA_TNUMBER:
1743                        box_target_anchor(n) = lmt_tohalfword(L, 4);
1744                        break;
1745                    case LUA_TBOOLEAN:
1746                        if (lua_toboolean(L, 4)) {
1747                            break;
1748                        }
1749                    default:
1750                        box_target_anchor(n) = 0;
1751                        break;
1752                }
1753                tex_check_box_geometry(n);
1754            case simple_noad:
1755            case radical_noad:
1756            case fraction_noad:
1757            case accent_noad:
1758            case fence_noad:
1759                switch (lua_type(L, 2)) {
1760                    case LUA_TNUMBER :
1761                        noad_source(n) = lmt_tohalfword(L, 2);
1762                        break;
1763                    case LUA_TBOOLEAN:
1764                        if (lua_toboolean(L, 2)) {
1765                            break;
1766                        }
1767                    default:
1768                        noad_source(n) = 0;
1769                        break;
1770                }
1771                tex_check_box_geometry(n);
1772        }
1773    }
1774    return 0;
1775}
1776
1777/* node.direct.getoffsets */
1778/* node.direct.getoffsets */
1779
1780# define getoffsets_usage (glyph_usage | hlist_usage | vlist_usage | rule_usage)
1781# define setoffsets_usage getoffsets_usage
1782
1783static int nodelib_direct_getoffsets(lua_State *L)
1784{
1785    halfword n = nodelib_valid_direct_from_index(L, 1);
1786    if (n) {
1787        switch (node_type(n)) {
1788            case glyph_node:
1789                lua_pushinteger(L, glyph_x_offset(n));
1790                lua_pushinteger(L, glyph_y_offset(n));
1791                lua_pushinteger(L, glyph_left(n));
1792                lua_pushinteger(L, glyph_right(n));
1793                lua_pushinteger(L, glyph_raise(n));
1794                return 5;
1795            case hlist_node:
1796            case vlist_node:
1797                lua_pushinteger(L, box_x_offset(n));
1798                lua_pushinteger(L, box_y_offset(n));
1799                return 2;
1800            case rule_node:
1801                lua_pushinteger(L, rule_x_offset(n));
1802                lua_pushinteger(L, rule_y_offset(n));
1803                lua_pushinteger(L, tex_get_rule_left(n));
1804                lua_pushinteger(L, tex_get_rule_right(n));
1805                lua_pushinteger(L, tex_get_rule_on(n));
1806                lua_pushinteger(L, tex_get_rule_off(n));
1807                return 6;
1808        }
1809    }
1810    return 0;
1811}
1812
1813static int nodelib_direct_setoffsets(lua_State *L)
1814{
1815    halfword n = nodelib_valid_direct_from_index(L, 1);
1816    if (n) {
1817        switch (node_type(n)) {
1818            case glyph_node:
1819                if (lua_type(L, 2) == LUA_TNUMBER) {
1820                    glyph_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
1821                }
1822                if (lua_type(L, 3) == LUA_TNUMBER) {
1823                    glyph_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
1824                }
1825                if (lua_type(L, 4) == LUA_TNUMBER) {
1826                    glyph_left(n) = (halfword) lmt_roundnumber(L, 4);
1827                }
1828                if (lua_type(L, 5) == LUA_TNUMBER) {
1829                    glyph_right(n) = (halfword) lmt_roundnumber(L, 5);
1830                }
1831                if (lua_type(L, 6) == LUA_TNUMBER) {
1832                    glyph_raise(n) = (halfword) lmt_roundnumber(L, 6);
1833                }
1834                break;
1835            case hlist_node:
1836            case vlist_node:
1837                if (lua_type(L, 2) == LUA_TNUMBER) {
1838                    box_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
1839                }
1840                if (lua_type(L, 3) == LUA_TNUMBER) {
1841                    box_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
1842                }
1843                tex_check_box_geometry(n);
1844                break;
1845            case rule_node:
1846                if (lua_type(L, 2) == LUA_TNUMBER) {
1847                    rule_x_offset(n) = (halfword) lmt_roundnumber(L, 2);
1848                }
1849                if (lua_type(L, 3) == LUA_TNUMBER) {
1850                    rule_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
1851                }
1852                if (lua_type(L, 4) == LUA_TNUMBER) {
1853                    tex_set_rule_left(n,  (halfword) lmt_roundnumber(L, 4));
1854                }
1855                if (lua_type(L, 5) == LUA_TNUMBER) {
1856                    tex_set_rule_right(n, (halfword) lmt_roundnumber(L, 5));
1857                }
1858                if (lua_type(L, 6) == LUA_TNUMBER) {
1859                    tex_set_rule_on(n,  (halfword) lmt_roundnumber(L, 6));
1860                }
1861                if (lua_type(L, 7) == LUA_TNUMBER) {
1862                    tex_set_rule_off(n, (halfword) lmt_roundnumber(L, 7));
1863                }
1864                break;
1865        }
1866    }
1867    return 0;
1868}
1869
1870/* node.direct.addxoffset */
1871/* node.direct.addyoffset */
1872
1873# define addxoffset_usage (glyph_usage | hlist_usage | vlist_usage | rule_usage)
1874# define addyoffset_usage addxoffset_usage
1875
1876static int nodelib_direct_addxoffset(lua_State *L)
1877{
1878    halfword n = nodelib_valid_direct_from_index(L, 1);
1879    if (n) {
1880        switch (node_type(n)) {
1881            case glyph_node:
1882                glyph_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
1883                break;
1884            case hlist_node:
1885            case vlist_node:
1886                box_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
1887                tex_check_box_geometry(n);
1888                break;
1889            case rule_node:
1890                rule_x_offset(n) += (halfword) lmt_roundnumber(L, 2);
1891                break;
1892        }
1893    }
1894    return 0;
1895}
1896
1897static int nodelib_direct_addyoffset(lua_State *L)
1898{
1899    halfword n = nodelib_valid_direct_from_index(L, 1);
1900    if (n) {
1901        switch (node_type(n)) {
1902            case glyph_node:
1903                glyph_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
1904                break;
1905            case hlist_node:
1906            case vlist_node:
1907                box_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
1908                tex_check_box_geometry(n);
1909                break;
1910            case rule_node:
1911                rule_y_offset(n) += (halfword) lmt_roundnumber(L, 2);
1912                break;
1913        }
1914    }
1915    return 0;
1916}
1917
1918/* node.direct.addmargins */
1919/* node.direct.addxymargins */
1920
1921# define addmargins_usage   (glyph_usage | rule_usage)
1922# define addxymargins_usage glyph_usage
1923
1924static int nodelib_direct_addmargins(lua_State *L)
1925{
1926    halfword n = nodelib_valid_direct_from_index(L, 1);
1927    if (n) {
1928        switch (node_type(n)) {
1929            case glyph_node:
1930                if (lua_type(L, 2) == LUA_TNUMBER) {
1931                    glyph_left(n) += (halfword) lmt_roundnumber(L, 2);
1932                }
1933                if (lua_type(L, 3) == LUA_TNUMBER) {
1934                    glyph_right(n) += (halfword) lmt_roundnumber(L, 3);
1935                }
1936                if (lua_type(L, 4) == LUA_TNUMBER) {
1937                    glyph_raise(n) += (halfword) lmt_roundnumber(L, 3);
1938                }
1939                break;
1940            case rule_node:
1941                if (lua_type(L, 2) == LUA_TNUMBER) {
1942                    tex_set_rule_left(n, tex_get_rule_left(n) + (halfword) lmt_roundnumber(L, 2));
1943                }
1944                if (lua_type(L, 3) == LUA_TNUMBER) {
1945                    tex_set_rule_right(n, tex_get_rule_right(n) + (halfword) lmt_roundnumber(L, 3));
1946                }
1947                break;
1948        }
1949    }
1950    return 0;
1951}
1952
1953static int nodelib_direct_addxymargins(lua_State *L)
1954{
1955    halfword n = nodelib_valid_direct_from_index(L, 1);
1956    if (n && node_type(n) == glyph_node) {
1957        scaled s = glyph_scale(n);
1958        scaled x = glyph_x_scale(n);
1959        scaled y = glyph_y_scale(n);
1960        double sx, sy;
1961        if (s == 0 || s == 1000) {
1962            if (x == 0 || x == 1000) {
1963                sx = 1;
1964            } else {
1965                sx = 0.001 * x;
1966            }
1967            if (y == 0 || y == 1000) {
1968                sy = 1;
1969            } else {
1970                sy = 0.001 * y;
1971            }
1972        } else {
1973            if (x == 0 || x == 1000) {
1974                sx = 0.001 * s;
1975            } else {
1976                sx = 0.000001 * s * x;
1977            }
1978            if (y == 0 || y == 1000) {
1979                sy = 0.001 * s;
1980            } else {
1981                sy = 0.000001 * s * y;
1982            }
1983        }
1984        if (lua_type(L, 2) == LUA_TNUMBER) {
1985            glyph_left(n) += scaledround(sx * lua_tonumber(L, 2));
1986        }
1987        if (lua_type(L, 3) == LUA_TNUMBER) {
1988            glyph_right(n) += scaledround(sx * lua_tonumber(L, 3));
1989        }
1990        if (lua_type(L, 4) == LUA_TNUMBER) {
1991            glyph_raise(n) += scaledround(sy * lua_tonumber(L, 4));
1992        }
1993    }
1994    return 0;
1995}
1996
1997/* node.direct.getscale   */
1998
1999# define getscale_usage glyph_usage
2000# define setscale_usage getscale_usage
2001
2002static int nodelib_direct_getscale(lua_State *L)
2003{
2004    halfword n = nodelib_valid_direct_from_index(L, 1);
2005    if (n && node_type(n) == glyph_node) {
2006        lua_pushinteger(L, glyph_scale(n));
2007        return 1;
2008    }
2009    return 0;
2010}
2011
2012static int nodelib_direct_setscale(lua_State *L)
2013{
2014    halfword n = nodelib_valid_direct_from_index(L, 1);
2015    if (n && node_type(n) == glyph_node) {
2016        if (lua_type(L, 2) == LUA_TNUMBER) {
2017            glyph_scale(n) = (halfword) lmt_roundnumber(L, 2);
2018            if (! glyph_scale(n)) {
2019                glyph_scale(n) = scaling_factor;
2020            }
2021        }
2022    }
2023    return 0;
2024}
2025
2026/* node.direct.getscales  */
2027/* node.direct.setscales  */
2028
2029# define getscales_usage glyph_usage
2030# define setscales_usage getscales_usage
2031
2032static int nodelib_direct_getscales(lua_State *L)
2033{
2034    halfword n = nodelib_valid_direct_from_index(L, 1);
2035    if (n && node_type(n) == glyph_node) {
2036        lua_pushinteger(L, glyph_scale(n));
2037        lua_pushinteger(L, glyph_x_scale(n));
2038        lua_pushinteger(L, glyph_y_scale(n));
2039        return 3;
2040    } else {
2041        return 0;
2042    }
2043}
2044
2045static int nodelib_direct_setscales(lua_State *L)
2046{
2047    halfword n = nodelib_valid_direct_from_index(L, 1);
2048    if (n && node_type(n) == glyph_node) {
2049        if (lua_type(L, 2) == LUA_TNUMBER) {
2050            glyph_scale(n) = (halfword) lmt_roundnumber(L, 2);
2051            if (! glyph_scale(n)) {
2052                glyph_scale(n) = scaling_factor;
2053            }
2054        }
2055        if (lua_type(L, 3) == LUA_TNUMBER) {
2056            glyph_x_scale(n) = (halfword) lmt_roundnumber(L, 3);
2057            if (! glyph_x_scale(n)) {
2058                glyph_x_scale(n) = scaling_factor;
2059            }
2060        }
2061        if (lua_type(L, 4) == LUA_TNUMBER) {
2062            glyph_y_scale(n) = (halfword) lmt_roundnumber(L, 4);
2063            if (! glyph_y_scale(n)) {
2064                glyph_y_scale(n) = scaling_factor;
2065            }
2066        }
2067    }
2068    return 0;
2069}
2070
2071/* node.direct.getxscale  */
2072/* node.direct.getyscale  */
2073
2074# define getxscale_usage (glue_usage | glyph_usage)
2075# define getyscale_usage getxscale_usage
2076
2077static int nodelib_direct_getxscale(lua_State *L)
2078{
2079    halfword n = nodelib_valid_direct_from_index(L, 1);
2080    if (n) {
2081        switch (node_type(n)) {
2082            case glue_node:
2083                {
2084                    switch (node_subtype(n)) {
2085                        case space_skip_glue:
2086                        case xspace_skip_glue:
2087                            if (tex_char_exists(glue_font(n), 0x20)) {
2088                                halfword prv = node_prev(n);
2089                                halfword nxt = node_next(n);
2090                                if (prv) {
2091                                    if (node_type(prv) == kern_node && font_related_kern(node_subtype(prv))) {
2092                                        prv = node_prev(prv);
2093                                    }
2094                                    if (prv && node_type(prv) == glyph_node && glyph_font(prv) == glue_font(n)) {
2095                                        n = prv;
2096                                        goto COMMON;
2097                                    }
2098                                }
2099                                if (nxt) {
2100                                    if (node_type(nxt) == kern_node && font_related_kern(node_subtype(nxt))) {
2101                                        prv = node_prev(nxt);
2102                                    }
2103                                    if (nxt && node_type(nxt) == glyph_node && glyph_font(nxt) == glue_font(n)) {
2104                                        n = nxt;
2105                                        goto COMMON;
2106                                    }
2107                                }
2108                            }
2109                            break;
2110                    }
2111                    break;
2112                }
2113            case glyph_node:
2114              COMMON:
2115                {
2116                    scaled s = glyph_scale(n);
2117                    scaled x = glyph_x_scale(n);
2118                    double d;
2119                    if (s == 0 || s == 1000) {
2120                        if (x == 0 || x == 1000) {
2121                            goto DONE;
2122                        } else {
2123                            d = 0.001 * x;
2124                        }
2125                    } else if (x == 0 || x == 1000) {
2126                        d = 0.001 * s;
2127                    } else {
2128                        d = 0.000001 * s * x;
2129                    }
2130                    lua_pushnumber(L, d);
2131                    return 1;
2132                }
2133        }
2134    }
2135  DONE:
2136    lua_pushinteger(L, 1);
2137    return 1;
2138}
2139
2140static int nodelib_direct_getyscale(lua_State *L)
2141{
2142    halfword n = nodelib_valid_direct_from_index(L, 1);
2143    if (n && node_type(n) == glyph_node) {
2144        scaled s = glyph_scale(n);
2145        scaled y = glyph_y_scale(n);
2146        double d;
2147        if (s == 0 || s == 1000) {
2148            if (y == 0 || y == 1000) {
2149                goto DONE;
2150            } else {
2151                d = 0.001 * y;
2152            }
2153        } else if (y == 0 || y == 1000) {
2154            d = 0.001 * s;
2155        } else {
2156            d = 0.000001 * s * y;
2157        }
2158        lua_pushnumber(L, d);
2159        return 1;
2160    }
2161  DONE:
2162    lua_pushinteger(L, 1);
2163    return 1;
2164}
2165
2166/* node.direct.xscaled  */
2167/* node.direct.yscaled  */
2168
2169# define xscaled_usage glyph_usage
2170# define yscaled_usage xscaled_usage
2171
2172static int nodelib_direct_xscaled(lua_State *L)
2173{
2174    halfword n = nodelib_valid_direct_from_index(L, 1);
2175    lua_Number v = lua_tonumber(L, 2);
2176    if (n && node_type(n) == glyph_node) {
2177        scaled s = glyph_scale(n);
2178        scaled x = glyph_x_scale(n);
2179        if (s == 0 || s == 1000) {
2180            if (x == 0 || x == 1000) {
2181                /* okay */
2182            } else {
2183                v = 0.001 * x * v;
2184            }
2185        } else if (x == 0 || x == 1000) {
2186            v = 0.001 * s * v;
2187        } else {
2188            v = 0.000001 * s * x * v;
2189        }
2190    }
2191    lua_pushnumber(L, v);
2192    return 1;
2193}
2194
2195static int nodelib_direct_yscaled(lua_State *L)
2196{
2197    halfword n = nodelib_valid_direct_from_index(L, 1);
2198    lua_Number v = lua_tonumber(L, 2);
2199    if (n && node_type(n) == glyph_node) {
2200        scaled s = glyph_scale(n);
2201        scaled y = glyph_y_scale(n);
2202        if (s == 0 || s == 1000) {
2203            if (y == 0 || y == 1000) {
2204                /* okay */
2205            } else {
2206                v = 0.001 * y * v;
2207            }
2208        } else if (y == 0 || y == 1000) {
2209            v = 0.001 * s * v;
2210        } else {
2211            v = 0.000001 * s * y * v;
2212        }
2213    }
2214    lua_pushnumber(L, v);
2215    return 1;
2216}
2217
2218static void nodelib_aux_pushxyscales(lua_State *L, halfword n)
2219{
2220    scaled s = glyph_scale(n);
2221    scaled x = glyph_x_scale(n);
2222    scaled y = glyph_y_scale(n);
2223    double dx;
2224    double dy;
2225    if (s && s != 1000) {
2226        dx = (x && x != 1000) ? 0.000001 * s * x : 0.001 * s;
2227    } else if (x && x != 1000) {
2228        dx = 0.001 * x;
2229    } else {
2230        lua_pushinteger(L, 1);
2231        goto DONEX;
2232    }
2233    lua_pushnumber(L, dx);
2234  DONEX:
2235    if (s && s != 1000) {
2236        dy = (y && y != 1000) ? 0.000001 * s * y : 0.001 * s;
2237    } else if (y && y != 1000) {
2238        dy = 0.001 * y;
2239    } else {
2240        lua_pushinteger(L, 1);
2241        goto DONEY;
2242    }
2243    lua_pushnumber(L, dy);
2244  DONEY: ;
2245}
2246
2247/* node.direct.getxyscales */
2248
2249# define getxyscales_usage glyph_usage
2250# define setxyscales_usage getxyscales_usage
2251
2252static int nodelib_direct_getxyscales(lua_State *L)
2253{
2254    halfword n = nodelib_valid_direct_from_index(L, 1);
2255    if (n && node_type(n) == glyph_node) {
2256        nodelib_aux_pushxyscales(L, n);
2257    } else {
2258        lua_pushinteger(L, 1);
2259        lua_pushinteger(L, 1);
2260    }
2261    return 2;
2262}
2263
2264/* node.direct.getslant */
2265/* node.direct.setslant */
2266
2267# define getslant_usage glyph_usage
2268# define setslant_usage getslant_usage
2269
2270static int nodelib_direct_getslant(lua_State *L)
2271{
2272    halfword n = nodelib_valid_direct_from_index(L, 1);
2273    lua_pushinteger(L, n && node_type(n) == glyph_node ? glyph_slant(n) : 0);
2274    return 1;
2275}
2276
2277static int nodelib_direct_setslant(lua_State *L)
2278{
2279    halfword n = nodelib_valid_direct_from_index(L, 1);
2280    if (n && node_type(n) == glyph_node) {
2281        glyph_slant(n) = lmt_opthalfword(L, 2, 0);
2282    }
2283    return 0;
2284}
2285
2286/* node.direct.getweight */
2287/* node.direct.setweight */
2288
2289# define getweight_usage glyph_usage
2290# define setweight_usage getweight_usage
2291
2292static int nodelib_direct_getweight(lua_State *L)
2293{
2294    halfword n = nodelib_valid_direct_from_index(L, 1);
2295    halfword b = n && node_type(n) == glyph_node;
2296    lua_pushinteger(L, b ? glyph_weight(n) : 0);
2297    lua_pushboolean(L, b ? tex_has_glyph_option(n, glyph_option_weight_less) : 0);
2298    return 2;
2299}
2300
2301static int nodelib_direct_setweight(lua_State *L)
2302{
2303    halfword n = nodelib_valid_direct_from_index(L, 1);
2304    if (n && node_type(n) == glyph_node) {
2305        glyph_weight(n) = lmt_opthalfword(L, 2, 0);
2306        if (lua_type(L, 3) == LUA_TBOOLEAN && lua_toboolean(L, 3)) {
2307            tex_add_glyph_option(n, glyph_option_weight_less);
2308        }
2309    }
2310    return 0;
2311}
2312
2313/*tex
2314    For the moment we don't provide setters for math discretionaries, mainly because these are
2315    special and I don't want to waste time on checking and intercepting errors. They are not that
2316    widely used anyway.
2317*/
2318
2319/* node.direct.getdisc */
2320/* node.direct.setdisc */
2321
2322# define getdisc_usage (disc_usage | choice_usage)
2323# define setdisc_usage getdisc_usage
2324
2325static int nodelib_direct_getdisc(lua_State *L)
2326{
2327    halfword n = nodelib_valid_direct_from_index(L, 1);
2328    if (n) {
2329        switch (node_type(n)) {
2330            case disc_node:
2331                nodelib_push_direct_or_nil(L, disc_pre_break_head(n));
2332                nodelib_push_direct_or_nil(L, disc_post_break_head(n));
2333                nodelib_push_direct_or_nil(L, disc_no_break_head(n));
2334                if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) {
2335                    nodelib_push_direct_or_nil(L, disc_pre_break_tail(n));
2336                    nodelib_push_direct_or_nil(L, disc_post_break_tail(n));
2337                    nodelib_push_direct_or_nil(L, disc_no_break_tail(n));
2338                    return 6;
2339                } else {
2340                    return 3;
2341                }
2342            case choice_node:
2343                if (node_subtype(n) == discretionary_choice_subtype) {
2344                    nodelib_push_direct_or_nil(L, choice_pre_break(n));
2345                    nodelib_push_direct_or_nil(L, choice_post_break(n));
2346                    nodelib_push_direct_or_nil(L, choice_no_break(n));
2347                    if (lua_isboolean(L, 2) && lua_toboolean(L, 2)) {
2348                        nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_pre_break(n)));
2349                        nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_post_break(n)));
2350                        nodelib_push_direct_or_nil(L, tex_tail_of_node_list(choice_no_break(n)));
2351                        return 6;
2352                    } else {
2353                        return 3;
2354                    }
2355                } else {
2356                    break;
2357                }
2358        }
2359    }
2360    return 0;
2361}
2362
2363static int nodelib_direct_setdisc(lua_State *L)
2364{
2365    halfword n = nodelib_valid_direct_from_index(L, 1);
2366    if (n && node_type(n) == disc_node) {
2367        int t = lua_gettop(L) ;
2368        if (t > 1) {
2369            tex_set_disc_field(n, pre_break_code, nodelib_valid_direct_from_index(L, 2));
2370            if (t > 2) {
2371                tex_set_disc_field(n, post_break_code, nodelib_valid_direct_from_index(L, 3));
2372                if (t > 3) {
2373                    tex_set_disc_field(n, no_break_code, nodelib_valid_direct_from_index(L, 4));
2374                    if (t > 4) {
2375                        node_subtype(n) = lmt_toquarterword(L, 5);
2376                        if (t > 5) {
2377                            disc_penalty(n) = lmt_tohalfword(L, 6);
2378                        }
2379                    }
2380                } else {
2381                    tex_set_disc_field(n, no_break_code, null);
2382                }
2383            } else {
2384                tex_set_disc_field(n, post_break_code, null);
2385                tex_set_disc_field(n, no_break_code, null);
2386            }
2387        } else {
2388            tex_set_disc_field(n, pre_break_code, null);
2389            tex_set_disc_field(n, post_break_code, null);
2390            tex_set_disc_field(n, no_break_code, null);
2391        }
2392    }
2393    return 0;
2394}
2395
2396/* node.direct.getdiscpart */
2397/* node.direct.setdiscpart */
2398
2399# define getdiscpart_usage glyph_node
2400# define setdiscpart_usage getdiscpart_usage
2401
2402static int nodelib_direct_getdiscpart(lua_State *L)
2403{
2404    halfword n = nodelib_valid_direct_from_index(L, 1);
2405    if (n && node_type(n) == glyph_node) {
2406        lua_pushinteger(L, get_glyph_discpart(n));
2407        lua_pushinteger(L, get_glyph_discafter(n));
2408        lua_pushinteger(L, get_glyph_disccode(n));
2409        return 3;
2410    } else {
2411        return 0;
2412    }
2413}
2414
2415static int nodelib_direct_setdiscpart(lua_State *L)
2416{
2417    halfword n = nodelib_valid_direct_from_index(L, 1);
2418    if (n && node_type(n) == glyph_node) {
2419        if (lua_type(L, 2) == LUA_TNUMBER) { set_glyph_discpart (n, lua_tointeger(L, 2)); }
2420        if (lua_type(L, 3) == LUA_TNUMBER) { set_glyph_discafter(n, lua_tointeger(L, 3)); }
2421        if (lua_type(L, 4) == LUA_TNUMBER) { set_glyph_disccode (n, lua_tointeger(L, 4)); }
2422        return 1;
2423    } else {
2424        return 0;
2425    }
2426}
2427
2428/* node.direct.getpre */
2429/* node.direct.getpost */
2430/* node.direct.getreplace */
2431
2432# define getpre_usage     (disc_usage | hlist_usage | vlist_usage | choice_usage)
2433# define getpost_usage    getpre_usage
2434# define getreplace_usage (disc_usage | choice_usage)
2435
2436static int nodelib_direct_getpre(lua_State *L)
2437{
2438    halfword n = nodelib_valid_direct_from_index(L, 1);
2439    if (n) {
2440        switch (node_type(n)) {
2441            case disc_node:
2442                nodelib_push_direct_or_nil(L, disc_pre_break_head(n));
2443                nodelib_push_direct_or_nil(L, disc_pre_break_tail(n));
2444                return 2;
2445            case hlist_node:
2446            case vlist_node:
2447                {
2448                    halfword h = box_pre_migrated(n);
2449                    halfword t = tex_tail_of_node_list(h);
2450                    nodelib_push_direct_or_nil(L, h);
2451                    nodelib_push_direct_or_nil(L, t);
2452                    return 2;
2453                }
2454            case choice_node:
2455                if (node_subtype(n) == discretionary_choice_subtype) {
2456                    nodelib_push_direct_or_nil(L, choice_pre_break(n));
2457                    return 1;
2458                } else {
2459                    break;
2460                }
2461        }
2462    }
2463    return 0;
2464}
2465
2466static int nodelib_direct_getpost(lua_State *L)
2467{
2468    halfword n = nodelib_valid_direct_from_index(L, 1);
2469    if (n) {
2470        switch (node_type(n)) {
2471            case disc_node:
2472                nodelib_push_direct_or_nil(L, disc_post_break_head(n));
2473                nodelib_push_direct_or_nil(L, disc_post_break_tail(n));
2474                return 2;
2475            case hlist_node:
2476            case vlist_node:
2477                {
2478                    halfword h = box_post_migrated(n);
2479                    halfword t = tex_tail_of_node_list(h);
2480                    nodelib_push_direct_or_nil(L, h);
2481                    nodelib_push_direct_or_nil(L, t);
2482                    return 2;
2483                }
2484            case choice_node:
2485                if (node_subtype(n) == discretionary_choice_subtype) {
2486                    nodelib_push_direct_or_nil(L, choice_post_break(n));
2487                    return 1;
2488                } else {
2489                    break;
2490                }
2491        }
2492    }
2493    return 0;
2494}
2495
2496static int nodelib_direct_getreplace(lua_State *L)
2497{
2498    halfword n = nodelib_valid_direct_from_index(L, 1);
2499    if (n) {
2500        switch (node_type(n)) {
2501            case disc_node:
2502                nodelib_push_direct_or_nil(L, disc_no_break_head(n));
2503                nodelib_push_direct_or_nil(L, disc_no_break_tail(n));
2504                return 2;
2505            case choice_node:
2506                if (node_subtype(n) == discretionary_choice_subtype) {
2507                    nodelib_push_direct_or_nil(L, choice_no_break(n));
2508                    return 1;
2509                } else {
2510                    break;
2511                }
2512        }
2513    }
2514    return 0;
2515}
2516
2517/* node.direct.setpre */
2518/* node.direct.setpost */
2519/* node.direct.setreplace */
2520
2521# define setpre_usage     (disc_usage | hlist_usage | vlist_usage)
2522# define setpost_usage    setpre_usage
2523# define setreplace_usage (disc_usage)
2524
2525static int nodelib_direct_setpre(lua_State *L)
2526{
2527    halfword n = nodelib_valid_direct_from_index(L, 1);
2528    if (n) {
2529        halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
2530        switch (node_type(n)) {
2531            case disc_node:
2532                tex_set_disc_field(n, pre_break_code, m);
2533                break;
2534            case hlist_node:
2535            case vlist_node:
2536                box_pre_migrated(n) = m;
2537                break;
2538        }
2539    }
2540    return 0;
2541}
2542
2543static int nodelib_direct_setpost(lua_State *L)
2544{
2545    halfword n = nodelib_valid_direct_from_index(L, 1);
2546    if (n) {
2547        halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
2548        switch (node_type(n)) {
2549            case disc_node:
2550                tex_set_disc_field(n, post_break_code, m);
2551                break;
2552            case hlist_node:
2553            case vlist_node:
2554                box_post_migrated(n) = m;
2555                break;
2556        }
2557    }
2558    return 0;
2559}
2560
2561static int nodelib_direct_setreplace(lua_State *L)
2562{
2563    halfword n = nodelib_valid_direct_from_index(L, 1);
2564    if (n && node_type(n) == disc_node) {
2565        halfword m = (lua_gettop(L) > 1) ? nodelib_valid_direct_from_index(L, 2) : null;
2566        tex_set_disc_field(n, no_break_code, m);
2567    }
2568    return 0;
2569}
2570
2571/* node.direct.getwidth  */
2572/* node.direct.setwidth  */
2573
2574/* split ifs for clearity .. compiler will optimize */
2575
2576# define getwidth_usage (hlist_usage | vlist_usage | unset_usage | align_record_usage | rule_usage | glue_usage | glue_spec_usage | glyph_usage | kern_usage | math_usage)
2577
2578static int nodelib_direct_getwidth(lua_State *L)
2579{
2580    halfword n = nodelib_valid_direct_from_index(L, 1);
2581    if (n) {
2582        switch (node_type(n)) {
2583            case hlist_node:
2584            case vlist_node:
2585            case unset_node:
2586                lua_pushinteger(L, box_width(n));
2587                break;
2588            case align_record_node:
2589                lua_pushinteger(L, box_width(n));
2590                if (lua_toboolean(L, 2)) {
2591                    lua_pushinteger(L, box_size(n));
2592                    return 2;
2593                }
2594                break;
2595            case rule_node:
2596                lua_pushinteger(L, rule_width(n));
2597                break;
2598            case glue_node:
2599            case glue_spec_node:
2600                lua_pushinteger(L, glue_amount(n));
2601                break;
2602            case glyph_node:
2603                lua_pushnumber(L, tex_glyph_width(n));
2604                if (lua_toboolean(L, 2)) {
2605                    lua_pushinteger(L, glyph_expansion(n));
2606                    return 2;
2607                }
2608                break;
2609            case kern_node:
2610                lua_pushinteger(L, kern_amount(n));
2611                if (lua_toboolean(L, 2)) {
2612                    lua_pushinteger(L, kern_expansion(n));
2613                    return 2;
2614                }
2615                break;
2616            case math_node:
2617                lua_pushinteger(L, math_amount(n));
2618                break;
2619            default:
2620                lua_pushnil(L);
2621                break;
2622        }
2623    } else {
2624        lua_pushnil(L);
2625    }
2626    return 1;
2627}
2628
2629# define setwidth_usage (hlist_usage | vlist_usage | unset_usage | align_record_usage | rule_usage | glue_usage | glue_spec_usage | kern_usage | math_usage)
2630
2631static int nodelib_direct_setwidth(lua_State *L)
2632{
2633    halfword n = nodelib_valid_direct_from_index(L, 1);
2634    if (n) {
2635        switch (node_type(n)) {
2636            case hlist_node:
2637            case vlist_node:
2638            case unset_node:
2639            case align_record_node:
2640                box_width(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
2641                if (lua_type(L, 3) == LUA_TNUMBER) {
2642                    box_size(n) = lmt_roundnumber(L, 3);
2643                    box_package_state(n) = package_dimension_size_set;
2644                }
2645                break;
2646            case rule_node:
2647                rule_width(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
2648                break;
2649            case glue_node:
2650            case glue_spec_node:
2651                glue_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
2652                break;
2653            case kern_node:
2654                kern_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
2655                break;
2656            case math_node:
2657                math_amount(n) = lua_type(L, 2) == LUA_TNUMBER ? lmt_roundnumber(L, 2) : 0;
2658                break;
2659        }
2660    }
2661    return 0;
2662}
2663
2664/* node.direct.getheight */
2665/* node.direct.setheight */
2666
2667# define getheight_usage (hlist_usage | vlist_usage | unset_usage | rule_usage | insert_usage | glyph_usage | fence_usage)
2668
2669static int nodelib_direct_getheight(lua_State *L)
2670{
2671    halfword n = nodelib_valid_direct_from_index(L, 1);
2672    if (n) {
2673        switch (node_type(n)) {
2674            case hlist_node:
2675            case vlist_node:
2676            case unset_node:
2677                lua_pushinteger(L, box_height(n));
2678                break;
2679            case rule_node:
2680                lua_pushinteger(L, rule_height(n));
2681                break;
2682            case insert_node:
2683                lua_pushinteger(L, insert_total_height(n));
2684                break;
2685            case glyph_node:
2686                lua_pushinteger(L, tex_glyph_height(n));
2687                break;
2688            case fence_noad:
2689                lua_pushinteger(L, noad_height(n));
2690                break;
2691            default:
2692                lua_pushnil(L);
2693                break;
2694        }
2695    } else {
2696        lua_pushnil(L);
2697    }
2698    return 1;
2699}
2700
2701# define setheight_usage (hlist_usage | vlist_usage | unset_usage | rule_usage | insert_usage | fence_usage)
2702
2703static int nodelib_direct_setheight(lua_State *L)
2704{
2705    halfword n = nodelib_valid_direct_from_index(L, 1);
2706    if (n) {
2707        halfword h = 0;
2708        if (lua_type(L, 2) == LUA_TNUMBER) {
2709            h = lmt_roundnumber(L, 2);
2710        }
2711        switch (node_type(n)) {
2712            case hlist_node:
2713            case vlist_node:
2714            case unset_node:
2715                box_height(n) = h;
2716                break;
2717            case rule_node:
2718                rule_height(n) = h;
2719                break;
2720            case insert_node:
2721                insert_total_height(n) = h;
2722                break;
2723            case fence_noad:
2724                noad_height(n) = h;
2725                break;
2726        }
2727    }
2728    return 0;
2729}
2730
2731/* node.direct.getdepth */
2732/* node.direct.setdepth */
2733
2734# define getdepth_usage getheight_usage
2735
2736static int nodelib_direct_getdepth(lua_State *L)
2737{
2738    halfword n = nodelib_valid_direct_from_index(L, 1);
2739    if (n) {
2740        switch (node_type(n)) {
2741            case hlist_node:
2742            case vlist_node:
2743            case unset_node:
2744                lua_pushinteger(L, box_depth(n));
2745                break;
2746            case rule_node:
2747                lua_pushinteger(L, rule_depth(n));
2748                break;
2749            case insert_node:
2750                lua_pushinteger(L, insert_max_depth(n));
2751                break;
2752            case glyph_node:
2753                lua_pushinteger(L, tex_glyph_depth(n));
2754                break;
2755            case fence_noad:
2756                lua_pushinteger(L, noad_depth(n));
2757                break;
2758            default:
2759                lua_pushnil(L);
2760                break;
2761        }
2762    } else {
2763        lua_pushnil(L);
2764    }
2765    return 1;
2766}
2767
2768# define setdepth_usage setheight_usage
2769
2770static int nodelib_direct_setdepth(lua_State *L)
2771{
2772    halfword n = nodelib_valid_direct_from_index(L, 1);
2773    if (n) {
2774        halfword d = 0;
2775        if (lua_type(L, 2) == LUA_TNUMBER) {
2776            d = lmt_roundnumber(L, 2);
2777        }
2778        switch (node_type(n)) {
2779            case hlist_node:
2780            case vlist_node:
2781            case unset_node:
2782                box_depth(n) = d;
2783                break;
2784            case rule_node:
2785                rule_depth(n) = d;
2786                break;
2787            case insert_node:
2788                insert_max_depth(n) = d;
2789                break;
2790            case fence_noad:
2791                noad_depth(n) = d;
2792                break;
2793        }
2794    }
2795    return 0;
2796}
2797
2798/* node.direct.gettotal */
2799/* node.direct.settotal */
2800
2801# define gettotal_usage (hlist_usage | vlist_usage | unset_usage | rule_usage | insert_usage | glyph_usage | fence_usage)
2802# define settotal_usage insert_usage
2803
2804static int nodelib_direct_gettotal(lua_State *L)
2805{
2806    halfword n = nodelib_valid_direct_from_index(L, 1);
2807    if (n) {
2808        switch (node_type(n)) {
2809            case hlist_node:
2810            case vlist_node:
2811            case unset_node:
2812                lua_pushinteger(L, (lua_Integer) box_total(n));
2813                break;
2814            case rule_node:
2815                lua_pushinteger(L, (lua_Integer) rule_total(n));
2816                break;
2817            case insert_node:
2818                lua_pushinteger(L, (lua_Integer) insert_total_height(n));
2819                break;
2820            case glyph_node:
2821                lua_pushinteger(L, (lua_Integer) tex_glyph_total(n));
2822                break;
2823            case fence_noad:
2824                lua_pushinteger(L, (lua_Integer) noad_total(n));
2825                break;
2826            default:
2827                lua_pushnil(L);
2828                break;
2829        }
2830    } else {
2831        lua_pushnil(L);
2832    }
2833    return 1;
2834}
2835
2836static int nodelib_direct_settotal(lua_State *L)
2837{
2838    halfword n = nodelib_valid_direct_from_index(L, 1);
2839    if (n) {
2840        switch (node_type(n)) {
2841            case insert_node:
2842                insert_total_height(n) = lua_type(L, 2) == LUA_TNUMBER ? (halfword) lmt_roundnumber(L,2) : 0;
2843                break;
2844        }
2845    }
2846    return 0;
2847}
2848
2849/* node.direct.getshift */
2850/* node.direct.setshift */
2851
2852# define getshift_usage (hlist_usage | vlist_usage)
2853# define setshift_usage getshift_usage
2854
2855static int nodelib_direct_getshift(lua_State *L)
2856{
2857    halfword n = nodelib_valid_direct_from_index(L, 1);
2858    if (n) {
2859        switch (node_type(n)) {
2860            case hlist_node:
2861            case vlist_node:
2862                lua_pushinteger(L, box_shift_amount(n));
2863                return 1;
2864        }
2865    }
2866    return 0;
2867}
2868
2869static int nodelib_direct_setshift(lua_State *L)
2870{
2871    halfword n = nodelib_valid_direct_from_index(L, 1);
2872    if (n) {
2873        switch (node_type(n)) {
2874            case hlist_node:
2875            case vlist_node:
2876                if (lua_type(L, 2) == LUA_TNUMBER) {
2877                    box_shift_amount(n) = (halfword) lmt_roundnumber(L,2);
2878                } else {
2879                    box_shift_amount(n) = 0;
2880                }
2881                break;
2882        }
2883    }
2884    return 0;
2885}
2886
2887/* node.direct.getindex */
2888/* node.direct.setindex */
2889
2890# define getindex_usage (hlist_usage | vlist_usage | insert_usage | mark_usage | adjust_usage)
2891# define setindex_usage getindex_usage
2892
2893static int nodelib_direct_getindex(lua_State* L)
2894{
2895    halfword n = nodelib_valid_direct_from_index(L, 1);
2896    if (n) {
2897        switch (node_type(n)) {
2898            case hlist_node:
2899            case vlist_node:
2900                lua_pushinteger(L, box_index(n));
2901                break;
2902            case insert_node:
2903                lua_pushinteger(L, insert_index(n));
2904                break;
2905            case mark_node:
2906                lua_pushinteger(L, mark_index(n));
2907                break;
2908            case adjust_node:
2909                lua_pushinteger(L, adjust_index(n));
2910                break;
2911        }
2912    } else {
2913        lua_pushnil(L);
2914    }
2915    return 1;
2916}
2917
2918static int nodelib_direct_setindex(lua_State* L)
2919{
2920    halfword n = nodelib_valid_direct_from_index(L, 1);
2921    if (n) {
2922        switch (node_type(n)) {
2923            case hlist_node:
2924            case vlist_node:
2925                {
2926                    halfword index = lmt_tohalfword(L, 2);
2927                    if (tex_valid_box_index(index)) {
2928                        box_index(n) = index;
2929                    } else {
2930                        /* error or just ignore */
2931                    }
2932                    break;
2933                }
2934            case insert_node:
2935                {
2936                    halfword index = lmt_tohalfword(L, 2);
2937                    if (tex_valid_insert_id(index)) {
2938                        insert_index(n) = index;
2939                    } else {
2940                        /* error or just ignore */
2941                    }
2942                    break;
2943                }
2944            case mark_node:
2945                {
2946                    halfword index = lmt_tohalfword(L, 2);
2947                    if (tex_valid_mark(index)) {
2948                       mark_index(n) = index;
2949                    }
2950                }
2951                break;
2952            case adjust_node:
2953                {
2954                    halfword index = lmt_tohalfword(L, 2);
2955                    if (tex_valid_adjust_index(index)) {
2956                        adjust_index(n) = index;
2957                    }
2958                }
2959                break;
2960        }
2961    } else {
2962        lua_pushnil(L);
2963    }
2964    return 1;
2965}
2966
2967/* node.direct.hasgeometry */
2968/* node.direct.getgeometry */
2969/* node.direct.setgeometry */
2970
2971# define hasgeometry_usage (hlist_usage | vlist_usage)
2972# define getgeometry_usage hasgeometry_usage
2973# define setgeometry_usage hasgeometry_usage
2974
2975static int nodelib_direct_hasgeometry(lua_State* L)
2976{
2977    halfword n = nodelib_valid_direct_from_index(L, 1);
2978    if (n) {
2979        switch (node_type(n)) {
2980            case hlist_node:
2981            case vlist_node:
2982                if (box_geometry(n)) {
2983                    lua_pushinteger(L, box_geometry(n));
2984                    return 1;
2985                }
2986        }
2987    }
2988    lua_pushboolean(L, 0);
2989    return 1;
2990}
2991
2992static int nodelib_direct_getgeometry(lua_State* L)
2993{
2994    halfword n = nodelib_valid_direct_from_index(L, 1);
2995    if (n) {
2996        switch (node_type(n)) {
2997            case hlist_node:
2998            case vlist_node:
2999                if (box_geometry(n)) {
3000                    lua_pushinteger(L, box_geometry(n));
3001                    if (lua_toboolean(L, 2)) {
3002                        lua_pushboolean(L, tex_has_box_geometry(n, offset_geometry));
3003                        lua_pushboolean(L, tex_has_box_geometry(n, orientation_geometry));
3004                        lua_pushboolean(L, tex_has_box_geometry(n, anchor_geometry));
3005                        lua_pushinteger(L, checked_direction_value(box_dir(n)));
3006                        return 5;
3007                    } else {
3008                        return 1;
3009                    }
3010                } else if (lua_toboolean(L, 2)) {
3011                    lua_pushboolean(L, 0);
3012                    lua_pushboolean(L, 0);
3013                    lua_pushboolean(L, 0);
3014                    lua_pushboolean(L, 0);
3015                    lua_pushinteger(L, checked_direction_value(box_dir(n)));
3016                    return 5;
3017                }
3018                break;
3019        }
3020    }
3021    lua_pushboolean(L, 0);
3022    return 1;
3023}
3024
3025static int nodelib_direct_setgeometry(lua_State* L)
3026{
3027    halfword n = nodelib_valid_direct_from_index(L, 1);
3028    if (n) {
3029        switch (node_type(n)) {
3030            case hlist_node:
3031            case vlist_node:
3032                box_geometry(n) = (singleword) lmt_tohalfword(L, 2);
3033                break;
3034        }
3035    }
3036    return 0;
3037}
3038
3039/* node.direct.getorientation */
3040/* node.direct.setorientation */
3041
3042# define getorientation_usage (hlist_usage |vlist_usage)
3043# define setorientation_usage getorientation_usage
3044
3045static int nodelib_direct_getorientation(lua_State *L)
3046{
3047    halfword n = nodelib_valid_direct_from_index(L, 1);
3048    if (n) {
3049        switch (node_type(n)) {
3050            case hlist_node:
3051            case vlist_node:
3052                lua_pushinteger(L, box_orientation(n));
3053                lua_pushinteger(L, box_x_offset(n));
3054                lua_pushinteger(L, box_y_offset(n));
3055                lua_pushinteger(L, box_w_offset(n));
3056                lua_pushinteger(L, box_h_offset(n));
3057                lua_pushinteger(L, box_d_offset(n));
3058                return 6;
3059        }
3060    }
3061    return 0;
3062}
3063
3064static int nodelib_direct_setorientation(lua_State *L)
3065{
3066    halfword n = nodelib_valid_direct_from_index(L, 1);
3067    if (n) {
3068        switch (node_type(n)) {
3069            case hlist_node:
3070            case vlist_node:
3071                switch (lua_type(L, 2)) {
3072                    case LUA_TNUMBER:
3073                        box_orientation(n) = lmt_tohalfword(L, 2);
3074                        break;
3075                    case LUA_TBOOLEAN:
3076                        if (lua_toboolean(L, 2)) {
3077                            break;
3078                       }
3079                    default:
3080                        box_orientation(n) = 0;
3081                        break;
3082                }
3083                switch (lua_type(L, 3)) {
3084                    case LUA_TNUMBER:
3085                        box_x_offset(n) = lmt_tohalfword(L, 3);
3086                        break;
3087                    case LUA_TBOOLEAN:
3088                        if (lua_toboolean(L, 3)) {
3089                            break;
3090                        }
3091                    default:
3092                        box_x_offset(n) = 0;
3093                        break;
3094                }
3095                switch (lua_type(L, 4)) {
3096                    case LUA_TNUMBER:
3097                        box_y_offset(n) = lmt_tohalfword(L, 4);
3098                        break;
3099                    case LUA_TBOOLEAN:
3100                        if (lua_toboolean(L, 4)) {
3101                            break;
3102                        }
3103                    default:
3104                        box_y_offset(n) = 0;
3105                        break;
3106                }
3107                switch (lua_type(L, 5)) {
3108                    case LUA_TNUMBER:
3109                        box_w_offset(n) = lmt_tohalfword(L, 5);
3110                        break;
3111                    case LUA_TBOOLEAN:
3112                        if (lua_toboolean(L, 5)) {
3113                            break;
3114                        }
3115                    default:
3116                        box_w_offset(n) = 0;
3117                        break;
3118                }
3119                switch (lua_type(L, 6)) {
3120                    case LUA_TNUMBER:
3121                        box_h_offset(n) = lmt_tohalfword(L, 6);
3122                        break;
3123                    case LUA_TBOOLEAN:
3124                        if (lua_toboolean(L, 6)) {
3125                            break;
3126                        }
3127                    default:
3128                        box_h_offset(n) = 0;
3129                        break;
3130                }
3131                switch (lua_type(L, 7)) {
3132                    case LUA_TNUMBER:
3133                        box_d_offset(n) = lmt_tohalfword(L, 7);
3134                        break;
3135                    case LUA_TBOOLEAN:
3136                        if (lua_toboolean(L, 7)) {
3137                            break;
3138                        }
3139                    default:
3140                        box_d_offset(n) = 0;
3141                        break;
3142                }
3143                tex_check_box_geometry(n);
3144                break;
3145        }
3146    }
3147    return 0;
3148}
3149
3150/* node.direct.setoptions */
3151/* node.direct.getoptions */
3152
3153# define setoptions_usage (glyph_usage | disc_usage | glue_usage | rule_usage | math_usage | penalty_usage | simple_usage | radical_usage | fraction_usage | accent_usage | math_char_usage | math_text_char_usage)
3154# define getoptions_usage setoptions_usage
3155
3156static int nodelib_direct_getoptions(lua_State *L)
3157{
3158    halfword n = nodelib_valid_direct_from_index(L, 1);
3159    if (n) {
3160        switch (node_type(n)) {
3161            case glyph_node:
3162                lua_pushinteger(L, glyph_options(n));
3163                return 1;
3164            case disc_node:
3165                lua_pushinteger(L, disc_options(n));
3166                return 1;
3167            case glue_node:
3168                lua_pushinteger(L, glue_options(n));
3169                return 1;
3170            case rule_node:
3171                lua_pushinteger(L, rule_options(n));
3172                return 1;
3173            case math_node:
3174                lua_pushinteger(L, math_options(n));
3175                return 1;
3176            case penalty_node:
3177                lua_pushinteger(L, penalty_options(n));
3178                return 1;
3179            case simple_noad:
3180            case radical_noad:
3181            case fraction_noad:
3182            case accent_noad:
3183            case fence_noad:
3184                lua_pushinteger(L, noad_options(n));
3185                return 1;
3186            case math_char_node:
3187            case math_text_char_node:
3188                lua_pushinteger(L, kernel_math_options(n));
3189                return 1;
3190          }
3191    }
3192    return 0;
3193}
3194
3195static int nodelib_direct_setoptions(lua_State *L)
3196{
3197    halfword n = nodelib_valid_direct_from_index(L, 1);
3198    if (n) {
3199        switch (node_type(n)) {
3200            case glyph_node:
3201                set_glyph_options(n, lmt_tohalfword(L, 2) & glyph_option_valid);
3202                break;
3203            case disc_node:
3204                set_disc_options(n, lmt_tohalfword(L, 2) & disc_option_valid);
3205                break;
3206            case glue_node:
3207                tex_add_glue_option(n, lmt_tohalfword(L, 2));
3208                return 1;
3209            case rule_node:
3210                set_rule_options(n, lmt_tohalfword(L, 2) & rule_option_valid);
3211                break;
3212            case math_node:
3213                tex_add_math_option(n, lmt_tohalfword(L, 2));
3214                return 1;
3215            case penalty_node:
3216                tex_add_penalty_option(n, lmt_tohalfword(L, 2));
3217                return 1;
3218            case simple_noad:
3219            case radical_noad:
3220            case fraction_noad:
3221            case accent_noad:
3222            case fence_noad:
3223                noad_options(n) = lmt_tofullword(L, 2);
3224                break;
3225            case math_char_node:
3226            case math_text_char_node:
3227                kernel_math_options(n) = lmt_tohalfword(L, 2);
3228                break;
3229        }
3230    }
3231    return 0;
3232}
3233
3234/* node.direct.getwhd */
3235/* node.direct.setwhd */
3236
3237# define getwhd_usage (hlist_usage | vlist_usage | unset_usage | rule_usage | glyph_usage | glue_usage )
3238# define setwhd_usage getwhd_usage
3239
3240static int nodelib_direct_getwhd(lua_State *L)
3241{
3242    halfword n = nodelib_valid_direct_from_index(L, 1);
3243    if (n) {
3244      AGAIN:
3245        switch (node_type(n)) {
3246            case hlist_node:
3247            case vlist_node:
3248            case unset_node:
3249                lua_pushinteger(L, box_width(n));
3250                lua_pushinteger(L, box_height(n));
3251                lua_pushinteger(L, box_depth(n));
3252                return 3;
3253            case rule_node:
3254                lua_pushinteger(L, rule_width(n));
3255                lua_pushinteger(L, rule_height(n));
3256                lua_pushinteger(L, rule_depth(n));
3257                return 3;
3258            case glyph_node:
3259                /* or glyph_dimensions: */
3260                lua_pushinteger(L, tex_glyph_width(n));
3261                lua_pushinteger(L, tex_glyph_height(n));
3262                lua_pushinteger(L, tex_glyph_depth(n));
3263                if (lua_toboolean(L,2)) {
3264                    lua_pushinteger(L, glyph_expansion(n));
3265                    return 4;
3266                } else {
3267                    return 3;
3268                }
3269            case glue_node:
3270                n = glue_leader_ptr(n);
3271                if (n) {
3272                    goto AGAIN;
3273                } else {
3274                    break;
3275                }
3276        }
3277    }
3278    return 0;
3279}
3280
3281static int nodelib_direct_setwhd(lua_State *L)
3282{
3283    halfword n = nodelib_valid_direct_from_index(L, 1);
3284    if (n) {
3285      AGAIN:
3286        switch (node_type(n)) {
3287            case hlist_node:
3288            case vlist_node:
3289            case unset_node:
3290                {
3291                    int top = lua_gettop(L) ;
3292                    if (top > 1) {
3293                        if ((lua_type(L, 2) == LUA_TNUMBER)) {
3294                            box_width(n) = (halfword) lmt_roundnumber(L, 2);
3295                        } else {
3296                            /*Leave as is */
3297                        }
3298                        if (top > 2) {
3299                            if ((lua_type(L, 3) == LUA_TNUMBER)) {
3300                                box_height(n) = (halfword) lmt_roundnumber(L, 3);
3301                            } else {
3302                                /*Leave as is */
3303                            }
3304                            if (top > 3) {
3305                                if ((lua_type(L, 4) == LUA_TNUMBER)) {
3306                                    box_depth(n) = (halfword) lmt_roundnumber(L, 4);
3307                                } else {
3308                                    /*Leave as is */
3309                                }
3310                            }
3311                        }
3312                    }
3313                }
3314                break;
3315            case rule_node:
3316                {
3317                    int top = lua_gettop(L) ;
3318                    if (top > 1) {
3319                        if ((lua_type(L, 2) == LUA_TNUMBER)) {
3320                            rule_width(n) = (halfword) lmt_roundnumber(L, 2);
3321                        } else {
3322                            /*Leave as is */
3323                        }
3324                        if (top > 2) {
3325                            if ((lua_type(L, 3) == LUA_TNUMBER)) {
3326                                rule_height(n) = (halfword) lmt_roundnumber(L, 3);
3327                            } else {
3328                                /*Leave as is */
3329                            }
3330                            if (top > 3) {
3331                                if ((lua_type(L, 4) == LUA_TNUMBER)) {
3332                                    rule_depth(n) = (halfword) lmt_roundnumber(L, 4);
3333                                } else {
3334                                    /*Leave as is */
3335                                }
3336                            }
3337                        }
3338                    }
3339                }
3340                break;
3341            case glue_node:
3342                n = glue_leader_ptr(n);
3343                if (n) {
3344                    goto AGAIN;
3345                } else {
3346                    break;
3347                }
3348        }
3349    }
3350    return 0;
3351}
3352
3353/* node.direct.getcornerkerns */
3354
3355# define getcornerkerns_usage glyph_usage
3356
3357static int nodelib_direct_getcornerkerns(lua_State *L)
3358{
3359    halfword n = nodelib_valid_direct_from_index(L, 1);
3360    if (n && node_type(n) == glyph_node) {
3361        scaledkrn krn = tex_char_corner_kerns_from_glyph(n);
3362        lua_pushinteger(L, krn.bl);
3363        lua_pushinteger(L, krn.br);
3364        lua_pushinteger(L, krn.tl);
3365        lua_pushinteger(L, krn.tr);
3366        return 4;
3367    }
3368    return 0;
3369}
3370
3371/* node.direct.hasdimensions */
3372
3373# define hasdimensions_usage (hlist_usage | vlist_usage | unset_usage | rule_usage | glyph_usage | glue_usage)
3374
3375static int nodelib_direct_hasdimensions(lua_State *L)
3376{
3377    int b = 0;
3378    halfword n = nodelib_valid_direct_from_index(L, 1);
3379    if (n) {
3380        switch (node_type(n)) {
3381            case hlist_node:
3382            case vlist_node:
3383            case unset_node:
3384                b = (box_width(n) > 0) || (box_total(n) > 0);
3385                break;
3386            case rule_node:
3387                b = (rule_width(n) > 0) || (rule_total(n) > 0);
3388                break;
3389            case glyph_node:
3390                b = tex_glyph_has_dimensions(n);
3391                break;
3392            case glue_node:
3393                {
3394                    halfword l = glue_leader_ptr(n);
3395                    if (l) {
3396                        switch (node_type(l)) {
3397                            case hlist_node:
3398                            case vlist_node:
3399                                b = (box_width(l) > 0) || (box_total(l) > 0);
3400                                break;
3401                            case rule_node:
3402                                b = (rule_width(l) > 0) || (rule_total(l) > 0);
3403                                break;
3404                        }
3405                    }
3406                }
3407                break;
3408        }
3409    }
3410    lua_pushboolean(L, b);
3411    return 1;
3412}
3413
3414/*tex
3415
3416    When the height and depth of a box is calculated the |y-offset| is taken into account. In \LUATEX\
3417    this is different for the height and depth, an historic artifact. However, because that can be
3418    controlled we now have this helper, mostly for tracing purposes because it listens to the mode
3419    parameter (and one can emulate other scenarios already).
3420
3421*/
3422
3423/* node.direct.getglyphdimensions */
3424
3425# define getglyphdimensions_usage glyph_usage
3426
3427static int nodelib_direct_getglyphdimensions(lua_State *L)
3428{
3429    halfword n = nodelib_valid_direct_from_index(L, 1);
3430    if (n && node_type(n) == glyph_node) {
3431        scaledwhd whd = tex_glyph_dimensions_ex(n);
3432        lua_pushinteger(L, whd.wd);
3433        lua_pushinteger(L, whd.ht);
3434        lua_pushinteger(L, whd.dp);
3435        lua_pushinteger(L, glyph_expansion(n)); /* in case we need it later on */
3436        nodelib_aux_pushxyscales(L, n);
3437        lua_pushnumber(L, (double) glyph_slant(n) / 1000);
3438        lua_pushnumber(L, (double) glyph_weight(n) / 1000);
3439     // lua_pushinteger(L, glyph_slant(n));
3440        return 8;
3441    } else {
3442        return 0;
3443    }
3444}
3445
3446/* node.direct.getkerndimension */
3447
3448# define getkerndimension_usage kern_usage
3449
3450static int nodelib_direct_getkerndimension(lua_State *L)
3451{
3452    halfword n = nodelib_valid_direct_from_index(L, 1);
3453    if (n && node_type(n) == kern_node) {
3454        lua_pushinteger(L, tex_kern_dimension_ex(n));
3455        return 1;
3456    } else {
3457        return 0;
3458    }
3459}
3460
3461/* node.direct.getlistdimensions */
3462
3463# define getlistdimensions_usage (hlist_usage | vlist_usage)
3464
3465static int nodelib_direct_getlistdimensions(lua_State *L)
3466{
3467    halfword n = nodelib_valid_direct_from_index(L, 1);
3468    if (n) {
3469        switch (node_type(n)) {
3470            /* when we need it more node types will be handled */
3471            case hlist_node:
3472            case vlist_node:
3473                lua_pushinteger(L, box_width(n));
3474                lua_pushinteger(L, box_height(n));
3475                lua_pushinteger(L, box_depth(n));
3476                lua_pushinteger(L, box_shift_amount(n));
3477                nodelib_push_direct_or_nil_node_prev(L, box_list(n));
3478                nodelib_push_direct_or_nil(L, box_except(n)); /* experiment */
3479                return 6;
3480        }
3481    }
3482    return 0;
3483}
3484
3485/* node.direct.getruledimensions */
3486/* node.direct.setruledimensions */
3487
3488# define getruledimensions_usage rule_usage
3489# define setruledimensions_usage rule_usage
3490
3491static int nodelib_direct_getruledimensions(lua_State *L)
3492{
3493    halfword n = nodelib_valid_direct_from_index(L, 1);
3494    if (n && node_type(n) == rule_node) {
3495        if (node_subtype(n) == virtual_rule_subtype) {
3496            lua_pushinteger(L, rule_virtual_width(n));
3497            lua_pushinteger(L, rule_virtual_height(n));
3498            lua_pushinteger(L, rule_virtual_depth(n));
3499            lua_pushboolean(L, 1);
3500        } else {
3501            lua_pushinteger(L, rule_width(n));
3502            lua_pushinteger(L, rule_height(n));
3503            lua_pushinteger(L, rule_depth(n));
3504            lua_pushboolean(L, 0);
3505        }
3506        return 4;
3507    } else {
3508        return 0;
3509    }
3510}
3511
3512static int nodelib_direct_setruledimensions(lua_State *L)
3513{
3514    halfword n = nodelib_valid_direct_from_index(L, 1);
3515    if (n && node_type(n) == rule_node) {
3516        scaled wd = (scaled) lmt_roundnumber(L, 2);
3517        scaled ht = (scaled) lmt_roundnumber(L, 3);
3518        scaled dp = (scaled) lmt_roundnumber(L, 4);
3519        if (node_subtype(n) == virtual_rule_subtype) {
3520            rule_virtual_width(n) = wd;
3521            rule_virtual_height(n) = ht;
3522            rule_virtual_depth(n) = dp;
3523            rule_width(n) = 0;
3524            rule_height(n) = 0;
3525            rule_depth(n) = 0;
3526        } else {
3527            rule_width(n) = wd;
3528            rule_height(n) = ht;
3529            rule_depth(n) = dp;
3530        }
3531        if (lua_type(L, 5) == LUA_TNUMBER) {
3532            rule_data(n) = (halfword) lmt_roundnumber(L, 5);
3533        }
3534    }
3535    return 0;
3536}
3537
3538/* node.direct.getlist */
3539/* node.direct.setlist */
3540
3541# define getlist_usage (hlist_usage | vlist_usage | unset_usage | sub_box_usage | sub_mlist_usage | insert_usage | adjust_usage)
3542# define setlist_usage getlist_usage
3543
3544static int nodelib_direct_getlist(lua_State *L)
3545{
3546    halfword n = nodelib_valid_direct_from_index(L, 1);
3547    if (n) {
3548        switch (node_type(n)) {
3549            case hlist_node:
3550            case vlist_node:
3551            case unset_node:
3552            case align_record_node:
3553                nodelib_push_direct_or_nil_node_prev(L, box_list(n));
3554                break;
3555            case sub_box_node:
3556            case sub_mlist_node:
3557                nodelib_push_direct_or_nil_node_prev(L, kernel_math_list(n));
3558                break;
3559            case insert_node:
3560                /* kind of fuzzy */
3561                nodelib_push_direct_or_nil_node_prev(L, insert_list(n));
3562                break;
3563            case adjust_node:
3564                nodelib_push_direct_or_nil_node_prev(L, adjust_list(n));
3565                break;
3566            default:
3567                lua_pushnil(L);
3568                break;
3569        }
3570    } else {
3571        lua_pushnil(L);
3572    }
3573    return 1;
3574}
3575
3576static int nodelib_direct_setlist(lua_State *L)
3577{
3578    halfword n = nodelib_valid_direct_from_index(L, 1);
3579    if (n) {
3580        switch (node_type(n)) {
3581            case hlist_node:
3582            case vlist_node:
3583            case unset_node:
3584                box_list(n) = nodelib_valid_direct_from_index(L, 2);
3585                break;
3586            case sub_box_node:
3587            case sub_mlist_node:
3588                kernel_math_list(n) = nodelib_valid_direct_from_index(L, 2);
3589                break;
3590            case insert_node:
3591                /* kind of fuzzy */
3592                insert_list(n) = nodelib_valid_direct_from_index(L, 2);
3593                break;
3594            case adjust_node:
3595                adjust_list(n) = nodelib_valid_direct_from_index(L, 2);
3596                break;
3597        }
3598    }
3599    return 0;
3600}
3601
3602/* node.direct.getexcept */
3603/* node.direct.setexcept */
3604
3605# define getexcept_usage (hlist_usage | vlist_usage | unset_usage)
3606# define setexcept_usage getexcept_usage
3607
3608static int nodelib_direct_getexcept(lua_State *L)
3609{
3610    halfword n = nodelib_valid_direct_from_index(L, 1);
3611    if (n) {
3612        switch (node_type(n)) {
3613            case hlist_node:
3614            case vlist_node:
3615            case unset_node:
3616            default:
3617                nodelib_push_direct_or_nil(L, box_except(n));
3618                lua_pushinteger(L, box_exdepth(n));
3619                return 2;
3620        }
3621    }
3622    lua_pushnil(L);
3623    lua_pushnil(L);
3624    return 2;
3625}
3626
3627static int nodelib_direct_setexcept(lua_State *L)
3628{
3629    halfword n = nodelib_valid_direct_from_index(L, 1);
3630    if (n) {
3631        switch (node_type(n)) {
3632            case hlist_node:
3633            case vlist_node:
3634            case unset_node:
3635                box_except(n) = nodelib_valid_direct_from_index(L, 2);
3636                box_exdepth(n) = lmt_optinteger(L, 3, 0);
3637                break;
3638        }
3639    }
3640    return 0;
3641}
3642
3643/* node.direct.getleader */
3644/* node.direct.setleader */
3645
3646# define getleader_usage glue_usage
3647# define setleader_usage getleader_usage
3648
3649static int nodelib_direct_getleader(lua_State *L)
3650{
3651    halfword n = nodelib_valid_direct_from_index(L, 1);
3652    if (n && node_type(n) == glue_node) {
3653        nodelib_push_direct_or_nil(L, glue_leader_ptr(n));
3654    } else {
3655        lua_pushnil(L);
3656    }
3657    return 1;
3658}
3659
3660static int nodelib_direct_setleader(lua_State *L)
3661{
3662    halfword n = nodelib_valid_direct_from_index(L, 1);
3663    if (n && node_type(n) == glue_node) {
3664        glue_leader_ptr(n) = nodelib_valid_direct_from_index(L, 2);
3665    }
3666    return 0;
3667}
3668
3669/* node.direct.getdata */
3670/* node.direct.setdata */
3671
3672/*tex
3673
3674    These getter and setter get |data| as well as |value| fields. One can make them equivalent to
3675    |getvalue| and |setvalue| if needed.
3676
3677*/
3678
3679# define getdata_usage (glyph_usage | rule_usage | glue_usage | boundary_usage | attribute_usage | mark_usage)
3680# define setdata_usage getdata_usage
3681
3682static int nodelib_direct_getdata(lua_State *L)
3683{
3684    halfword n = nodelib_valid_direct_from_index(L, 1);
3685    if (n) {
3686        switch (node_type(n)) {
3687            case glyph_node:
3688                lua_pushinteger(L, glyph_data(n));
3689                return 1;
3690            case rule_node:
3691                lua_pushinteger(L, rule_data(n));
3692                return 1;
3693            case glue_node:
3694                lua_pushinteger(L, glue_data(n));
3695                return 1;
3696            case boundary_node:
3697                lua_pushinteger(L, boundary_data(n));
3698                lua_pushinteger(L, boundary_reserved(n));
3699                return 2;
3700            case insert_node:
3701                lua_pushinteger(L, insert_identifier(n));
3702             // lua_pushinteger(L, insert_reserved(n));
3703                return 1;
3704            case attribute_node:
3705                switch (node_subtype(n)) {
3706                    case attribute_list_subtype:
3707                        nodelib_push_attribute_data(L, n);
3708                        return 1;
3709                    case attribute_value_subtype:
3710                        /*tex Only used for introspection so it's okay to return 2 values. */
3711                        lua_pushinteger(L, attribute_index(n));
3712                        lua_pushinteger(L, attribute_value(n));
3713                        return 2;
3714                    default:
3715                        /*tex We just ignore. */
3716                        break;
3717                }
3718                break;
3719            case mark_node:
3720                if (lua_toboolean(L, 2)) {
3721                    lmt_token_list_to_luastring(L, mark_ptr(n), 0, 0, 0);
3722                } else {
3723                    lmt_token_list_to_lua(L, mark_ptr(n));
3724                }
3725                return 1;
3726        }
3727    }
3728    lua_pushnil(L);
3729    return 1;
3730}
3731
3732static int nodelib_direct_setdata(lua_State *L) /* data and value */
3733{
3734    halfword n = nodelib_valid_direct_from_index(L, 1);
3735    if (n) {
3736        switch (node_type(n)) {
3737            case glyph_node:
3738                glyph_data(n) = lmt_tohalfword(L, 2);
3739                break;
3740            case rule_node:
3741                rule_data(n) = lmt_tohalfword(L, 2);
3742                break;
3743            case glue_node:
3744                glue_data(n) = lmt_tohalfword(L, 2);
3745                break;
3746            case boundary_node:
3747                boundary_data(n) = lmt_tohalfword(L, 2);
3748                boundary_reserved(n) = lmt_opthalfword(L, 3, 0);
3749                break;
3750            case insert_node:
3751                insert_identifier(n) = lmt_tohalfword(L, 2);
3752             // insert_reserved(n) = lmt_opthalfword(L, 3, 0);
3753                break;
3754            case attribute_node:
3755                /*tex Not supported for now! */
3756                break;
3757            case mark_node:
3758                tex_delete_token_reference(mark_ptr(n));
3759                mark_ptr(n) = lmt_token_list_from_lua(L, 2); /* check ref */
3760                break;
3761        }
3762    }
3763    return 0;
3764}
3765
3766/* node.direct.get[left|right|top|bottom|]delimiter */
3767/* node.direct.set[left|right|top|bottom|]delimiter */
3768
3769# define getdelimiter_usage       (fraction_usage | fence_usage | radical_usage | accent_usage)
3770# define setdelimiter_usage       getdelimiter_usage
3771
3772# define getleftdelimiter_usage   (fraction_usage | radical_usage)
3773# define setleftdelimiter_usage   getleftdelimiter_usage
3774
3775# define getrightdelimiter_usage  getleftdelimiter_usage
3776# define setrightdelimiter_usage  setleftdelimiter_usage
3777
3778# define gettopdelimiter_usage    (fence_usage | radical_usage)
3779# define settopdelimiter_usage    gettopdelimiter_usage
3780
3781# define getbottomdelimiter_usage gettopdelimiter_usage
3782# define setbottomdelimiter_usage settopdelimiter_usage
3783
3784static int nodelib_direct_getleftdelimiter(lua_State *L)
3785{
3786    halfword n = nodelib_valid_direct_from_index(L, 1);
3787    if (n) {
3788        switch (node_type(n)) {
3789            case fraction_noad:
3790                nodelib_push_direct_or_nil(L, fraction_left_delimiter(n));
3791                return 1;
3792            case radical_noad:
3793                nodelib_push_direct_or_nil(L, radical_left_delimiter(n));
3794                return 1;
3795        }
3796    }
3797    lua_pushnil(L);
3798    return 1;
3799}
3800
3801static int nodelib_direct_getrightdelimiter(lua_State *L)
3802{
3803    halfword n = nodelib_valid_direct_from_index(L, 1);
3804    if (n) {
3805        switch (node_type(n)) {
3806            case fraction_noad:
3807                nodelib_push_direct_or_nil(L, fraction_right_delimiter(n));
3808                return 1;
3809            case radical_noad:
3810                nodelib_push_direct_or_nil(L, radical_right_delimiter(n));
3811                return 1;
3812        }
3813    }
3814    lua_pushnil(L);
3815    return 1;
3816}
3817
3818static int nodelib_direct_gettopdelimiter(lua_State *L)
3819{
3820    halfword n = nodelib_valid_direct_from_index(L, 1);
3821    if (n) {
3822        switch (node_type(n)) {
3823            case radical_noad:
3824                nodelib_push_direct_or_nil(L, radical_top_delimiter(n));
3825                return 1;
3826            case fence_noad:
3827                nodelib_push_direct_or_nil(L, fence_delimiter_top(n));
3828                return 1;
3829        }
3830    }
3831    lua_pushnil(L);
3832    return 1;
3833}
3834
3835static int nodelib_direct_getbottomdelimiter(lua_State *L)
3836{
3837    halfword n = nodelib_valid_direct_from_index(L, 1);
3838    if (n) {
3839        switch (node_type(n)) {
3840            case radical_noad:
3841                nodelib_push_direct_or_nil(L, radical_bottom_delimiter(n));
3842                return 1;
3843            case fence_noad:
3844                nodelib_push_direct_or_nil(L, fence_delimiter_bottom(n));
3845                return 1;
3846        }
3847    }
3848    lua_pushnil(L);
3849    return 1;
3850}
3851
3852static int nodelib_direct_getdelimiter(lua_State *L)
3853{
3854    halfword n = nodelib_valid_direct_from_index(L, 1);
3855    if (n) {
3856        switch (node_type(n)) {
3857            case fraction_noad:
3858                nodelib_push_direct_or_nil(L, fraction_middle_delimiter(n));
3859                return 1;
3860            case fence_noad:
3861                nodelib_push_direct_or_node(L, n, fence_delimiter(n));
3862                return 1;
3863            case radical_noad:
3864                nodelib_push_direct_or_node(L, n, radical_left_delimiter(n));
3865                return 1;
3866            case accent_noad:
3867                nodelib_push_direct_or_node(L, n, accent_middle_character(n)); /* not really a delimiter */
3868                return 1;
3869        }
3870    }
3871    lua_pushnil(L);
3872    return 1;
3873}
3874
3875static int nodelib_direct_setleftdelimiter(lua_State *L)
3876{
3877    halfword n = nodelib_valid_direct_from_index(L, 1);
3878    if (n) {
3879        switch (node_type(n)) {
3880            case fraction_noad:
3881                fraction_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3882                break;
3883            case radical_noad:
3884                radical_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3885                break;
3886        }
3887    }
3888    return 0;
3889}
3890
3891static int nodelib_direct_setrightdelimiter(lua_State *L)
3892{
3893    halfword n = nodelib_valid_direct_from_index(L, 1);
3894    if (n) {
3895        switch (node_type(n)) {
3896            case fraction_noad:
3897                fraction_right_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3898                break;
3899            case radical_noad:
3900                radical_right_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3901                break;
3902        }
3903    }
3904    return 0;
3905}
3906
3907static int nodelib_direct_settopdelimiter(lua_State *L)
3908{
3909    halfword n = nodelib_valid_direct_from_index(L, 1);
3910    if (n) {
3911        switch (node_type(n)) {
3912            case radical_noad:
3913                radical_top_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3914                return 1;
3915            case fence_noad:
3916                fence_delimiter_top(n) = nodelib_valid_direct_from_index(L, 2);
3917                return 1;
3918        }
3919    }
3920    return 0;
3921}
3922
3923static int nodelib_direct_setbottomdelimiter(lua_State *L)
3924{
3925    halfword n = nodelib_valid_direct_from_index(L, 1);
3926    if (n) {
3927        switch (node_type(n)) {
3928            case radical_noad:
3929                radical_bottom_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3930                return 1;
3931            case fence_noad:
3932                fence_delimiter_bottom(n) = nodelib_valid_direct_from_index(L, 2);
3933                return 1;
3934        }
3935    }
3936    return 0;
3937}
3938
3939static int nodelib_direct_setdelimiter(lua_State *L)
3940{
3941    halfword n = nodelib_valid_direct_from_index(L, 1);
3942    if (n) {
3943        switch (node_type(n)) {
3944            case fraction_noad:
3945                fraction_middle_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3946                break;
3947            case fence_noad:
3948                fence_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3949                break;
3950            case radical_noad:
3951                radical_left_delimiter(n) = nodelib_valid_direct_from_index(L, 2);
3952                break;
3953            case accent_noad:
3954                accent_middle_character(n) = nodelib_valid_direct_from_index(L, 2); /* not really a delimiter */
3955                break;
3956        }
3957    }
3958    return 0;
3959}
3960
3961/* node.direct.get[top|bottom] */
3962/* node.direct.set[top|bottom] */
3963
3964# define gettop_usage    (accent_usage | fence_usage)
3965# define settop_usage    gettop_usage
3966# define getbottom_usage gettop_usage
3967# define setbottom_usage getbottom_usage
3968
3969static int nodelib_direct_gettop(lua_State *L)
3970{
3971    halfword n = nodelib_valid_direct_from_index(L, 1);
3972    if (n) {
3973        switch (node_type(n)) {
3974            case accent_noad:
3975                nodelib_push_direct_or_nil(L, accent_top_character(n));
3976                return 1;
3977            case fence_noad:
3978                nodelib_push_direct_or_nil(L, fence_delimiter_top(n));
3979                return 1;
3980        }
3981    }
3982    lua_pushnil(L);
3983    return 1;
3984}
3985
3986static int nodelib_direct_getbottom(lua_State *L)
3987{
3988    halfword n = nodelib_valid_direct_from_index(L, 1);
3989    if (n) {
3990        switch (node_type(n)) {
3991            case accent_noad:
3992                nodelib_push_direct_or_nil(L, accent_bottom_character(n));
3993                return 1;
3994            case fence_noad:
3995                nodelib_push_direct_or_nil(L, fence_delimiter_bottom(n));
3996                return 1;
3997        }
3998    }
3999    lua_pushnil(L);
4000    return 1;
4001}
4002
4003static int nodelib_direct_settop(lua_State *L)
4004{
4005    halfword n = nodelib_valid_direct_from_index(L, 1);
4006    if (n) {
4007        switch (node_type(n)) {
4008            case accent_noad:
4009                accent_top_character(n) = nodelib_valid_direct_from_index(L, 2);
4010                return 0;
4011            case fence_noad:
4012                fence_delimiter_top(n) = nodelib_valid_direct_from_index(L, 2);
4013                return 0;
4014        }
4015    }
4016    lua_pushnil(L);
4017    return 1;
4018}
4019
4020static int nodelib_direct_setbottom(lua_State *L)
4021{
4022    halfword n = nodelib_valid_direct_from_index(L, 1);
4023    if (n) {
4024        switch (node_type(n)) {
4025            case accent_noad:
4026                accent_bottom_character(n) = nodelib_valid_direct_from_index(L, 2);
4027                return 0;
4028            case fence_noad:
4029                fence_delimiter_bottom(n) = nodelib_valid_direct_from_index(L, 2);
4030                return 0;
4031        }
4032    }
4033    lua_pushnil(L);
4034    return 1;
4035}
4036
4037/* node.direct.get[numerator|denominator] */
4038/* node.direct.set[numerator|denominator] */
4039
4040# define getdenominator_usage fraction_usage
4041# define setdenominator_usage fraction_usage
4042# define getnumerator_usage   fraction_usage
4043# define setnumerator_usage   fraction_usage
4044
4045static int nodelib_direct_getnumerator(lua_State *L)
4046{
4047    halfword n = nodelib_valid_direct_from_index(L, 1);
4048    if (n) {
4049        switch (node_type(n)) {
4050            case fraction_noad:
4051                nodelib_push_direct_or_nil(L, fraction_numerator(n));
4052                return 1;
4053        }
4054    }
4055    lua_pushnil(L);
4056    return 1;
4057}
4058
4059static int nodelib_direct_getdenominator(lua_State *L)
4060{
4061    halfword n = nodelib_valid_direct_from_index(L, 1);
4062    if (n) {
4063        switch (node_type(n)) {
4064            case fraction_noad:
4065                nodelib_push_direct_or_nil(L, fraction_denominator(n));
4066                return 1;
4067        }
4068    }
4069    lua_pushnil(L);
4070    return 1;
4071}
4072
4073static int nodelib_direct_setnumerator(lua_State *L)
4074{
4075    halfword n = nodelib_valid_direct_from_index(L, 1);
4076    if (n) {
4077        switch (node_type(n)) {
4078            case fraction_noad:
4079                fraction_numerator(n) = nodelib_valid_direct_from_index(L, 2);
4080                break;
4081        }
4082    }
4083    return 0;
4084}
4085
4086static int nodelib_direct_setdenominator(lua_State *L)
4087{
4088    halfword n = nodelib_valid_direct_from_index(L, 1);
4089    if (n) {
4090        switch (node_type(n)) {
4091            case fraction_noad:
4092                fraction_denominator(n) = nodelib_valid_direct_from_index(L, 2);
4093                break;
4094        }
4095    }
4096    return 0;
4097}
4098
4099/* node.direct.getdegree */
4100/* node.direct.setdegree */
4101
4102# define getdegree_usage radical_usage
4103# define setdegree_usage getdegree_usage
4104
4105static int nodelib_direct_getdegree(lua_State *L)
4106{
4107    halfword n = nodelib_valid_direct_from_index(L, 1);
4108    if (n) {
4109        switch (node_type(n)) {
4110            case radical_noad:
4111                nodelib_push_direct_or_nil(L, radical_degree(n));
4112                return 1;
4113        }
4114    }
4115    lua_pushnil(L);
4116    return 1;
4117}
4118
4119static int nodelib_direct_setdegree(lua_State *L)
4120{
4121    halfword n = nodelib_valid_direct_from_index(L, 1);
4122    if (n) {
4123        switch (node_type(n)) {
4124            case radical_noad:
4125                radical_degree(n) = nodelib_valid_direct_from_index(L, 2);
4126                break;
4127        }
4128    }
4129    return 0;
4130}
4131
4132/* node.direct.getchoice */
4133/* node.direct.setchoice */
4134
4135# define getchoice_usage choice_usage
4136# define setchoice_usage getchoice_usage
4137
4138static int nodelib_direct_getchoice(lua_State *L)
4139{
4140    halfword n = nodelib_valid_direct_from_index(L, 1);
4141    halfword c = null;
4142    if (n && node_type(n) == choice_node) {
4143        switch (lmt_tointeger(L, 2)) {
4144            case 1: c =
4145                choice_display_mlist(n);
4146                break;
4147            case 2: c =
4148                choice_text_mlist(n);
4149                break;
4150            case 3:
4151                c = choice_script_mlist(n);
4152                break;
4153            case 4:
4154                c = choice_script_script_mlist(n);
4155                break;
4156        }
4157    }
4158    nodelib_push_direct_or_nil(L, c);
4159    return 1;
4160}
4161
4162static int nodelib_direct_setchoice(lua_State *L)
4163{
4164    halfword n = nodelib_valid_direct_from_index(L, 1);
4165    if (n && node_type(n) == choice_node) {
4166        halfword c = nodelib_valid_direct_from_index(L, 2);
4167        switch (lmt_tointeger(L, 2)) {
4168            case 1:
4169                choice_display_mlist(n) = c;
4170                break;
4171            case 2:
4172                choice_text_mlist(n) = c;
4173                break;
4174            case 3:
4175                choice_script_mlist(n) = c;
4176                break;
4177            case 4:
4178                choice_script_script_mlist(n) = c;
4179                break;
4180        }
4181    }
4182    return 0;
4183}
4184
4185/* node.direct.getglyphdata */
4186/* node.direct.setglyphdata */
4187
4188# define getglyphdata_usage glyph_usage
4189# define setglyphdata_usage getglyphdata_usage
4190
4191static int nodelib_direct_getglyphdata(lua_State *L)
4192{
4193    halfword n = nodelib_valid_direct_from_index(L, 1);
4194    if (n && (node_type(n) == glyph_node) && (glyph_data(n) != unused_attribute_value)) {
4195        lua_pushinteger(L, glyph_data(n));
4196    } else {
4197        lua_pushnil(L);
4198    }
4199    return 1;
4200}
4201
4202static int nodelib_direct_setglyphdata(lua_State *L)
4203{
4204    halfword n = nodelib_valid_direct_from_index(L, 1);
4205    if (n && node_type(n) == glyph_node) {
4206        glyph_data(n) = (halfword) luaL_optinteger(L, 2, unused_attribute_value);
4207    }
4208    return 0;
4209}
4210
4211/* node.direct.getnext */
4212/* node.direct.setnext */
4213
4214# define getnext_usage common_usage
4215# define setnext_usage common_usage
4216
4217static int nodelib_direct_getnext(lua_State *L)
4218{
4219    halfword n = nodelib_valid_direct_from_index(L, 1);
4220    if (n) {
4221        nodelib_push_direct_or_nil(L, node_next(n));
4222    } else {
4223        lua_pushnil(L);
4224    }
4225    return 1;
4226}
4227
4228static int nodelib_direct_setnext(lua_State *L)
4229{
4230    halfword n = nodelib_valid_direct_from_index(L, 1);
4231    if (n) {
4232       node_next(n) = nodelib_valid_direct_from_index(L, 2);
4233    }
4234    return 0;
4235}
4236
4237/* node.direct.getprev */
4238/* node.direct.setprev */
4239
4240# define getprev_usage common_usage
4241# define setprev_usage common_usage
4242
4243static int nodelib_direct_getprev(lua_State *L)
4244{
4245    halfword n = nodelib_valid_direct_from_index(L, 1);
4246    if (n) {
4247        nodelib_push_direct_or_nil(L, node_prev(n));
4248    } else {
4249        lua_pushnil(L);
4250    }
4251    return 1;
4252}
4253
4254static int nodelib_direct_setprev(lua_State *L)
4255{
4256    halfword n = nodelib_valid_direct_from_index(L, 1);
4257    if (n) {
4258        node_prev(n) = nodelib_valid_direct_from_index(L, 2);
4259    }
4260    return 0;
4261}
4262
4263/* node.direct.getboth */
4264/* node.direct.setboth */
4265
4266# define getboth_usage common_usage
4267# define setboth_usage common_usage
4268
4269static int nodelib_direct_getboth(lua_State *L)
4270{
4271    halfword n = nodelib_valid_direct_from_index(L, 1);
4272    if (n) {
4273        nodelib_push_direct_or_nil(L, node_prev(n));
4274        nodelib_push_direct_or_nil(L, node_next(n));
4275    } else {
4276        lua_pushnil(L);
4277        lua_pushnil(L);
4278    }
4279    return 2;
4280}
4281
4282static int nodelib_direct_setboth(lua_State *L)
4283{
4284    halfword n = nodelib_valid_direct_from_index(L, 1);
4285    if (n) {
4286        node_prev(n) = nodelib_valid_direct_from_index(L, 2);
4287        node_next(n) = nodelib_valid_direct_from_index(L, 3);
4288    }
4289    return 0;
4290}
4291
4292/* node.direct.isnext */
4293/* node.direct.isprev */
4294/* node.direct.isboth */
4295
4296# define isnext_usage common_usage
4297# define isprev_usage common_usage
4298# define isboth_usage common_usage
4299
4300static int nodelib_direct_isnext(lua_State *L)
4301{
4302    halfword n = nodelib_valid_direct_from_index(L, 1);
4303    if (n && node_type(n) == lmt_tohalfword(L, 2)) {
4304        nodelib_push_direct_or_nil(L, node_next(n));
4305    } else {
4306        lua_pushnil(L);
4307    }
4308    return 1;
4309}
4310
4311static int nodelib_direct_isprev(lua_State *L)
4312{
4313    halfword n = nodelib_valid_direct_from_index(L, 1);
4314    if (n && node_type(n) == lmt_tohalfword(L, 2)) {
4315        nodelib_push_direct_or_nil(L, node_prev(n));
4316    } else {
4317        lua_pushnil(L);
4318    }
4319    return 1;
4320}
4321
4322static int nodelib_direct_isboth(lua_State *L)
4323{
4324    halfword n = nodelib_valid_direct_from_index(L, 1);
4325    if (n) {
4326        halfword typ = lmt_tohalfword(L, 2);
4327        halfword prv = node_prev(n);
4328        halfword nxt = node_next(n);
4329        nodelib_push_direct_or_nil(L, prv && node_type(prv) == typ ? prv : null);
4330        nodelib_push_direct_or_nil(L, nxt && node_type(nxt) == typ ? nxt : null);
4331    } else {
4332        lua_pushnil(L);
4333        lua_pushnil(L);
4334    }
4335    return 2;
4336}
4337
4338/* node.direct.setlink  */
4339
4340/*
4341    a b b nil c d         : prev-a-b-c-d-next
4342    nil a b b nil c d nil : nil-a-b-c-d-nil
4343*/
4344
4345# define setlink_usage common_usage
4346
4347static int nodelib_direct_setlink(lua_State *L)
4348{
4349    int n = lua_gettop(L);
4350    halfword h = null; /* head node */
4351    halfword t = null; /* tail node */
4352    for (int i = 1; i <= n; i++) {
4353        /*
4354            We don't go for the tail of the current node because we can inject between existing nodes
4355            and the nodes themselves can have old values for prev and next, so ... only single nodes
4356            are looked at!
4357        */
4358        switch (lua_type(L, i)) {
4359            case LUA_TNUMBER:
4360                {
4361                    halfword c = nodelib_valid_direct_from_index(L, i); /* current node */
4362                    if (c) {
4363                        if (c != t) {
4364                            if (t) {
4365                                node_next(t) = c;
4366                                node_prev(c) = t;
4367                            } else if (i > 1) {
4368                                /* we assume that the first node is a kind of head */
4369                                node_prev(c) = null;
4370                            }
4371                            t = c;
4372                            if (! h) {
4373                                h = t;
4374                            }
4375                        } else {
4376                            /* we ignore duplicate nodes which can be tails or the previous */
4377                        }
4378                    } else {
4379                        /* we ignore bad nodes, but we could issue a message */
4380                    }
4381                }
4382                break;
4383            case LUA_TBOOLEAN:
4384                if (lua_toboolean(L, i)) {
4385                    /*tex Just skip this one. */
4386                    break;
4387                } else {
4388                    /* fall through */
4389                }
4390            default:
4391                if (t) {
4392                    /* safeguard: a nil in the list can be meant as end so we nil the next of tail */
4393                    node_next(t) = null;
4394                } else {
4395                    /* we just ignore nil nodes and have no tail yet */
4396                }
4397        }
4398    }
4399    nodelib_push_direct_or_nil(L, h);
4400    return 1;
4401}
4402
4403/* node.direct.setsplit */
4404
4405# define setsplit_usage common_usage
4406
4407static int nodelib_direct_setsplit(lua_State *L)
4408{
4409    halfword l = nodelib_valid_direct_from_index(L, 1);
4410    halfword r = nodelib_valid_direct_from_index(L, 2); /* maybe default to next */
4411    if (l && r) {
4412        if (l != r) {
4413            node_prev(node_next(l)) = null;
4414            node_next(node_prev(r)) = null;
4415        }
4416        node_next(l) = null;
4417        node_prev(r) = null;
4418    }
4419    return 0;
4420}
4421
4422/*tex Local_par nodes can have frozen properties. */
4423
4424/* node.direct.validpar */
4425/* node.direct.patchparshape */
4426/* node.direct.getparstate */
4427
4428# define validpar_usage      par_usage
4429# define patchparshape_usage par_usage
4430# define getparstate_usage   par_usage
4431
4432static halfword nodelib_direct_aux_validpar(lua_State *L, int index)
4433{
4434    halfword p = nodelib_valid_direct_from_index(L, index);
4435    if (! p) {
4436        p = tex_find_par_par(cur_list.head);
4437    } else if (node_type(p) != par_node) {
4438        while (node_prev(p)) {
4439            p = node_prev(p);
4440        }
4441    }
4442    return (p && node_type(p) == par_node) ? p : null;
4443}
4444
4445static int nodelib_direct_patchparshape(lua_State *L) // maybe also patchparstate
4446{
4447    halfword par = nodelib_direct_aux_validpar(L, 1);
4448    if (par) {
4449        halfword shape = par_par_shape(par);
4450        halfword options = shape ? specification_options(shape) : 0;
4451        if (shape) {
4452            tex_flush_node(shape);
4453            par_par_shape(par) = null;
4454        }
4455        if (lua_type(L, 2) == LUA_TTABLE) {
4456            halfword size = (halfword) lua_rawlen(L, 2);
4457            shape = tex_new_specification_node(size, par_shape_code, options);
4458            par_par_shape(par) = shape;
4459            for (int i = 1; i <= size; i++) {
4460                if (lua_rawgeti(L, 2, i) == LUA_TTABLE) {
4461                    if (lua_rawgeti(L, -1, 1) == LUA_TNUMBER) {
4462                        scaled indent = lmt_roundnumber(L, -1);
4463                        if (lua_rawgeti(L, -2, 2) == LUA_TNUMBER) {
4464                            scaled width = lmt_roundnumber(L, -1);
4465                            tex_set_specification_indent(shape, i, indent);
4466                            tex_set_specification_width(shape, i, width);
4467                        }
4468                        lua_pop(L, 1);
4469                    }
4470                    lua_pop(L, 1);
4471                }
4472                lua_pop(L, 1);
4473            }
4474        }
4475    }
4476    return 0;
4477}
4478
4479static int nodelib_direct_getparstate(lua_State *L)
4480{
4481    halfword p = nodelib_direct_aux_validpar(L, 1);
4482    if (p) {
4483        int limited = lua_toboolean(L, 2);
4484        lua_createtable(L, 0, 24);
4485        switch (node_subtype(p)) {
4486            case vmode_par_par_subtype:
4487            case hmode_par_par_subtype:
4488                {
4489                    /* todo: optional: all skip components */
4490                    lua_push_integer_at_key(L, hsize,                              tex_get_par_par(p, par_hsize_code));
4491                    lua_push_integer_at_key(L, leftskip,               glue_amount(tex_get_par_par(p, par_left_skip_code)));
4492                    lua_push_integer_at_key(L, rightskip,              glue_amount(tex_get_par_par(p, par_right_skip_code)));
4493                    lua_push_integer_at_key(L, hangindent,                         tex_get_par_par(p, par_hang_indent_code));
4494                    lua_push_integer_at_key(L, hangafter,                          tex_get_par_par(p, par_hang_after_code));
4495                    lua_push_integer_at_key(L, parindent,                          tex_get_par_par(p, par_par_indent_code));
4496                    lua_push_integer_at_key(L, prevgraf,                           par_prev_graf(p));
4497                    if (! limited) {
4498                        lua_push_integer_at_key(L, parfillleftskip,    glue_amount(tex_get_par_par(p, par_par_fill_left_skip_code)));
4499                        lua_push_integer_at_key(L, parfillskip,        glue_amount(tex_get_par_par(p, par_par_fill_right_skip_code)));
4500                        lua_push_integer_at_key(L, parinitleftskip,    glue_amount(tex_get_par_par(p, par_par_init_left_skip_code)));
4501                        lua_push_integer_at_key(L, parinitrightskip,   glue_amount(tex_get_par_par(p, par_par_init_right_skip_code)));
4502                        lua_push_integer_at_key(L, emergencyleftskip,  glue_amount(tex_get_par_par(p, emergency_left_skip_code)));
4503                        lua_push_integer_at_key(L, emergencyrightskip, glue_amount(tex_get_par_par(p, emergency_right_skip_code)));
4504                        lua_push_integer_at_key(L, adjustspacing,                  tex_get_par_par(p, par_adjust_spacing_code));
4505                        lua_push_integer_at_key(L, protrudechars,                  tex_get_par_par(p, par_protrude_chars_code));
4506                        lua_push_integer_at_key(L, pretolerance,                   tex_get_par_par(p, par_pre_tolerance_code));
4507                        lua_push_integer_at_key(L, tolerance,                      tex_get_par_par(p, par_tolerance_code));
4508                        lua_push_integer_at_key(L, emergencystretch,               tex_get_par_par(p, par_emergency_stretch_code));
4509                        lua_push_integer_at_key(L, looseness,                      tex_get_par_par(p, par_looseness_code));
4510                        lua_push_integer_at_key(L, lastlinefit,                    tex_get_par_par(p, par_last_line_fit_code));
4511                        lua_push_integer_at_key(L, linepenalty,                    tex_get_par_par(p, par_line_penalty_code));
4512                        lua_push_integer_at_key(L, interlinepenalty,               tex_get_par_par(p, par_inter_line_penalty_code));
4513                        lua_push_integer_at_key(L, clubpenalty,                    tex_get_par_par(p, par_club_penalty_code));
4514                        lua_push_integer_at_key(L, widowpenalty,                   tex_get_par_par(p, par_widow_penalty_code));
4515                        lua_push_integer_at_key(L, displaywidowpenalty,            tex_get_par_par(p, par_display_widow_penalty_code));
4516                        lua_push_integer_at_key(L, lefttwindemerits,               tex_get_par_par(p, par_left_twin_demerits_code));
4517                        lua_push_integer_at_key(L, righttwindemerits,              tex_get_par_par(p, par_right_twin_demerits_code));
4518                        lua_push_integer_at_key(L, singlelinepenalty,              tex_get_par_par(p, par_single_line_penalty_code));
4519                        lua_push_integer_at_key(L, hyphenpenalty,                  tex_get_par_par(p, par_hyphen_penalty_code));
4520                        lua_push_integer_at_key(L, exhyphenpenalty,                tex_get_par_par(p, par_ex_hyphen_penalty_code));
4521                        lua_push_integer_at_key(L, brokenpenalty,                  tex_get_par_par(p, par_broken_penalty_code));
4522                        lua_push_integer_at_key(L, adjdemerits,                    tex_get_par_par(p, par_adj_demerits_code));
4523                        lua_push_integer_at_key(L, doublehyphendemerits,           tex_get_par_par(p, par_double_hyphen_demerits_code));
4524                        lua_push_integer_at_key(L, finalhyphendemerits,            tex_get_par_par(p, par_final_hyphen_demerits_code));
4525                        lua_push_integer_at_key(L, baselineskip,       glue_amount(tex_get_par_par(p, par_baseline_skip_code)));
4526                        lua_push_integer_at_key(L, lineskip,           glue_amount(tex_get_par_par(p, par_line_skip_code)));
4527                        lua_push_integer_at_key(L, lineskiplimit,                  tex_get_par_par(p, par_line_skip_limit_code));
4528                        lua_push_integer_at_key(L, shapingpenaltiesmode,           tex_get_par_par(p, par_shaping_penalties_mode_code));
4529                        lua_push_integer_at_key(L, shapingpenalty,                 tex_get_par_par(p, par_shaping_penalty_code));
4530                        lua_push_integer_at_key(L, emergencyextrastretch,          tex_get_par_par(p, par_emergency_extra_stretch_code));
4531                    }
4532                    lua_push_specification_at_key(L, parshape,                     tex_get_par_par(p, par_par_shape_code));
4533                    if (! limited) {
4534                        lua_push_specification_at_key(L, interlinepenalties,       tex_get_par_par(p, par_inter_line_penalties_code));
4535                        lua_push_specification_at_key(L, clubpenalties,            tex_get_par_par(p, par_club_penalties_code));
4536                        lua_push_specification_at_key(L, widowpenalties,           tex_get_par_par(p, par_widow_penalties_code));
4537                        lua_push_specification_at_key(L, displaywidowpenalties,    tex_get_par_par(p, par_display_widow_penalties_code));
4538                        lua_push_specification_at_key(L, orphanpenalties,          tex_get_par_par(p, par_orphan_penalties_code));
4539                        lua_push_specification_at_key(L, toddlerpenalties,         tex_get_par_par(p, par_toddler_penalties_code));
4540                        lua_push_specification_at_key(L, parpasses,                tex_get_par_par(p, par_par_passes_code));
4541                        lua_push_specification_at_key(L, linebreakchecks,          tex_get_par_par(p, par_line_break_checks_code));
4542                        lua_push_specification_at_key(L, adjacentdemerits,         tex_get_par_par(p, par_adjacent_demerits_code));
4543                        lua_push_specification_at_key(L, fitnessclasses,           tex_get_par_par(p, par_fitness_classes_code));
4544                    }
4545                    break;
4546                }
4547         // case local_box_par_subtype:
4548         // case penalty_par_subtype:
4549         // case math_par_subtype:
4550        }
4551        return 1;
4552    } else {
4553        return 0;
4554    }
4555}
4556
4557/* node.type (converts id numbers to type names) */
4558
4559# define type_usage common_usage
4560
4561static int nodelib_hybrid_type(lua_State *L)
4562{
4563    if (lua_type(L, 1) == LUA_TNUMBER) {
4564        halfword i = lmt_tohalfword(L, 1);
4565        if (tex_nodetype_is_visible(i)) {
4566            lua_push_key_by_index(lmt_interface.node_data[i].lua);
4567            return 1;
4568        }
4569    } else if (lmt_maybe_isnode(L, 1)) {
4570        lua_push_key(node);
4571        return 1;
4572    }
4573    lua_pushnil(L);
4574    return 1;
4575}
4576
4577/* node.new (allocate a new node) */
4578
4579static halfword nodelib_new_node(lua_State *L)
4580{
4581    quarterword i = unknown_node;
4582    switch (lua_type(L, 1)) {
4583        case LUA_TNUMBER:
4584            i = lmt_toquarterword(L, 1);
4585         // if (! tex_nodetype_is_visible(i)) {
4586         //     i = unknown_node;
4587         // }
4588            break;
4589        case LUA_TSTRING:
4590            i = nodelib_aux_get_node_type_id_from_name(L, 1, lmt_interface.node_data, 0);
4591            break;
4592    }
4593    if (tex_nodetype_is_visible(i)) {
4594        quarterword j = unknown_subtype;
4595        switch (lua_type(L, 2)) {
4596            case LUA_TNUMBER:
4597                j = lmt_toquarterword(L, 2);
4598                break;
4599            case LUA_TSTRING:
4600                j = nodelib_aux_get_node_subtype_id_from_name(L, 2, lmt_interface.node_data[i].subtypes);
4601                break;
4602        }
4603        return tex_new_node(i, (j == unknown_subtype) ? 0 : j);
4604    } else {
4605        return luaL_error(L, "invalid node id for creating new node");
4606    }
4607}
4608
4609static int nodelib_userdata_new(lua_State *L)
4610{
4611    lmt_push_node_fast(L, nodelib_new_node(L));
4612    return 1;
4613}
4614
4615/* node.direct.new */
4616
4617# define new_usage common_usage
4618
4619static int nodelib_direct_new(lua_State *L)
4620{
4621    lua_pushinteger(L, nodelib_new_node(L));
4622    return 1;
4623}
4624
4625/* node.size (kind of private, needed for manuals) */
4626
4627# define size_usage common_usage
4628
4629static halfword nodelib_shared_size(lua_State *L)
4630{
4631    quarterword i = unknown_node;
4632    switch (lua_type(L, 1)) {
4633        case LUA_TNUMBER:
4634            i = lmt_toquarterword(L, 1);
4635            break;
4636        case LUA_TSTRING:
4637            i = nodelib_aux_get_node_type_id_from_name(L, 1, lmt_interface.node_data, 1);
4638            break;
4639    }
4640    if (tex_nodetype_is_valid(i)) {
4641        lua_pushinteger(L, lmt_interface.node_data[i].size * 8);
4642    } else {
4643        lua_pushnil(L);
4644    }
4645    return 1;
4646}
4647
4648static int nodelib_direct_newtextglyph(lua_State* L)
4649{
4650    halfword glyph = tex_new_text_glyph(lmt_tohalfword(L, 1), lmt_tohalfword(L, 2));
4651    nodelib_aux_setattributelist(L, glyph, 3);
4652    lua_pushinteger(L, glyph);
4653    return 1;
4654}
4655
4656# define newmathglyph_usage        common_usage
4657# define newtextglyph_usage        common_usage
4658# define newcontinuationatom_usage common_usage
4659
4660static int nodelib_direct_newmathglyph(lua_State* L)
4661{
4662    /*tex For now we don't set a properties, group and/or index here. */
4663    halfword glyph = tex_new_math_glyph(lmt_tohalfword(L, 1), lmt_tohalfword(L, 2));
4664    nodelib_aux_setattributelist(L, glyph, 3);
4665    lua_pushinteger(L, glyph);
4666    return 1;
4667}
4668
4669static int nodelib_direct_newcontinuationatom(lua_State* L)
4670{
4671    if (lua_type(L, 1) == LUA_TBOOLEAN) {
4672        halfword n = tex_new_math_continuation_atom(null, null);
4673        nodelib_aux_setattributelist(L, n, 2);
4674        lua_pushinteger(L, n);
4675        return 1;
4676    } else {
4677        halfword n = nodelib_valid_direct_from_index(L, 1);
4678        if (n) {
4679            n = tex_new_math_continuation_atom(n, null);
4680        }
4681        return 0;
4682    }
4683}
4684
4685/* node.free (this function returns the 'next' node, because that may be helpful) */
4686
4687static int nodelib_userdata_free(lua_State *L)
4688{
4689    if (lua_gettop(L) < 1) {
4690        lua_pushnil(L);
4691    } else if (! lua_isnil(L, 1)) {
4692        halfword n = lmt_check_isnode(L, 1);
4693        halfword p = node_next(n);
4694        tex_flush_node(n);
4695        lmt_push_node_fast(L, p);
4696    }
4697    return 1;
4698}
4699
4700/* node.direct.free */
4701
4702# define free_usage common_usage
4703
4704static int nodelib_direct_free(lua_State *L)
4705{
4706    halfword n = nodelib_valid_direct_from_index(L, 1);
4707    if (n) {
4708        halfword p = node_next(n);
4709        tex_flush_node(n);
4710        n = p;
4711    } else {
4712        n = null;
4713    }
4714    nodelib_push_direct_or_nil(L, n);
4715    return 1;
4716}
4717
4718/* node.flushnode (no next returned) */
4719
4720static int nodelib_userdata_flushnode(lua_State *L)
4721{
4722    if (! lua_isnil(L, 1)) {
4723        halfword n = lmt_check_isnode(L, 1);
4724        tex_flush_node(n);
4725    }
4726    return 0;
4727}
4728
4729/* node.direct.flushnode */
4730
4731# define flushnode_usage common_usage
4732
4733static int nodelib_direct_flushnode(lua_State *L)
4734{
4735    halfword n = nodelib_valid_direct_from_index(L, 1);
4736    if (n) {
4737        tex_flush_node(n);
4738    }
4739    return 0;
4740}
4741
4742/* node.flushlist */
4743
4744static int nodelib_userdata_flushlist(lua_State *L)
4745{
4746    if (! lua_isnil(L, 1)) {
4747        halfword n_ptr = lmt_check_isnode(L, 1);
4748        tex_flush_node_list(n_ptr);
4749    }
4750    return 0;
4751}
4752
4753/* node.direct.flushlist */
4754
4755# define flushlist_usage common_usage
4756
4757static int nodelib_direct_flushlist(lua_State *L)
4758{
4759    halfword n = nodelib_valid_direct_from_index(L, 1);
4760    if (n) {
4761        tex_flush_node_list(n);
4762    }
4763    return 0;
4764}
4765
4766/* node.remove */
4767
4768static int nodelib_userdata_remove(lua_State *L)
4769{
4770    if (lua_gettop(L) < 2) {
4771        return luaL_error(L, "Not enough arguments for node.remove()");
4772    } else {
4773        halfword head = lmt_check_isnode(L, 1);
4774        if (lua_isnil(L, 2)) {
4775            return 2;
4776        } else {
4777            halfword current = lmt_check_isnode(L, 2);
4778            halfword removed = current;
4779            int remove = lua_toboolean(L, 3);
4780            if (head == current) {
4781                if (node_prev(current)){
4782                    node_next(node_prev(current)) = node_next(current);
4783                }
4784                if (node_next(current)){
4785                    node_prev(node_next(current)) = node_prev(current);
4786                }
4787                head = node_next(current);
4788                current = node_next(current);
4789            } else {
4790                halfword t = node_prev(current);
4791                if (t) {
4792                    node_next(t) = node_next(current);
4793                    if (node_next(current)) {
4794                        node_prev(node_next(current)) = t;
4795                    }
4796                    current = node_next(current);
4797                } else {
4798                    return luaL_error(L, "Bad arguments to node.remove()");
4799                }
4800            }
4801            lmt_push_node_fast(L, head);
4802            lmt_push_node_fast(L, current);
4803            if (remove) {
4804                tex_flush_node(removed);
4805                return 2;
4806            } else {
4807                lmt_push_node_fast(L, removed);
4808                node_next(removed) = null;
4809                node_prev(removed) = null;
4810                return 3;
4811            }
4812        }
4813    }
4814}
4815
4816/* node.direct.remove */
4817
4818# define remove_usage common_usage
4819
4820static int nodelib_direct_remove(lua_State *L)
4821{
4822    halfword head = nodelib_valid_direct_from_index(L, 1);
4823    if (head) {
4824        halfword current = nodelib_valid_direct_from_index(L, 2);
4825        if (current) {
4826            halfword removed = current;
4827            int remove = lua_toboolean(L, 3);
4828            halfword prev = node_prev(current);
4829            if (head == current) {
4830                halfword next = node_next(current);
4831                if (prev){
4832                    node_next(prev) = next;
4833                }
4834                if (next){
4835                    node_prev(next) = prev;
4836                }
4837                head = node_next(current);
4838                current = head;
4839            } else {
4840                if (prev) {
4841                    halfword next = node_next(current);
4842                    node_next(prev) = next;
4843                    if (next) {
4844                        node_prev(next) = prev;
4845                    }
4846                    current = next;
4847                } else {
4848                 /* tex_formatted_warning("nodes","invalid arguments to node.remove"); */
4849                    return 2;
4850                }
4851            }
4852            nodelib_push_direct_or_nil(L, head);
4853            nodelib_push_direct_or_nil(L, current);
4854            if (remove) {
4855                tex_flush_node(removed);
4856                return 2;
4857            } else {
4858                nodelib_push_direct_or_nil(L, removed);
4859                node_next(removed) = null;
4860                node_prev(removed) = null;
4861                return 3;
4862            }
4863        } else {
4864            nodelib_push_direct_or_nil(L, head);
4865            lua_pushnil(L);
4866        }
4867    } else {
4868        lua_pushnil(L);
4869        lua_pushnil(L);
4870    }
4871    return 2;
4872}
4873
4874# define removefromlist_usage common_usage
4875
4876static int nodelib_direct_removefromlist(lua_State *L)
4877{
4878    halfword head = nodelib_valid_direct_from_index(L, 1);
4879    int count = 0;
4880    if (head) {
4881        halfword id = lmt_tohalfword(L, 2);
4882        halfword subtype = lmt_opthalfword(L, 3, -1);
4883        halfword current = head;
4884        while (current) {
4885            halfword next = node_next(current);
4886            if (node_type(current) == id && (subtype < 0 || node_subtype(current) == subtype)) {
4887                if (current == head) {
4888                    head = next;
4889                    node_prev(next) = null;
4890                } else {
4891                    tex_try_couple_nodes(node_prev(current), next);
4892                }
4893                tex_flush_node(current);
4894                ++count;
4895            }
4896            current = next;
4897        }
4898    }
4899    nodelib_push_direct_or_nil(L, head);
4900    lua_push_integer(L, count);
4901    return 2;
4902}
4903
4904/* node.insertbefore (insert a node in a list) */
4905
4906static int nodelib_userdata_insertbefore(lua_State *L)
4907{
4908    if (lua_gettop(L) < 3) {
4909        return luaL_error(L, "Not enough arguments for node.insertbefore()");
4910    } else if (lua_isnil(L, 3)) {
4911        lua_settop(L, 2);
4912    } else {
4913        halfword n = lmt_check_isnode(L, 3);
4914        if (lua_isnil(L, 1)) {
4915            node_next(n) = null;
4916            node_prev(n) = null;
4917            lmt_push_node_fast(L, n);
4918            lua_pushvalue(L, -1);
4919        } else {
4920            halfword current;
4921            halfword head = lmt_check_isnode(L, 1);
4922            if (lua_isnil(L, 2)) {
4923                current = tex_tail_of_node_list(head);
4924            } else {
4925                current = lmt_check_isnode(L, 2);
4926            }
4927            if (head != current) {
4928                halfword t = node_prev(current);
4929                if (t) {
4930                    tex_couple_nodes(t, n);
4931                } else {
4932                    return luaL_error(L, "Bad arguments to node.insertbefore()");
4933                }
4934            }
4935            tex_couple_nodes(n, current);
4936            lmt_push_node_fast(L, (head == current) ? n : head);
4937            lmt_push_node_fast(L, n);
4938        }
4939    }
4940    return 2;
4941}
4942
4943/* node.direct.insertbefore */
4944
4945# define insertbefore_usage common_usage
4946
4947static int nodelib_direct_insertbefore(lua_State *L)
4948{
4949    halfword n = nodelib_valid_direct_from_index(L, 3);
4950    if (n) {
4951        halfword head = nodelib_valid_direct_from_index(L, 1);
4952        halfword current = nodelib_valid_direct_from_index(L, 2);
4953        /* no head, ignore current */
4954        if (head) {
4955            if (! current) {
4956                current = tex_tail_of_node_list(head);
4957            }
4958            if (head != current) {
4959                halfword prev = node_prev(current);
4960                if (prev) {
4961                    tex_couple_nodes(prev, n);
4962                } else {
4963                    /* error so just quit and return originals */
4964                    return 2;
4965                }
4966            }
4967            tex_couple_nodes(n, current); /* nice but incompatible: tex_couple_nodes(tail_of_list(n),current) */
4968            lua_pushinteger(L, (head == current) ? n : head);
4969            lua_pushinteger(L, n);
4970        } else {
4971            node_next(n) = null;
4972            node_prev(n) = null;
4973            lua_pushinteger(L, n);
4974            lua_pushinteger(L, n);
4975            /* n, n */
4976        }
4977    } else {
4978        lua_settop(L, 2);
4979    }
4980    return 2;
4981}
4982
4983/* node.insertafter */
4984
4985static int nodelib_userdata_insertafter(lua_State *L)
4986{
4987    if (lua_gettop(L) < 3) {
4988        return luaL_error(L, "Not enough arguments for node.insertafter()");
4989    } else if (lua_isnil(L, 3)) {
4990        lua_settop(L, 2);
4991    } else {
4992        halfword n = lmt_check_isnode(L, 3);
4993        if (lua_isnil(L, 1)) {
4994            node_next(n) = null;
4995            node_prev(n) = null;
4996            lmt_push_node_fast(L, n);
4997            lua_pushvalue(L, -1);
4998        } else {
4999            halfword current;
5000            halfword head = lmt_check_isnode(L, 1);
5001            if (lua_isnil(L, 2)) {
5002                current = head;
5003                while (node_next(current)) {
5004                    current = node_next(current);
5005                }
5006            } else {
5007                current = lmt_check_isnode(L, 2);
5008            }
5009            tex_try_couple_nodes(n, node_next(current));
5010            tex_couple_nodes(current, n);
5011            lua_pop(L, 2);
5012            lmt_push_node_fast(L, n);
5013        }
5014    }
5015    return 2;
5016}
5017
5018/* node.direct.insertafter */
5019
5020# define insertafter_usage common_usage
5021
5022static int nodelib_direct_insertafter(lua_State *L)
5023{
5024    /*[head][current][new]*/
5025    halfword n = nodelib_valid_direct_from_index(L, 3);
5026    if (n) {
5027        halfword head = nodelib_valid_direct_from_index(L, 1);
5028        halfword current = nodelib_valid_direct_from_index(L, 2);
5029        if (head) {
5030            if (! current) {
5031                current = head;
5032                while (node_next(current)) {
5033                    current = node_next(current);
5034                }
5035            }
5036            tex_try_couple_nodes(n, node_next(current)); /* nice but incompatible: try_couple_nodes(tail_of_list(n), node_next(current)); */
5037            tex_couple_nodes(current, n);
5038            lua_pop(L, 2);
5039            lua_pushinteger(L, n);
5040        } else {
5041            /* no head, ignore current */
5042            node_next(n) = null;
5043            node_prev(n) = null;
5044            lua_pushinteger(L, n);
5045            lua_pushvalue(L, -1);
5046            /* n, n */
5047        }
5048    } else {
5049        lua_settop(L, 2);
5050    }
5051    return 2;
5052}
5053
5054/* */
5055
5056# define appendaftertail_usage   common_usage
5057# define prependbeforehead_usage common_usage
5058
5059static int nodelib_direct_appendaftertail(lua_State *L)
5060{
5061    /*[head][current][new]*/
5062    halfword h = nodelib_valid_direct_from_index(L, 1);
5063    halfword n = nodelib_valid_direct_from_index(L, 2);
5064    if (h && n) {
5065        tex_couple_nodes(tex_tail_of_node_list(h), n);
5066    }
5067    return 0;
5068}
5069
5070static int nodelib_direct_prependbeforehead(lua_State *L)
5071{
5072    /*[head][current][new]*/
5073    halfword h = nodelib_valid_direct_from_index(L, 1);
5074    halfword n = nodelib_valid_direct_from_index(L, 2);
5075    if (h && n) {
5076        tex_couple_nodes(n, tex_head_of_node_list(h));
5077    }
5078    return 0;
5079}
5080
5081/* node.copylist */
5082
5083/*tex
5084
5085    We need to use an intermediate variable as otherwise target is used in the loop and subfields
5086    get overwritten (or something like that) which results in crashes and unexpected side effects.
5087
5088*/
5089
5090static int nodelib_userdata_copylist(lua_State *L)
5091{
5092    if (lua_isnil(L, 1)) {
5093        return 1; /* the nil itself */
5094    } else {
5095        halfword m;
5096        halfword s = null;
5097        halfword n = lmt_check_isnode(L, 1);
5098        if ((lua_gettop(L) > 1) && (! lua_isnil(L, 2))) {
5099            s = lmt_check_isnode(L, 2);
5100        }
5101        m = tex_copy_node_list(n, s);
5102        lmt_push_node_fast(L, m);
5103        return 1;
5104    }
5105}
5106
5107/* node.direct.copylist */
5108
5109# define copylist_usage common_usage
5110
5111static int nodelib_direct_copylist(lua_State *L)
5112{
5113    halfword n = nodelib_valid_direct_from_index(L, 1);
5114    halfword s = nodelib_valid_direct_from_index(L, 2);
5115    if (n) {
5116        halfword m = tex_copy_node_list(n, s);
5117        lua_pushinteger(L, m);
5118    } else {
5119        lua_pushnil(L);
5120    }
5121    return 1;
5122}
5123
5124/* node.show (node, threshold, max) */
5125/* node.direct.show */
5126
5127static int nodelib_userdata_show(lua_State *L)
5128{
5129    halfword n = lmt_check_isnode(L, 1);
5130    if (n) {
5131        tex_show_node_list(n, lmt_optinteger(L, 2, show_box_depth_par), lmt_optinteger(L, 3, show_box_breadth_par));
5132    }
5133    return 0;
5134}
5135
5136# define show_usage common_usage
5137
5138static int nodelib_direct_show(lua_State *L)
5139{
5140    halfword n = nodelib_valid_direct_from_index(L, 1);
5141    if (n) {
5142        tex_show_node_list(n, lmt_optinteger(L, 2, show_box_depth_par), lmt_optinteger(L, 3, show_box_breadth_par));
5143    }
5144    return 0;
5145}
5146
5147/* node.serialize(node, details, threshold, max) */
5148/* node.direct.serialize */
5149
5150# define showlist_usage common_usage
5151
5152static int nodelib_aux_showlist(lua_State* L, halfword box)
5153{
5154    if (box) {
5155        luaL_Buffer buffer;
5156        int saved_selector = lmt_print_state.selector;
5157        halfword levels = tracing_levels_par;
5158        halfword online = tracing_online_par;
5159        halfword details = show_node_details_par;
5160        halfword depth = lmt_opthalfword(L, 3, show_box_depth_par);
5161        halfword breadth = lmt_opthalfword(L, 4, show_box_breadth_par);
5162        tracing_levels_par = 0;
5163        tracing_online_par = 0;
5164        show_node_details_par = lmt_opthalfword(L, 2, details);
5165        lmt_print_state.selector = luabuffer_selector_code;
5166        lmt_lua_state.used_buffer = &buffer;
5167        luaL_buffinit(L, &buffer);
5168        tex_show_node_list(box, depth, breadth);
5169        tex_print_ln();
5170        luaL_pushresult(&buffer);
5171        lmt_lua_state.used_buffer = NULL;
5172        lmt_print_state.selector = saved_selector;
5173        show_node_details_par = details;
5174        tracing_levels_par = levels;
5175        tracing_online_par = online;
5176    } else {
5177        lua_pushliteral(L, "");
5178    }
5179    return 1;
5180}
5181
5182static int nodelib_common_serialized(lua_State *L, halfword n)
5183{
5184    if (n) {
5185        switch (node_type(n)) {
5186            case hlist_node:
5187            case vlist_node:
5188                return nodelib_aux_showlist(L, n);
5189            default:
5190                {
5191                    halfword prv = null;
5192                    halfword nxt = null;
5193                    if (tex_nodetype_has_prev(n)) {
5194                        prv = node_prev(n);
5195                        node_prev(n) = null;
5196                    }
5197                    if (tex_nodetype_has_next(n)) {
5198                        nxt = node_next(n);
5199                        node_next(n) = null;
5200                    }
5201                    nodelib_aux_showlist(L, n);
5202                    if (prv) {
5203                        node_prev(n) = prv;
5204                    }
5205                    if (nxt) {
5206                        node_next(n) = nxt;
5207                    }
5208                    return 1;
5209                }
5210        }
5211    }
5212    lua_pushliteral(L, "");
5213    return 1;
5214}
5215
5216static int nodelib_userdata_serialized(lua_State *L)
5217{
5218    return nodelib_common_serialized(L, lmt_check_isnode(L, 1));
5219}
5220
5221# define serialized_usage common_usage
5222
5223static int nodelib_direct_serialized(lua_State *L)
5224{
5225    return nodelib_common_serialized(L, nodelib_valid_direct_from_index(L, 1));
5226}
5227
5228/* node.copy (deep copy) */
5229
5230static int nodelib_userdata_copy(lua_State *L)
5231{
5232    if (! lua_isnil(L, 1)) {
5233        halfword n = lmt_check_isnode(L, 1);
5234        n = tex_copy_node(n);
5235        lmt_push_node_fast(L, n);
5236    }
5237    return 1;
5238}
5239
5240/* node.direct.copy (deep copy) */
5241
5242# define copy_usage common_usage
5243
5244static int nodelib_direct_copy(lua_State *L)
5245{
5246    if (! lua_isnil(L, 1)) {
5247        /* beware, a glue node can have number 0 (zeropt) so we cannot test for null) */
5248        halfword n = nodelib_valid_direct_from_index(L, 1);
5249        if (n) {
5250            n = tex_copy_node(n);
5251            lua_pushinteger(L, n);
5252        } else {
5253            lua_pushnil(L);
5254        }
5255    }
5256    return 1;
5257}
5258
5259/* node.direct.copyonly (use with care) */
5260
5261# define copyonly_usage common_usage
5262
5263static int nodelib_direct_copyonly(lua_State *L)
5264{
5265    if (! lua_isnil(L, 1)) {
5266        halfword n = nodelib_valid_direct_from_index(L, 1);
5267        if (n) {
5268            n = tex_copy_node_only(n);
5269            lua_pushinteger(L, n);
5270        } else {
5271            lua_pushnil(L);
5272        }
5273    }
5274    return 1;
5275}
5276
5277/* node.write (output a node to tex's processor) */
5278/* node.append (idem but no attributes) */
5279
5280static int nodelib_userdata_write(lua_State *L)
5281{
5282    int j = lua_gettop(L);
5283    for (int i = 1; i <= j; i++) {
5284        halfword n = lmt_check_isnode(L, i);
5285        if (n) {
5286            halfword m = node_next(n);
5287            tex_tail_append(n);
5288            if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
5289                attach_current_attribute_list(n);
5290            }
5291            while (m) {
5292                tex_tail_append(m);
5293                if (tex_nodetype_has_attributes(node_type(m)) && ! node_attr(m)) {
5294                    attach_current_attribute_list(m);
5295                }
5296                m = node_next(m);
5297            }
5298        }
5299    }
5300    return 0;
5301}
5302
5303/*
5304static int nodelib_userdata_append(lua_State *L)
5305{
5306    int j = lua_gettop(L);
5307    for (int i = 1; i <= j; i++) {
5308        halfword n = lmt_check_isnode(L, i);
5309        if (n) {
5310            halfword m = node_next(n);
5311            tail_append(n);
5312            while (m) {
5313                tex_tail_append(m);
5314                m = node_next(m);
5315            }
5316        }
5317    }
5318    return 0;
5319}
5320*/
5321
5322/* node.direct.write (output a node to tex's processor) */
5323/* node.direct.append (idem no attributes) */
5324
5325# define write_usage common_usage
5326
5327static int nodelib_direct_write(lua_State *L)
5328{
5329    int j = lua_gettop(L);
5330    for (int i = 1; i <= j; i++) {
5331        halfword n = nodelib_valid_direct_from_index(L, i);
5332        if (n) {
5333            halfword m = node_next(n);
5334            tex_tail_append(n);
5335            if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
5336                attach_current_attribute_list(n);
5337            }
5338            while (m) {
5339                tex_tail_append(m);
5340                if (tex_nodetype_has_attributes(node_type(m)) && ! node_attr(m)) {
5341                    attach_current_attribute_list(m);
5342                }
5343                m = node_next(m);
5344            }
5345        }
5346    }
5347    return 0;
5348}
5349
5350/*
5351static int nodelib_direct_appendtocurrentlist(lua_State *L)
5352{
5353    int j = lua_gettop(L);
5354    for (int i = 1; i <= j; i++) {
5355        halfword n = nodelib_valid_direct_from_index(L, i);
5356        if (n) {
5357            halfword m = node_next(n);
5358            tex_tail_append(n);
5359            while (m) {
5360                tex_tail_append(m);
5361                m = node_next(m);
5362            }
5363        }
5364    }
5365    return 0;
5366}
5367*/
5368
5369/* node.direct.last */
5370
5371# define lastnode_usage common_usage
5372
5373static int nodelib_direct_lastnode(lua_State *L)
5374{
5375    halfword m = tex_pop_tail();
5376    lua_pushinteger(L, m);
5377    return 1;
5378}
5379
5380/* node.direct.hpack */
5381
5382static int nodelib_aux_packing(lua_State *L, int slot)
5383{
5384    switch (lua_type(L, slot)) {
5385        case LUA_TSTRING:
5386            {
5387                const char *s = lua_tostring(L, slot);
5388                if (lua_key_eq(s, exactly)) {
5389                    return packing_exactly;
5390                } else if (lua_key_eq(s, additional)) {
5391                    return packing_additional;
5392                } else if (lua_key_eq(s, expanded)) {
5393                    return packing_expanded;
5394                } else if (lua_key_eq(s, substitute)) {
5395                    return packing_substitute;
5396                } else if (lua_key_eq(s, adapted)) {
5397                    return packing_adapted;
5398                }
5399                break;
5400            }
5401        case LUA_TNUMBER:
5402            {
5403                int m = (int) lua_tointeger(L, slot);
5404                if (m >= packing_exactly && m <= packing_adapted) {
5405                    return m;
5406                }
5407                break;
5408            }
5409    }
5410    return packing_additional;
5411}
5412
5413# define hpack_usage common_usage
5414
5415static int nodelib_direct_hpack(lua_State *L)
5416{
5417    halfword p;
5418    int w = 0;
5419    int m = packing_additional;
5420    singleword d = direction_def_value;
5421    halfword n = nodelib_valid_direct_from_index(L, 1);
5422    if (n) {
5423        int top = lua_gettop(L);
5424        if (top > 1) {
5425            w = lmt_roundnumber(L, 2);
5426            if (top > 2) {
5427                m = nodelib_aux_packing(L, 3);
5428                if (top > 3) {
5429                    d = nodelib_getdirection(L, 4);
5430                }
5431            }
5432        }
5433    } else {
5434        n = null;
5435    }
5436    p = tex_hpack(n, w, m, d, holding_none_option, box_limit_none);
5437    lua_pushinteger(L, p);
5438    lua_pushinteger(L, lmt_packaging_state.last_badness);
5439    lua_pushinteger(L, lmt_packaging_state.last_overshoot);
5440    return 3;
5441}
5442
5443# define repack_usage common_usage
5444
5445static int nodelib_direct_repack(lua_State *L)
5446{
5447    halfword n = nodelib_valid_direct_from_index(L, 1);
5448    if (n) {
5449        switch (node_type(n)) {
5450            case hlist_node:
5451            case vlist_node:
5452                {
5453                    int top = lua_gettop(L);
5454                    int w = top > 1 ? lmt_roundnumber(L, 2) : 0;
5455                    int m = top > 2 ? nodelib_aux_packing(L, 3) : packing_additional;
5456                    tex_repack(n, w, m);
5457                    break;
5458                }
5459        }
5460    }
5461    return 0;
5462}
5463
5464# define freeze_usage common_usage
5465
5466static int nodelib_direct_freeze(lua_State *L)
5467{
5468    halfword n = nodelib_valid_direct_from_index(L, 1);
5469    if (n) {
5470        switch (node_type(n)) {
5471            case hlist_node:
5472            case vlist_node:
5473                 tex_freeze(n, lua_toboolean(L, 2), lua_toboolean(L, 3) ? node_type(n) : -1, lmt_opthalfword(L, 4, 0));
5474                 break;
5475        }
5476    }
5477    return 0;
5478}
5479
5480/* node.direct.verticalbreak */
5481/* node.direct.vpack */
5482
5483# define verticalbreak_usage common_usage
5484# define vpack_usage         common_usage
5485
5486static int nodelib_direct_verticalbreak(lua_State *L)
5487{
5488    halfword n = nodelib_valid_direct_from_index(L, 1);
5489    if (n) {
5490        scaled ht = lmt_roundnumber(L, 2);
5491        scaled dp = lmt_roundnumber(L, 3);
5492        n = tex_vert_break(n, ht, dp, 1, 0);
5493    }
5494    lua_pushinteger(L, n);
5495    return 1;
5496}
5497
5498static int nodelib_direct_vpack(lua_State *L)
5499{
5500    halfword p;
5501    int w = 0;
5502    int m = packing_additional;
5503    singleword d = direction_def_value;
5504    halfword n = nodelib_valid_direct_from_index(L, 1);
5505    if (n) {
5506        int top = lua_gettop(L);
5507        if (top > 1) {
5508            w = lmt_roundnumber(L, 2);
5509            if (top > 2) {
5510                switch (lua_type(L, 3)) {
5511                    case LUA_TSTRING:
5512                        {
5513                            const char *s = lua_tostring(L, 3);
5514                            if (lua_key_eq(s, additional)) {
5515                                m = packing_additional;
5516                            } else if (lua_key_eq(s, exactly)) {
5517                                m = packing_exactly;
5518                            }
5519                            break;
5520                        }
5521                    case LUA_TNUMBER:
5522                        {
5523                            m = (int) lua_tointeger(L, 3);
5524                            if (m != packing_exactly && m != packing_additional) {
5525                                m = packing_additional;
5526                            }
5527                            break;
5528                        }
5529                }
5530                if (top > 3) {
5531                    d = nodelib_getdirection(L, 4);
5532                }
5533            }
5534        }
5535    } else {
5536        n = null;
5537    }
5538    p = tex_vpack(n, w, m, max_dimension, d, holding_none_option, NULL);
5539    lua_pushinteger(L, p);
5540    lua_pushinteger(L, lmt_packaging_state.last_badness);
5541    return 2;
5542}
5543
5544/* node.direct.dimensions */
5545/* node.direct.rangedimensions */
5546/* node.direct.naturalwidth */
5547
5548/* mult sign order firstnode          verticalbool */
5549/* mult sign order firstnode lastnode verticalbool */
5550/*                 firstnode          verticalbool */
5551/*                 firstnode lastnode verticalbool */
5552
5553# define dimensions_usage common_usage
5554
5555static int nodelib_direct_dimensions(lua_State *L)
5556{
5557    int top = lua_gettop(L);
5558    if (top > 0) {
5559        scaledwhd siz = { .wd = 0, .ht = 0, .dp = 0, .ns = 0 };
5560        glueratio g_mult = normal_glue_multiplier;
5561        int vertical = 0;
5562        int g_sign = normal_glue_sign;
5563        int g_order = normal_glue_order;
5564        int i = 1;
5565        halfword n = null;
5566        halfword p = null;
5567        if (top > 3) {
5568            g_mult = (glueratio) lua_tonumber(L, i++);
5569            g_sign = tex_checked_glue_sign(lmt_tohalfword(L, i++));
5570            g_order = tex_checked_glue_order(lmt_tohalfword(L, i++));
5571        }
5572        n = nodelib_valid_direct_from_index(L, i++);
5573        if (lua_type(L, i) == LUA_TBOOLEAN) {
5574            vertical = lua_toboolean(L, i++);
5575        } else {
5576            p = nodelib_valid_direct_from_index(L, i++);
5577            vertical = lua_toboolean(L, i);
5578        }
5579        if (n) {
5580            if (vertical) { 
5581                siz = tex_natural_vsizes(n, p, g_mult, g_sign, g_order, 0);
5582            } else {
5583                siz = tex_natural_hsizes(n, p, g_mult, g_sign, g_order);
5584            } 
5585        }
5586        lua_pushinteger(L, siz.wd);
5587        lua_pushinteger(L, siz.ht);
5588        lua_pushinteger(L, siz.dp);
5589        return 3;
5590    } else {
5591        return luaL_error(L, "missing argument to 'dimensions' (direct node expected)");
5592    }
5593}
5594
5595/* parentnode firstnode lastnode vertical */
5596/* parentnode firstnode          vertical */
5597/* parentnode firstnode lastnode vertical nsizetoo */
5598/* parentnode firstnode          vertical nsizetoo */
5599
5600# define rangedimensions_usage common_usage
5601
5602static int nodelib_direct_rangedimensions(lua_State *L)
5603{
5604    int top = lua_gettop(L);
5605    if (top > 1) {
5606        scaledwhd siz = { .wd = 0, .ht = 0, .dp = 0, .ns = 0 };
5607        int vertical = 0;
5608        int nsizetoo = 0;
5609        int index = 1;
5610        halfword parent = nodelib_valid_direct_from_index(L, index++);
5611        halfword first = nodelib_valid_direct_from_index(L, index++);
5612        halfword last = first;
5613        if (lua_type(L, index) == LUA_TBOOLEAN) {
5614            vertical = lua_toboolean(L, index++);
5615        } else {
5616            last = nodelib_valid_direct_from_index(L, index++);
5617            vertical = lua_toboolean(L, index++);
5618        }
5619        nsizetoo = lua_toboolean(L, index);
5620        if (parent && first) {
5621            if (vertical) { 
5622                siz = tex_natural_vsizes(first, last, (glueratio) box_glue_set(parent), box_glue_sign(parent), box_glue_order(parent), 0);
5623            } else {
5624                siz = tex_natural_hsizes(first, last, (glueratio) box_glue_set(parent), box_glue_sign(parent), box_glue_order(parent));
5625            }
5626        }
5627        lua_pushinteger(L, siz.wd);
5628        lua_pushinteger(L, siz.ht);
5629        lua_pushinteger(L, siz.dp);
5630        if (nsizetoo) {
5631            lua_pushinteger(L, siz.ns);
5632            return 4;
5633        } else {
5634            return 3;
5635        }
5636    } else {
5637        return luaL_error(L, "missing argument to 'rangedimensions' (2 or more direct nodes expected)");
5638    }
5639}
5640
5641/* parentnode firstnode [last] */
5642
5643# define naturalwidth_usage common_usage
5644
5645static int nodelib_direct_naturalwidth(lua_State *L)
5646{
5647    int top = lua_gettop(L);
5648    if (top > 1) {
5649        scaled wd = 0;
5650        halfword parent = nodelib_valid_direct_from_index(L, 1);
5651        halfword first = nodelib_valid_direct_from_index(L, 2);
5652        halfword last = nodelib_valid_direct_from_index(L, 3);
5653        if (parent && first) {
5654            wd = tex_natural_width(first, last, (glueratio) box_glue_set(parent), box_glue_sign(parent), box_glue_order(parent));
5655        }
5656        lua_pushinteger(L, wd);
5657        return 1;
5658    } else {
5659        return luaL_error(L, "missing argument to 'naturalwidth' (2 or more direct nodes expected)");
5660    }
5661}
5662
5663# define naturalhsize_usage common_usage
5664
5665static int nodelib_direct_naturalhsize(lua_State *L)
5666{
5667    scaled wd = 0;
5668    halfword c = null;
5669    halfword n = nodelib_valid_direct_from_index(L, 1);
5670    if (n) {
5671        wd = tex_natural_hsize(n, &c);
5672    }
5673    lua_pushinteger(L, wd);
5674    lua_pushinteger(L, c ? glue_amount(c) : 0);
5675    nodelib_push_direct_or_nil(L, c);
5676    return 3;
5677}
5678
5679# define mlisttohlist_usage common_usage
5680
5681static int nodelib_direct_mlisttohlist(lua_State *L)
5682{
5683    halfword n = nodelib_valid_direct_from_index(L, 1);
5684    if (n) {
5685        int style = lmt_get_math_style(L, 2, text_style);
5686        int penalties = lua_toboolean(L, 3);
5687        int beginclass = lmt_optinteger(L, 4, unset_noad_class);
5688        int endclass = lmt_optinteger(L, 5, unset_noad_class);
5689        if (! valid_math_class_code(beginclass)) {
5690            beginclass = unset_noad_class;
5691        }
5692        if (! valid_math_class_code(endclass)) {
5693            endclass = unset_noad_class;
5694        }
5695        n = tex_mlist_to_hlist(n, penalties, style, beginclass, endclass, NULL, m_to_h_callback);
5696    }
5697    nodelib_push_direct_or_nil(L, n);
5698    return 1;
5699}
5700
5701/*tex
5702
5703    This function is similar to |get_node_type_id|, for field identifiers. It has to do some more
5704    work, because not all identifiers are valid for all types of nodes. We can make this faster if
5705    needed but when this needs to be called often something is wrong with the code.
5706
5707*/
5708
5709static int nodelib_aux_get_node_field_id(lua_State *L, int n, int node)
5710{
5711    int t = node_type(node);
5712    const char *s = lua_tostring(L, n);
5713    if (! s) {
5714        return -2;
5715    } else if (lua_key_eq(s, next)) {
5716        return 0;
5717    } else if (lua_key_eq(s, id)) {
5718        return 1;
5719    } else if (lua_key_eq(s, subtype)) {
5720        if (tex_nodetype_has_subtype(t)) {
5721            return 2;
5722        }
5723    } else if (lua_key_eq(s, attr)) {
5724        if (tex_nodetype_has_attributes(t)) {
5725            return 3;
5726        }
5727    } else if (lua_key_eq(s, prev)) {
5728        if (tex_nodetype_has_prev(t)) {
5729            return -1;
5730        }
5731    } else {
5732        value_info *fields = lmt_interface.node_data[t].fields;
5733        if (fields) {
5734            if (lua_key_eq(s, list)) {
5735                const char *sh = lua_key(head);
5736                for (int j = 0; fields[j].lua; j++) {
5737                    if (fields[j].name == s || fields[j].name == sh) {
5738                        return j + 3;
5739                    }
5740                }
5741            } else {
5742                for (int j = 0; fields[j].lua; j++) {
5743                    if (fields[j].name == s) {
5744                        return j + 3;
5745                    }
5746                }
5747            }
5748        }
5749    }
5750    return -2;
5751}
5752
5753/* node.hasfield */
5754
5755static int nodelib_userdata_hasfield(lua_State *L)
5756{
5757    int i = -2;
5758    if (! lua_isnil(L, 1)) {
5759        i = nodelib_aux_get_node_field_id(L, 2, lmt_check_isnode(L, 1));
5760    }
5761    lua_pushboolean(L, (i != -2));
5762    return 1;
5763}
5764
5765/* node.direct.hasfield */
5766
5767# define hasfield_usage common_usage
5768
5769static int nodelib_direct_hasfield(lua_State *L)
5770{
5771    int i = -2;
5772    halfword n = nodelib_valid_direct_from_index(L, 1);
5773    if (n) {
5774        i = nodelib_aux_get_node_field_id(L, 2, n);
5775    }
5776    lua_pushboolean(L, (i != -2));
5777    return 1;
5778}
5779
5780/* node.types */
5781
5782static int nodelib_shared_types(lua_State *L)
5783{
5784    lua_newtable(L);
5785    for (int i = 0; lmt_interface.node_data[i].id != -1; i++) {
5786        if (lmt_interface.node_data[i].visible) {
5787            lua_pushstring(L, lmt_interface.node_data[i].name);
5788            lua_rawseti(L, -2, lmt_interface.node_data[i].id);
5789        }
5790    }
5791    return 1;
5792}
5793
5794/* node.fields (fetch the list of valid fields) */
5795
5796# define fields_usage common_usage
5797
5798static int nodelib_shared_fields(lua_State *L)
5799{
5800    int t = nodelib_aux_get_valid_node_type_id(L, 1);
5801    int f = lua_toboolean(L, 2);
5802    value_info *fields = lmt_interface.node_data[t].fields;
5803    lua_newtable(L);
5804    if (f) {
5805        lua_push_key(next);
5806        lua_push_key(node);
5807        lua_rawset(L, -3);
5808        lua_push_key(id)
5809        lua_push_key(integer);
5810        lua_rawset(L, -3);
5811        if (tex_nodetype_has_subtype(t)) {
5812            lua_push_key(subtype);
5813            lua_push_key(integer);
5814            lua_rawset(L, -3);
5815        }
5816        if (tex_nodetype_has_prev(t)) {
5817            lua_push_key(prev);
5818            lua_push_key(node);
5819            lua_rawset(L, -3);
5820        }
5821        if (fields) {
5822            for (lua_Integer i = 0; fields[i].lua != 0; i++) {
5823                /* todo: use other macros */
5824                lua_push_key_by_index(fields[i].lua);
5825                lua_push_key_by_index(lmt_interface.field_type_values[fields[i].type].lua);
5826             // lua_pushinteger(L, fields[i].type);
5827                lua_rawset(L, -3);
5828            }
5829        }
5830    } else {
5831        int offset = 0;
5832        lua_push_key(id);
5833        lua_rawseti(L, -2, offset++);
5834        if (tex_nodetype_has_subtype(t)) {
5835            lua_push_key(subtype);
5836            lua_rawseti(L, -2, offset++);
5837        }
5838        lua_push_key(next);
5839        lua_rawseti(L, -2, offset++);
5840        if (tex_nodetype_has_prev(t)) {
5841            lua_push_key(prev);
5842            lua_rawseti(L, -2, offset++);
5843        }
5844        if (fields) {
5845            for (lua_Integer i = 0; fields[i].lua != 0; i++) {
5846             // lua_push_key_by_index(L, fields[i].lua);
5847                lua_rawgeti(L, LUA_REGISTRYINDEX, fields[i].lua);
5848                lua_rawseti(L, -2, offset++);
5849            }
5850        }
5851    }
5852    return 1;
5853}
5854
5855/* These should move to texlib ... which might happen.  */
5856
5857// static int nodelib_shared_values(lua_State *L)
5858// {
5859//     if (lua_type(L, 1) == LUA_TSTRING) {
5860//         /*
5861//             delimiter options (bit set)
5862//             delimiter modes   (bit set)
5863//         */
5864//         const char *s = lua_tostring(L, 1);
5865//         if (lua_key_eq(s, glue) || lua_key_eq(s, fill)) {
5866//             return lmt_push_info_values(L, lmt_interface.node_fill_values);
5867//         } else if (lua_key_eq(s, dir)) {
5868//             /* moved to lmttexlib */
5869//             return lmt_push_info_values(L, lmt_interface.direction_values);
5870//         } else if (lua_key_eq(s, math)) {
5871//             /* moved to lmttexlib */
5872//             return lmt_push_info_keys(L, lmt_interface.math_parameter_values);
5873//         } else if (lua_key_eq(s, style)) {
5874//             /* moved to lmttexlib */
5875//             return lmt_push_info_values(L, lmt_interface.math_style_values);
5876//         } else if (lua_key_eq(s, page)) {
5877//             /*tex These are never used, whatsit related. */
5878//             return lmt_push_info_values(L, lmt_interface.page_contribute_values);
5879//         }
5880//     }
5881//     lua_pushnil(L);
5882//     return 1;
5883// }
5884
5885static int nodelib_shared_subtypes(lua_State *L)
5886{
5887    value_info *subtypes = NULL;
5888    switch (lua_type(L, 1)) {
5889        case LUA_TSTRING:
5890            {
5891                const char *s = lua_tostring(L,1);
5892                /* */
5893                if  (lua_key_eq(s, list)) {
5894                    subtypes = lmt_interface.node_data[hlist_node].subtypes;
5895                } else {
5896                    for (int id = first_nodetype; id <= last_nodetype; id++) {
5897                        if (lmt_interface.node_data[id].name && ! strcmp(lmt_interface.node_data[id].name, s)) {
5898                            subtypes = lmt_interface.node_data[id].subtypes;
5899                            break;
5900                        }
5901                    }
5902                }
5903                /* no need for speed for more popular types: */
5904                /*
5905                     if (lua_key_eq(s, glyph))     subtypes = lmt_interface.node_data[glyph_node]    .subtypes;
5906                else if (lua_key_eq(s, glue))      subtypes = lmt_interface.node_data[glue_node]     .subtypes;
5907                else if (lua_key_eq(s, dir))       subtypes = lmt_interface.node_data[dir_node]      .subtypes;
5908                else if (lua_key_eq(s, mark))      subtypes = lmt_interface.node_data[mark_node]     .subtypes;
5909                else if (lua_key_eq(s, boundary))  subtypes = lmt_interface.node_data[boundary_node] .subtypes;
5910                else if (lua_key_eq(s, penalty))   subtypes = lmt_interface.node_data[penalty_node]  .subtypes;
5911                else if (lua_key_eq(s, kern))      subtypes = lmt_interface.node_data[kern_node]     .subtypes;
5912                else if (lua_key_eq(s, rule))      subtypes = lmt_interface.node_data[rule_node]     .subtypes;
5913                else if (lua_key_eq(s, list)
5914                     ||  lua_key_eq(s, hlist)
5915                     ||  lua_key_eq(s, vlist))     subtypes = lmt_interface.node_data[hlist_node]    .subtypes;
5916                else if (lua_key_eq(s, adjust))    subtypes = lmt_interface.node_data[adjust_node]   .subtypes;
5917                else if (lua_key_eq(s, disc))      subtypes = lmt_interface.node_data[disc_node]     .subtypes;
5918                else if (lua_key_eq(s, math))      subtypes = lmt_interface.node_data[math_node]     .subtypes;
5919                else if (lua_key_eq(s, noad))      subtypes = lmt_interface.node_data[simple_noad]   .subtypes;
5920                else if (lua_key_eq(s, radical))   subtypes = lmt_interface.node_data[radical_noad]  .subtypes;
5921                else if (lua_key_eq(s, accent))    subtypes = lmt_interface.node_data[accent_noad]   .subtypes;
5922                else if (lua_key_eq(s, fence))     subtypes = lmt_interface.node_data[fence_noad]    .subtypes;
5923                else if (lua_key_eq(s, fraction))  subtypes = lmt_interface.node_data[fraction_noad] .subtypes;
5924                else if (lua_key_eq(s, choice))    subtypes = lmt_interface.node_data[choice_node]   .subtypes;
5925                else if (lua_key_eq(s, par))       subtypes = lmt_interface.node_data[par_node]      .subtypes;
5926                else if (lua_key_eq(s, attribute)) subtypes = lmt_interface.node_data[attribute_node].subtypes;
5927                */
5928            }
5929            break;
5930        case LUA_TNUMBER:
5931            {
5932                int id = (int) lua_tointeger(L, 1);
5933                if (id >= first_nodetype && id <= last_nodetype) {
5934                    subtypes = lmt_interface.node_data[id].subtypes;
5935                }
5936            }
5937            break;
5938    }
5939    if (subtypes) {
5940        lua_newtable(L);
5941        for (int i = 0; subtypes[i].name; i++) {
5942            lua_rawgeti(L, LUA_REGISTRYINDEX, subtypes[i].lua);
5943            lua_rawseti(L, -2, subtypes[i].id);
5944        }
5945    } else {
5946        lua_pushnil(L);
5947    }
5948    return 1;
5949}
5950
5951/* node.direct.slide */
5952
5953# define slide_usage common_usage
5954
5955static int nodelib_direct_slide(lua_State *L)
5956{
5957    halfword n = nodelib_valid_direct_from_index(L, 1);
5958    if (n) {
5959        while (node_next(n)) {
5960            node_prev(node_next(n)) = n;
5961            n = node_next(n);
5962        }
5963        lua_pushinteger(L, n);
5964    } else {
5965        lua_pushnil(L);
5966    }
5967    return 1;
5968}
5969
5970/* node.tail (find the end of a list) */
5971
5972static int nodelib_userdata_tail(lua_State *L)
5973{
5974    if (! lua_isnil(L, 1)) {
5975        halfword n = lmt_check_isnode(L, 1);
5976        if (n) {
5977            while (node_next(n)) {
5978                n = node_next(n);
5979            }
5980            lmt_push_node_fast(L, n);
5981        } else {
5982            /*tex We keep the old userdata. */
5983        }
5984    }
5985    return 1;
5986}
5987
5988/* node.direct.tail */
5989
5990# define tail_usage common_usage
5991
5992static int nodelib_direct_tail(lua_State *L)
5993{
5994    halfword n = nodelib_valid_direct_from_index(L, 1);
5995    if (n) {
5996        while (node_next(n)) {
5997            n = node_next(n);
5998        }
5999        lua_pushinteger(L, n);
6000    } else {
6001        lua_pushnil(L);
6002    }
6003    return 1;
6004}
6005
6006/* node.direct.beginofmath */
6007/* node.direct.endofmath */
6008
6009# define beginofmath_usage common_usage
6010# define endofmath_usage   common_usage
6011
6012static int nodelib_direct_beginofmath(lua_State *L)
6013{
6014    halfword n = nodelib_valid_direct_from_index(L, 1);
6015    if (n) {
6016        if (node_type(n) == math_node && (node_subtype(n) == begin_inline_math || node_subtype(n) == begin_broken_math)) {
6017            lua_pushinteger(L, n);
6018            return 1;
6019        } else {
6020            int level = 1;
6021            while (node_next(n)) {
6022                n = node_next(n);
6023                if (n && node_type(n) == math_node) {
6024                    /*tex We can't really have nested math but we can be at the end so: */
6025                    switch (node_subtype(n)) {
6026                        case begin_inline_math:
6027                        case begin_broken_math:
6028                            ++level;
6029                            if (level > 0) {
6030                                lua_pushinteger(L, n);
6031                                return 1;
6032                            } else {
6033                                break;
6034                            }
6035                        case end_inline_math:
6036                        case end_broken_math:
6037                            --level;
6038                            break;
6039                    }
6040                }
6041            }
6042        }
6043    }
6044    return 0;
6045}
6046
6047static int nodelib_direct_endofmath(lua_State *L)
6048{
6049    halfword n = nodelib_valid_direct_from_index(L, 1);
6050    if (n) {
6051        if (node_type(n) == math_node && (node_subtype(n) == end_inline_math || node_subtype(n) == end_broken_math)) {
6052            lua_pushinteger(L, n);
6053            return 1;
6054        } else {
6055            int level = 1;
6056            while (node_next(n)) {
6057                n = node_next(n);
6058                if (n && node_type(n) == math_node) {
6059                    /*tex We can't really have nested math but we can be at a begin so: */
6060                    switch (node_subtype(n)) {
6061                        case begin_inline_math:
6062                        case begin_broken_math:
6063                            ++level;
6064                            break;
6065                        case end_inline_math:
6066                        case end_broken_math:
6067                            --level;
6068                            if (level > 0) {
6069                                break;
6070                            } else {
6071                                lua_pushinteger(L, n);
6072                                return 1;
6073                            }
6074                    }
6075                }
6076            }
6077        }
6078    }
6079    return 0;
6080}
6081
6082/* node.hasattribute (gets attribute) */
6083
6084static int nodelib_userdata_hasattribute(lua_State *L)
6085{
6086    halfword n = lmt_check_isnode(L, 1);
6087    if (n) {
6088        int key = lmt_tointeger(L, 2);
6089        int val = tex_has_attribute(n, key, lmt_optinteger(L, 3, unused_attribute_value));
6090        if (val > unused_attribute_value) {
6091            lua_pushinteger(L, val);
6092            return 1;
6093        }
6094    }
6095    lua_pushnil(L);
6096    return 1;
6097}
6098
6099/* node.direct.has_attribute */
6100
6101# define hasattribute_usage common_usage
6102
6103static int nodelib_direct_hasattribute(lua_State *L)
6104{
6105    halfword n = nodelib_valid_direct_from_index(L, 1);
6106    if (n) {
6107        int key = nodelib_valid_direct_from_index(L, 2);
6108        int val = tex_has_attribute(n, key, lmt_optinteger(L, 3, unused_attribute_value));
6109        if (val > unused_attribute_value) {
6110            lua_pushinteger(L, val);
6111            return 1;
6112        }
6113    }
6114    lua_pushnil(L);
6115    return 1;
6116}
6117
6118/* node.get_attribute */
6119
6120static int nodelib_userdata_getattribute(lua_State *L)
6121{
6122    halfword p = lmt_check_isnode(L, 1);
6123    if (tex_nodetype_has_attributes(node_type(p))) {
6124        p = node_attr(p);
6125        if (p) {
6126            p = node_next(p);
6127            if (p) {
6128                int i = lmt_optinteger(L, 2, 0);
6129                while (p) {
6130                    if (attribute_index(p) == i) {
6131                        int v = attribute_value(p);
6132                        if (v == unused_attribute_value) {
6133                            break;
6134                        } else {
6135                            lua_pushinteger(L, v);
6136                            return 1;
6137                        }
6138                    } else if (attribute_index(p) > i) {
6139                        break;
6140                    }
6141                    p = node_next(p);
6142                }
6143            }
6144        }
6145    }
6146    lua_pushnil(L);
6147    return 1;
6148}
6149
6150# define findattributerange_usage common_usage
6151
6152static int nodelib_direct_findattributerange(lua_State *L)
6153{
6154    halfword h = nodelib_valid_direct_from_index(L, 1);
6155    if (h) {
6156        halfword i = lmt_tohalfword(L, 2);
6157        while (h) {
6158            if (tex_nodetype_has_attributes(node_type(h))) {
6159                halfword p = node_attr(h);
6160                if (p) {
6161                    p = node_next(p);
6162                    while (p) {
6163                        if (attribute_index(p) == i) {
6164                            if (attribute_value(p) == unused_attribute_value) {
6165                                break;
6166                            } else {
6167                                halfword t = h;
6168                                while (node_next(t)) {
6169                                    t = node_next(t);
6170                                }
6171                                while (t != h) {
6172                                    if (tex_nodetype_has_attributes(node_type(t))) {
6173                                        halfword a = node_attr(t);
6174                                        if (a) {
6175                                            a = node_next(a);
6176                                            while (a) {
6177                                                if (attribute_index(a) == i) {
6178                                                    if (attribute_value(a) == unused_attribute_value) {
6179                                                        break;
6180                                                    } else {
6181                                                        goto FOUND;
6182                                                    }
6183                                                } else if (attribute_index(a) > i) {
6184                                                    break;
6185                                                }
6186                                                a = node_next(a);
6187                                            }
6188                                        }
6189                                    }
6190                                    t = node_prev(t);
6191                                }
6192                              FOUND:
6193                                lua_pushinteger(L, h);
6194                                lua_pushinteger(L, t);
6195                                return 2;
6196                            }
6197                        } else if (attribute_index(p) > i) {
6198                            break;
6199                        }
6200                        p = node_next(p);
6201                    }
6202                }
6203            }
6204            h = node_next(h);
6205        }
6206    }
6207    return 0;
6208}
6209
6210/* node.direct.getattribute */
6211/* node.direct.setattribute */
6212/* node.direct.unsetattribute */
6213/* node.direct.findattribute */
6214
6215/* maybe: getsplitattribute(n,a,24,8) => (v & ~(~0 << 24)) (v & ~(~0 << 8)) */
6216
6217# define getattribute_usage  common_usage
6218# define getattributes_usage common_usage
6219
6220static int nodelib_direct_getattribute(lua_State *L)
6221{
6222    halfword p = nodelib_valid_direct_from_index(L, 1);
6223    if (p) {
6224        if (node_type(p) != attribute_node) {
6225            p = tex_nodetype_has_attributes(node_type(p)) ? node_attr(p) : null;
6226        }
6227        if (p) {
6228            if (node_subtype(p) == attribute_list_subtype) {
6229                p = node_next(p);
6230            }
6231            if (p) {
6232                halfword index = lmt_opthalfword(L, 2, 0);
6233                while (p) {
6234                    halfword i = attribute_index(p);
6235                    if (i == index) {
6236                        int v = attribute_value(p);
6237                        if (v == unused_attribute_value) {
6238                            break;
6239                        } else {
6240                            lua_pushinteger(L, v);
6241                            return 1;
6242                        }
6243                    } else if (i > index) {
6244                        break;
6245                    }
6246                    p = node_next(p);
6247                }
6248            }
6249        }
6250    }
6251    lua_pushnil(L);
6252    return 1;
6253}
6254
6255static int nodelib_direct_getattributes(lua_State *L)
6256{
6257    halfword p = nodelib_valid_direct_from_index(L, 1);
6258    if (p) {
6259        if (node_type(p) != attribute_node) {
6260            p = tex_nodetype_has_attributes(node_type(p)) ? node_attr(p) : null;
6261        }
6262        if (p) {
6263            if (node_subtype(p) == attribute_list_subtype) {
6264                p = node_next(p);
6265            }
6266            if (p) {
6267                int top = lua_gettop(L);
6268                for (int i = 2; i <= top; i++) {
6269                    halfword a = lmt_tohalfword(L, i);
6270                    halfword n = p;
6271                    halfword v = unused_attribute_value;
6272                    while (n) {
6273                        halfword id = attribute_index(n);
6274                        if (id == a) {
6275                            v = attribute_value(n);
6276                            break;
6277                        } else if (id > a) {
6278                            break;
6279                        } else {
6280                            n = node_next(n);
6281                        }
6282                    }
6283                    if (v == unused_attribute_value) {
6284                        lua_pushnil(L);
6285                    } else {
6286                        lua_pushinteger(L, v);
6287                    }
6288                }
6289                return top - 1;
6290            }
6291        }
6292    }
6293    return 0;
6294}
6295
6296# define setattribute_usage  common_usage
6297# define setattributes_usage common_usage
6298
6299static int nodelib_direct_setattribute(lua_State *L)
6300{
6301    halfword n = nodelib_valid_direct_from_index(L, 1);
6302    if (n && tex_nodetype_has_attributes(node_type(n))) { // already checked
6303        halfword index = lmt_tohalfword(L, 2);
6304        halfword value = lmt_optinteger(L, 3, unused_attribute_value);
6305     // if (value == unused_attribute_value) {
6306     //     tex_unset_attribute(n, index, value);
6307     // } else {
6308            tex_set_attribute(n, index, value);
6309     // }
6310    }
6311    return 0;
6312}
6313
6314/* set_attributes(n,[initial,]key1,val1,key2,val2,...) */
6315
6316static int nodelib_direct_setattributes(lua_State *L)
6317{
6318    halfword n = nodelib_valid_direct_from_index(L, 1);
6319    if (n && tex_nodetype_has_attributes(node_type(n))) {
6320        int top = lua_gettop(L);
6321        int ini = 2;
6322        if (lua_type(L, 2) == LUA_TBOOLEAN) {
6323            ++ini;
6324            if (lua_toboolean(L, 2) && ! node_attr(n)) {
6325                attach_current_attribute_list(n);
6326            }
6327        }
6328        for (int i = ini; i <= top; i += 2) {
6329            halfword key = lmt_tohalfword(L, i);
6330            halfword val = lmt_optinteger(L, i + 1, unused_attribute_value);
6331         // if (val == unused_attribute_value) {
6332         //     tex_unset_attribute(p, key, val);
6333         // } else {
6334                tex_set_attribute(n, key, val);
6335         // }
6336        }
6337    }
6338    return 0;
6339}
6340
6341# define patchattributes_usage common_usage
6342
6343static int nodelib_direct_patchattributes(lua_State *L)
6344{
6345    halfword p = nodelib_valid_direct_from_index(L, 1);
6346    if (p) { /* todo: check if attributes */
6347        halfword att = null;
6348        int top = lua_gettop(L);
6349        for (int i = 2; i <= top; i += 2) {
6350            halfword index = lmt_tohalfword(L, i);
6351            halfword value = lua_type(L, i + 1) == LUA_TNUMBER ? lmt_tohalfword(L, i + 1) : unused_attribute_value;
6352            if (att) {
6353                att = tex_patch_attribute_list(att, index, value);
6354            } else {
6355                att = tex_copy_attribute_list_set(node_attr(p), index, value);
6356            }
6357        }
6358        tex_attach_attribute_list_attribute(p, att);
6359    }
6360    return 0;
6361}
6362
6363/* firstnode attributeid [nodetype] */
6364
6365# define findattribute_usage common_usage
6366
6367static int nodelib_direct_findattribute(lua_State *L) /* returns attr value and node */
6368{
6369    halfword c = nodelib_valid_direct_from_index(L, 1);
6370    if (c) {
6371        halfword i = lmt_tohalfword(L, 2);
6372        if (lua_type(L, 3) == LUA_TBOOLEAN && lua_toboolean(L, 3)) { 
6373            /* true: content */
6374            halfword a = null;
6375            while (c) {
6376                switch (node_type(c)) {
6377                    case hlist_node:
6378                    case vlist_node:
6379                        /*tex We're okay with an empty one here. */
6380                    case rule_node:
6381                    case disc_node:
6382                    case glyph_node:
6383                        goto CHECK;
6384                    case glue_node:
6385                        if (glue_leader_ptr(c)) {
6386                            goto CHECK;
6387                        } else {
6388                            goto NEXT;
6389                        }
6390                    default: 
6391                        goto NEXT;
6392                }
6393              CHECK:
6394                {
6395                    /* We skip if the previous value is the same and didn't match. */
6396                    halfword p = node_attr(c);
6397                    if (p && a != p) {
6398                        a = p;
6399                        p = node_next(p);
6400                        while (p) {
6401                            if (attribute_index(p) == i) {
6402                                halfword ret = attribute_value(p);
6403                                if (ret == unused_attribute_value) {
6404                                    break;
6405                                } else {
6406                                    lua_pushinteger(L, ret);
6407                                    lua_pushinteger(L, c);
6408                                    return 2;
6409                                }
6410                            } else if (attribute_index(p) > i) {
6411                                break;
6412                            }
6413                            p = node_next(p);
6414                        }
6415                    }
6416                }
6417              NEXT:
6418                c = node_next(c);
6419            }
6420        } else {
6421            halfword t = lmt_optinteger(L, 3, -1);
6422            halfword a = null;
6423            while (c) {
6424                if ((t < 0 || node_type(c) == t) && tex_nodetype_has_attributes(node_type(c))) {
6425                    /* We skip if the previous value is the same and didn't match. */
6426                    halfword p = node_attr(c);
6427                    if (p && a != p) {
6428                        a = p;
6429                        p = node_next(p);
6430                        while (p) {
6431                            if (attribute_index(p) == i) {
6432                                halfword ret = attribute_value(p);
6433                                if (ret == unused_attribute_value) {
6434                                    break;
6435                                } else {
6436                                    lua_pushinteger(L, ret);
6437                                    lua_pushinteger(L, c);
6438                                    return 2;
6439                                }
6440                            } else if (attribute_index(p) > i) {
6441                                break;
6442                            }
6443                            p = node_next(p);
6444                        }
6445                    }
6446                }
6447                c = node_next(c);
6448            }
6449        }
6450    }
6451    return 0;
6452}
6453
6454# define unsetattribute_usage  common_usage
6455# define unsetattributes_usage common_usage
6456
6457static int nodelib_direct_unsetattribute(lua_State *L)
6458{
6459    halfword n = nodelib_valid_direct_from_index(L, 1);
6460    if (n) {
6461        halfword key = lmt_checkhalfword(L, 2);
6462        halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
6463        halfword ret = tex_unset_attribute(n, key, val);
6464        if (ret > unused_attribute_value) { /* != */
6465            lua_pushinteger(L, ret);
6466        } else {
6467            lua_pushnil(L);
6468        }
6469    } else {
6470        lua_pushnil(L);
6471    }
6472    return 1;
6473}
6474static int nodelib_direct_unsetattributes(lua_State *L)
6475{
6476    halfword key = lmt_checkhalfword(L, 1);
6477    halfword first = nodelib_valid_direct_from_index(L, 2);
6478    halfword last = nodelib_valid_direct_from_index(L, 3);
6479    if (first) {
6480        tex_unset_attributes(first, last, key);
6481    }
6482    return 0;
6483}
6484
6485/* node.setattribute */
6486/* node.unsetattribute */
6487
6488static int nodelib_userdata_setattribute(lua_State *L)
6489{
6490    halfword n = lmt_check_isnode(L, 1);
6491    if (n) {
6492        halfword key = lmt_tohalfword(L, 2);
6493        halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
6494        if (val == unused_attribute_value) {
6495            tex_unset_attribute(n, key, val);
6496        } else {
6497            tex_set_attribute(n, key, val);
6498        }
6499    }
6500    return 0;
6501}
6502
6503static int nodelib_userdata_unsetattribute(lua_State *L)
6504{
6505    halfword n = lmt_check_isnode(L, 1);
6506    if (n) {
6507        halfword key = lmt_checkhalfword(L, 2);
6508        halfword val = lmt_opthalfword(L, 3, unused_attribute_value);
6509        halfword ret = tex_unset_attribute(n, key, val);
6510        if (ret > unused_attribute_value) {
6511            lua_pushinteger(L, ret);
6512        } else {
6513            lua_pushnil(L);
6514        }
6515    } else {
6516        lua_pushnil(L);
6517    }
6518    return 1;
6519}
6520
6521/* node.direct.getglue */
6522/* node.direct.setglue */
6523/* node.direct.iszeroglue */
6524
6525# define getglue_usage    (glue_usage | glue_spec_usage | hlist_usage | vlist_usage | unset_usage | math_usage)
6526# define setglue_usage    getglue_usage
6527# define iszeroglue_usage getglue_usage
6528
6529static int nodelib_direct_getglue(lua_State *L)
6530{
6531    halfword n = nodelib_valid_direct_from_index(L, 1);
6532    if (n) {
6533        switch (node_type(n)) {
6534            case glue_node:
6535            case glue_spec_node:
6536                lua_pushinteger(L, glue_amount(n));
6537                lua_pushinteger(L, glue_stretch(n));
6538                lua_pushinteger(L, glue_shrink(n));
6539                lua_pushinteger(L, glue_stretch_order(n));
6540                lua_pushinteger(L, glue_shrink_order(n));
6541                return 5;
6542            case hlist_node:
6543            case vlist_node:
6544            case unset_node:
6545                lua_pushnumber(L, (double) box_glue_set(n)); /* float */
6546                lua_pushinteger(L, box_glue_order(n));
6547                lua_pushinteger(L, box_glue_sign(n));
6548                return 3;
6549            case math_node:
6550                lua_pushinteger(L, math_amount(n));
6551                lua_pushinteger(L, math_stretch(n));
6552                lua_pushinteger(L, math_shrink(n));
6553                lua_pushinteger(L, math_stretch_order(n));
6554                lua_pushinteger(L, math_shrink_order(n));
6555                return 5;
6556        }
6557    }
6558    return 0;
6559}
6560
6561static int nodelib_direct_setglue(lua_State *L)
6562{
6563    halfword n = nodelib_valid_direct_from_index(L, 1);
6564    if (n) {
6565        int top = lua_gettop(L);
6566        switch (node_type(n)) {
6567            case glue_node:
6568            case glue_spec_node:
6569                glue_amount(n)        = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 2) : 0;
6570                glue_stretch(n)       = ((top > 2 && lua_type(L, 3) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 3) : 0;
6571                glue_shrink(n)        = ((top > 3 && lua_type(L, 4) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 4) : 0;
6572                glue_stretch_order(n) = tex_checked_glue_order((top > 4 && lua_type(L, 5) == LUA_TNUMBER) ? lmt_tohalfword(L, 5) : 0);
6573                glue_shrink_order(n)  = tex_checked_glue_order((top > 5 && lua_type(L, 6) == LUA_TNUMBER) ? lmt_tohalfword(L, 6) : 0);
6574                break;
6575            case hlist_node:
6576            case vlist_node:
6577            case unset_node:
6578                box_glue_set(n)   = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (glueratio) lua_tonumber(L, 2)  : 0;
6579                box_glue_order(n) = tex_checked_glue_order((top > 2 && lua_type(L, 3) == LUA_TNUMBER) ? (halfword)  lua_tointeger(L, 3) : 0);
6580                box_glue_sign(n)  = tex_checked_glue_sign((top > 3 && lua_type(L, 4) == LUA_TNUMBER) ? (halfword)  lua_tointeger(L, 4) : 0);
6581                break;
6582            case math_node:
6583                math_amount(n)        = ((top > 1 && lua_type(L, 2) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 2) : 0;
6584                math_stretch(n)       = ((top > 2 && lua_type(L, 3) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 3) : 0;
6585                math_shrink(n)        = ((top > 3 && lua_type(L, 4) == LUA_TNUMBER)) ? (halfword) lmt_roundnumber(L, 4) : 0;
6586                math_stretch_order(n) = tex_checked_glue_order((top > 4 && lua_type(L, 5) == LUA_TNUMBER) ? lmt_tohalfword(L, 5) : 0);
6587                math_shrink_order(n)  = tex_checked_glue_order((top > 5 && lua_type(L, 6) == LUA_TNUMBER) ? lmt_tohalfword(L, 6) : 0);
6588                break;
6589        }
6590    }
6591    return 0;
6592}
6593
6594static int nodelib_direct_iszeroglue(lua_State *L)
6595{
6596    halfword n = nodelib_valid_direct_from_index(L, 1);
6597    if (n) {
6598        switch (node_type(n)) {
6599            case glue_node:
6600            case glue_spec_node:
6601                lua_pushboolean(L, glue_amount(n) == 0 && glue_stretch(n) == 0 && glue_shrink(n) == 0);
6602                return 1;
6603            case hlist_node:
6604            case vlist_node:
6605                lua_pushboolean(L, box_glue_set(n) == 0.0 && box_glue_order(n) == 0 && box_glue_sign(n) == 0);
6606                return 1;
6607            case math_node:
6608                lua_pushboolean(L, math_amount(n) == 0 && math_stretch(n) == 0 && math_shrink(n) == 0);
6609                return 1;
6610        }
6611    }
6612    return 0;
6613}
6614
6615/* direct.startofpar */
6616
6617# define startofpar_usage common_usage
6618
6619static int nodelib_direct_startofpar(lua_State *L)
6620{
6621    halfword n = nodelib_valid_direct_from_index(L, 1);
6622    lua_pushboolean(L, n && tex_is_start_of_par_node(n));
6623    return 1;
6624}
6625
6626/* iteration */
6627
6628static int nodelib_aux_nil(lua_State *L)
6629{
6630    lua_pushnil(L);
6631    return 1;
6632}
6633
6634/* node.direct.traverse */
6635/* node.direct.traverseid */
6636/* node.direct.traversechar */
6637/* node.direct.traverseglyph */
6638/* node.direct.traverselist */
6639/* node.direct.traverseleader */
6640/* node.direct.traverseitalic */
6641/* node.direct.traversecontent */
6642
6643# define traverse_usage        common_usage
6644# define traverseid_usage      common_usage
6645# define traversechar_usage    common_usage
6646# define traverseglyph_usage   common_usage
6647# define traverselist_usage    common_usage
6648# define traverseleader_usage  common_usage
6649# define traverseitalic_usage  common_usage
6650# define traversecontent_usage common_usage
6651
6652static int nodelib_direct_aux_next(lua_State *L)
6653{
6654    halfword t;
6655    if (lua_isnil(L, 2)) {
6656        t = lmt_tohalfword(L, 1) ;
6657        lua_settop(L, 1);
6658    } else {
6659        t = lmt_tohalfword(L, 2) ;
6660        t = node_next(t);
6661        lua_settop(L, 2);
6662    }
6663    if (t) {
6664        lua_pushinteger(L, t);
6665        lua_pushinteger(L, node_type(t));
6666        lua_pushinteger(L, node_subtype(t));
6667        return 3;
6668    } else {
6669        lua_pushnil(L);
6670        return 1;
6671    }
6672}
6673
6674static int nodelib_direct_aux_prev(lua_State *L)
6675{
6676    halfword t;
6677    if (lua_isnil(L, 2)) {
6678        t = lmt_tohalfword(L, 1) ;
6679        lua_settop(L, 1);
6680    } else {
6681        t = lmt_tohalfword(L, 2) ;
6682        t = node_prev(t);
6683        lua_settop(L, 2);
6684    }
6685    if (t) {
6686        lua_pushinteger(L, t);
6687        lua_pushinteger(L, node_type(t));
6688        lua_pushinteger(L, node_subtype(t));
6689        return 3;
6690    } else {
6691        lua_pushnil(L);
6692        return 1;
6693    }
6694}
6695
6696static int nodelib_direct_traverse(lua_State *L)
6697{
6698    if (lua_isnil(L, 1)) {
6699        lua_pushcclosure(L, nodelib_aux_nil, 0);
6700        return 1;
6701    } else {
6702        halfword n = nodelib_valid_direct_from_index(L, 1);
6703        if (n) {
6704            if (lua_toboolean(L, 2)) {
6705                if (lua_toboolean(L, 3)) {
6706                    n = tex_tail_of_node_list(n);
6707                }
6708                lua_pushcclosure(L, nodelib_direct_aux_prev, 0);
6709            } else {
6710                lua_pushcclosure(L, nodelib_direct_aux_next, 0);
6711            }
6712            lua_pushinteger(L, n);
6713            lua_pushnil(L);
6714            return 3;
6715        } else {
6716            lua_pushcclosure(L, nodelib_aux_nil, 0);
6717            return 1;
6718        }
6719    }
6720}
6721
6722static int nodelib_direct_aux_next_filtered(lua_State *L)
6723{
6724    halfword t;
6725    int i = (int) lua_tointeger(L, lua_upvalueindex(1));
6726    if (lua_isnil(L, 2)) {
6727        t = lmt_tohalfword(L, 1) ;
6728        lua_settop(L, 1);
6729    } else {
6730        t = lmt_tohalfword(L, 2) ;
6731        t = node_next(t);
6732        lua_settop(L, 2);
6733    }
6734    while (t && node_type(t) != i) {
6735        t = node_next(t);
6736    }
6737    if (t) {
6738        lua_pushinteger(L, t);
6739        lua_pushinteger(L, node_subtype(t));
6740        return 2;
6741    } else {
6742        lua_pushnil(L);
6743        return 1;
6744    }
6745}
6746
6747static int nodelib_direct_aux_prev_filtered(lua_State *L)
6748{
6749    halfword t;
6750    int i = (int) lua_tointeger(L, lua_upvalueindex(1));
6751    if (lua_isnil(L, 2)) {
6752        t = lmt_tohalfword(L, 1) ;
6753        lua_settop(L, 1);
6754    } else {
6755        t = lmt_tohalfword(L, 2) ;
6756        t = node_prev(t);
6757        lua_settop(L, 2);
6758    }
6759    while (t && node_type(t) != i) {
6760        t = node_prev(t);
6761    }
6762    if (t) {
6763        lua_pushinteger(L, t);
6764        lua_pushinteger(L, node_subtype(t));
6765        return 2;
6766    } else {
6767        lua_pushnil(L);
6768        return 1;
6769    }
6770}
6771
6772static int nodelib_direct_traverseid(lua_State *L)
6773{
6774    if (lua_isnil(L, 2)) {
6775        lua_pushcclosure(L, nodelib_aux_nil, 0);
6776        return 1;
6777    } else {
6778        halfword n = nodelib_valid_direct_from_index(L, 2);
6779        if (n) {
6780            if (lua_toboolean(L, 3)) {
6781                if (lua_toboolean(L, 4)) {
6782                    n = tex_tail_of_node_list(n);
6783                }
6784                lua_settop(L, 1);
6785                lua_pushcclosure(L, nodelib_direct_aux_prev_filtered, 1);
6786            } else {
6787                lua_settop(L, 1);
6788                lua_pushcclosure(L, nodelib_direct_aux_next_filtered, 1);
6789            }
6790            lua_pushinteger(L, n);
6791            lua_pushnil(L);
6792            return 3;
6793        } else {
6794            return 0;
6795        }
6796    }
6797}
6798
6799static int nodelib_direct_aux_next_char(lua_State *L)
6800{
6801    halfword t;
6802    if (lua_isnil(L, 2)) {
6803        t = lmt_tohalfword(L, 1) ;
6804        lua_settop(L, 1);
6805    } else {
6806        t = lmt_tohalfword(L, 2) ;
6807        t = node_next(t);
6808        lua_settop(L, 2);
6809    }
6810    while (t && (node_type(t) != glyph_node || glyph_protected(t))) {
6811        t = node_next(t);
6812    }
6813    if (t) {
6814        lua_pushinteger(L, t);
6815        lua_pushinteger(L, glyph_character(t));
6816        lua_pushinteger(L, glyph_font(t));
6817        lua_pushinteger(L, glyph_data(t));
6818        return 4;
6819    } else {
6820        lua_pushnil(L);
6821        return 1;
6822    }
6823}
6824
6825static int nodelib_direct_aux_prev_char(lua_State *L)
6826{
6827    halfword t;
6828    if (lua_isnil(L, 2)) {
6829        t = lmt_tohalfword(L, 1) ;
6830        lua_settop(L, 1);
6831    } else {
6832        t = lmt_tohalfword(L, 2) ;
6833        t = node_prev(t);
6834        lua_settop(L, 2);
6835    }
6836    while (t && (node_type(t) != glyph_node || glyph_protected(t))) {
6837        t = node_prev(t);
6838    }
6839    if (t) {
6840        lua_pushinteger(L, t);
6841        lua_pushinteger(L, glyph_character(t));
6842        lua_pushinteger(L, glyph_font(t));
6843        lua_pushinteger(L, glyph_data(t));
6844        return 4;
6845    } else {
6846        lua_pushnil(L);
6847        return 1;
6848    }
6849}
6850
6851static int nodelib_direct_traversechar(lua_State *L)
6852{
6853    if (lua_isnil(L, 1)) {
6854        lua_pushcclosure(L, nodelib_aux_nil, 0);
6855        return 1;
6856    } else {
6857        halfword n = nodelib_valid_direct_from_index(L, 1);
6858        if (n) {
6859            if (lua_toboolean(L, 2)) {
6860                if (lua_toboolean(L, 3)) {
6861                    n = tex_tail_of_node_list(n);
6862                }
6863                lua_pushcclosure(L, nodelib_direct_aux_prev_char, 0);
6864            } else {
6865                lua_pushcclosure(L, nodelib_direct_aux_next_char, 0);
6866            }
6867            lua_pushinteger(L, n);
6868            lua_pushnil(L);
6869            return 3;
6870        } else {
6871            lua_pushcclosure(L, nodelib_aux_nil, 0);
6872            return 1;
6873        }
6874    }
6875}
6876
6877/* maybe a variant that checks for an attribute  */
6878
6879static int nodelib_direct_aux_next_glyph(lua_State *L)
6880{
6881    halfword t;
6882    if (lua_isnil(L, 2)) {
6883        t = lmt_tohalfword(L, 1) ;
6884        lua_settop(L, 1);
6885    } else {
6886        t = lmt_tohalfword(L, 2) ;
6887        t = node_next(t);
6888        lua_settop(L, 2);
6889    }
6890    while (t && node_type(t) != glyph_node) {
6891        t = node_next(t);
6892    }
6893    if (t) {
6894        lua_pushinteger(L, t);
6895        lua_pushinteger(L, glyph_character(t));
6896        lua_pushinteger(L, glyph_font(t));
6897        return 3;
6898    } else {
6899        lua_pushnil(L);
6900        return 1;
6901    }
6902}
6903
6904static int nodelib_direct_aux_prev_glyph(lua_State *L)
6905{
6906    halfword t;
6907    if (lua_isnil(L, 2)) {
6908        t = lmt_tohalfword(L, 1) ;
6909        lua_settop(L, 1);
6910    } else {
6911        t = lmt_tohalfword(L, 2) ;
6912        t = node_prev(t);
6913        lua_settop(L, 2);
6914    }
6915    while (t && node_type(t) != glyph_node) {
6916        t = node_prev(t);
6917    }
6918    if (t) {
6919        lua_pushinteger(L, t);
6920        lua_pushinteger(L, glyph_character(t));
6921        lua_pushinteger(L, glyph_font(t));
6922        return 3;
6923    } else {
6924        lua_pushnil(L);
6925        return 1;
6926    }
6927}
6928
6929static int nodelib_direct_traverseglyph(lua_State *L)
6930{
6931    if (lua_isnil(L, 1)) {
6932        lua_pushcclosure(L, nodelib_aux_nil, 0);
6933        return 1;
6934    } else {
6935        halfword n = nodelib_valid_direct_from_index(L, 1);
6936        if (n) {
6937            if (lua_toboolean(L, 2)) {
6938                if (lua_toboolean(L, 3)) {
6939                    n = tex_tail_of_node_list(n);
6940                }
6941                lua_pushcclosure(L, nodelib_direct_aux_prev_glyph, 0);
6942            } else {
6943                lua_pushcclosure(L, nodelib_direct_aux_next_glyph, 0);
6944            }
6945            lua_pushinteger(L, n);
6946            lua_pushnil(L);
6947            return 3;
6948        } else {
6949            lua_pushcclosure(L, nodelib_aux_nil, 0);
6950            return 1;
6951        }
6952    }
6953}
6954
6955static int nodelib_direct_aux_next_list(lua_State *L)
6956{
6957    halfword t;
6958    if (lua_isnil(L, 2)) {
6959        t = lmt_tohalfword(L, 1) ;
6960        lua_settop(L, 1);
6961    } else {
6962        t = lmt_tohalfword(L, 2) ;
6963        t = node_next(t);
6964        lua_settop(L, 2);
6965    }
6966    while (t && node_type(t) != hlist_node && node_type(t) != vlist_node) {
6967        t = node_next(t);
6968    }
6969    if (t) {
6970        lua_pushinteger(L, t);
6971        lua_pushinteger(L, node_type(t));
6972        lua_pushinteger(L, node_subtype(t));
6973        nodelib_push_direct_or_nil(L, box_list(t));
6974        return 4;
6975    } else {
6976        lua_pushnil(L);
6977        return 1;
6978    }
6979}
6980
6981static int nodelib_direct_aux_prev_list(lua_State *L)
6982{
6983    halfword t;
6984    if (lua_isnil(L, 2)) {
6985        t = lmt_tohalfword(L, 1) ;
6986        lua_settop(L, 1);
6987    } else {
6988        t = lmt_tohalfword(L, 2) ;
6989        t = node_prev(t);
6990        lua_settop(L, 2);
6991    }
6992    while (t && node_type(t) != hlist_node && node_type(t) != vlist_node) {
6993        t = node_prev(t);
6994    }
6995    if (t) {
6996        lua_pushinteger(L, t);
6997        lua_pushinteger(L, node_type(t));
6998        lua_pushinteger(L, node_subtype(t));
6999        nodelib_push_direct_or_nil(L, box_list(t));
7000        return 4;
7001    } else {
7002        lua_pushnil(L);
7003        return 1;
7004    }
7005}
7006
7007static int nodelib_direct_traverselist(lua_State *L)
7008{
7009    if (lua_isnil(L, 1)) {
7010        lua_pushcclosure(L, nodelib_aux_nil, 0);
7011        return 1;
7012    } else {
7013        halfword n = nodelib_valid_direct_from_index(L, 1);
7014        if (n) {
7015            if (lua_toboolean(L, 2)) {
7016                if (lua_toboolean(L, 3)) {
7017                    n = tex_tail_of_node_list(n);
7018                }
7019                lua_pushcclosure(L, nodelib_direct_aux_prev_list, 0);
7020            } else {
7021                lua_pushcclosure(L, nodelib_direct_aux_next_list, 0);
7022            }
7023            lua_pushinteger(L, n);
7024            lua_pushnil(L);
7025            return 3;
7026        } else {
7027            lua_pushcclosure(L, nodelib_aux_nil, 0);
7028            return 1;
7029        }
7030    }
7031}
7032
7033/*tex This is an experiment. */
7034
7035static int nodelib_direct_aux_next_leader(lua_State *L)
7036{
7037    halfword t;
7038    if (lua_isnil(L, 2)) {
7039        t = lmt_tohalfword(L, 1) ;
7040        lua_settop(L, 1);
7041    } else {
7042        t = lmt_tohalfword(L, 2) ;
7043        t = node_next(t);
7044        lua_settop(L, 2);
7045    }
7046    while (t && ! ((node_type(t) == hlist_node || node_type(t) == vlist_node) && has_box_package_state(t, package_u_leader_set))) {
7047        t = node_next(t);
7048    }
7049    if (t) {
7050        lua_pushinteger(L, t);
7051        lua_pushinteger(L, node_type(t));
7052        lua_pushinteger(L, node_subtype(t));
7053        nodelib_push_direct_or_nil(L, box_list(t));
7054        return 4;
7055    } else {
7056        lua_pushnil(L);
7057        return 1;
7058    }
7059}
7060
7061static int nodelib_direct_aux_prev_leader(lua_State *L)
7062{
7063    halfword t;
7064    if (lua_isnil(L, 2)) {
7065        t = lmt_tohalfword(L, 1) ;
7066        lua_settop(L, 1);
7067    } else {
7068        t = lmt_tohalfword(L, 2) ;
7069        t = node_prev(t);
7070        lua_settop(L, 2);
7071    }
7072    while (t && ! ((node_type(t) == hlist_node || node_type(t) == vlist_node) && has_box_package_state(t, package_u_leader_set))) {
7073        t = node_prev(t);
7074    }
7075    if (t) {
7076        lua_pushinteger(L, t);
7077        lua_pushinteger(L, node_type(t));
7078        lua_pushinteger(L, node_subtype(t));
7079        nodelib_push_direct_or_nil(L, box_list(t));
7080        return 4;
7081    } else {
7082        lua_pushnil(L);
7083        return 1;
7084    }
7085}
7086
7087static int nodelib_direct_traverseleader(lua_State *L)
7088{
7089    if (lua_isnil(L, 1)) {
7090        lua_pushcclosure(L, nodelib_aux_nil, 0);
7091        return 1;
7092    } else {
7093        halfword n = nodelib_valid_direct_from_index(L, 1);
7094        if (n) {
7095            if (lua_toboolean(L, 2)) {
7096                if (lua_toboolean(L, 3)) {
7097                    n = tex_tail_of_node_list(n);
7098                }
7099                lua_pushcclosure(L, nodelib_direct_aux_prev_leader, 0);
7100            } else {
7101                lua_pushcclosure(L, nodelib_direct_aux_next_leader, 0);
7102            }
7103            lua_pushinteger(L, n);
7104            lua_pushnil(L);
7105            return 3;
7106        } else {
7107            lua_pushcclosure(L, nodelib_aux_nil, 0);
7108            return 1;
7109        }
7110    }
7111}
7112
7113/*tex */
7114
7115static int nodelib_direct_aux_next_italic(lua_State *L)
7116{
7117    halfword t;
7118    if (lua_isnil(L, 2)) {
7119        t = lmt_tohalfword(L, 1) ;
7120        lua_settop(L, 1);
7121    } else {
7122        t = lmt_tohalfword(L, 2) ;
7123        t = node_next(t);
7124        lua_settop(L, 2);
7125    }
7126    while (t && ! (node_type(t) == kern_node && (node_subtype(t) == italic_kern_subtype || node_subtype(t) == left_correction_kern_subtype || node_subtype(t) == right_correction_kern_subtype))) {
7127        t = node_next(t);
7128    }
7129    if (t) {
7130        lua_pushinteger(L, t);
7131        lua_pushinteger(L, node_subtype(t));
7132        return 2;
7133    } else {
7134        lua_pushnil(L);
7135        return 1;
7136    }
7137}
7138
7139static int nodelib_direct_traverseitalic(lua_State *L)
7140{
7141    if (lua_isnil(L, 1)) {
7142        lua_pushcclosure(L, nodelib_aux_nil, 0);
7143        return 1;
7144    } else {
7145        halfword n = nodelib_valid_direct_from_index(L, 1);
7146        if (n) {
7147            lua_pushcclosure(L, nodelib_direct_aux_next_italic, 0);
7148            lua_pushinteger(L, n);
7149            lua_pushnil(L);
7150            return 3;
7151        } else {
7152            lua_pushcclosure(L, nodelib_aux_nil, 0);
7153            return 1;
7154        }
7155    }
7156}
7157
7158static int nodelib_direct_aux_next_content(lua_State *L)
7159{
7160    halfword t;
7161    halfword l = null;
7162    if (lua_isnil(L, 2)) {
7163        t = lmt_tohalfword(L, 1) ;
7164        lua_settop(L, 1);
7165    } else {
7166        t = lmt_tohalfword(L, 2) ;
7167        t = node_next(t);
7168        lua_settop(L, 2);
7169    }
7170    while (t) {
7171        switch (node_type(t)) {
7172            case glyph_node:
7173            case disc_node:
7174            case rule_node:
7175                goto FOUND;
7176            case glue_node:
7177                l = glue_leader_ptr(t);
7178                if (l) {
7179                    goto FOUND;
7180                } else {
7181                    break;
7182                }
7183            case hlist_node:
7184            case vlist_node:
7185                l = box_list(t);
7186                goto FOUND;
7187        }
7188        t = node_next(t);
7189    }
7190    lua_pushnil(L);
7191    return 1;
7192  FOUND:
7193    lua_pushinteger(L, t);
7194    lua_pushinteger(L, node_type(t));
7195    lua_pushinteger(L, node_subtype(t));
7196    if (l) {
7197        nodelib_push_direct_or_nil(L, l);
7198        return 4;
7199    } else {
7200        return 3;
7201    }
7202}
7203
7204static int nodelib_direct_aux_prev_content(lua_State *L)
7205{
7206    halfword t;
7207    halfword l = null;
7208    if (lua_isnil(L, 2)) {
7209        t = lmt_tohalfword(L, 1) ;
7210        lua_settop(L, 1);
7211    } else {
7212        t = lmt_tohalfword(L, 2) ;
7213        t = node_prev(t);
7214        lua_settop(L, 2);
7215    }
7216    while (t) {
7217        switch (node_type(t)) {
7218            case glyph_node:
7219            case disc_node:
7220            case rule_node:
7221                goto FOUND;
7222            case glue_node:
7223                l = glue_leader_ptr(t);
7224                if (l) {
7225                    goto FOUND;
7226                } else {
7227                    break;
7228                }
7229            case hlist_node:
7230            case vlist_node:
7231                l = box_list(t);
7232                goto FOUND;
7233        }
7234        t = node_prev(t);
7235    }
7236    lua_pushnil(L);
7237    return 1;
7238  FOUND:
7239    lua_pushinteger(L, t);
7240    lua_pushinteger(L, node_type(t));
7241    lua_pushinteger(L, node_subtype(t));
7242    if (l) {
7243        nodelib_push_direct_or_nil(L, l);
7244        return 4;
7245    } else {
7246        return 3;
7247    }
7248}
7249
7250static int nodelib_direct_traversecontent(lua_State *L)
7251{
7252    if (lua_isnil(L, 1)) {
7253        lua_pushcclosure(L, nodelib_aux_nil, 0);
7254        return 1;
7255    } else {
7256        halfword n = nodelib_valid_direct_from_index(L, 1);
7257        if (n) {
7258            if (lua_toboolean(L, 2)) {
7259                if (lua_toboolean(L, 3)) {
7260                    n = tex_tail_of_node_list(n);
7261                }
7262                lua_pushcclosure(L, nodelib_direct_aux_prev_content, 0);
7263            } else {
7264                lua_pushcclosure(L, nodelib_direct_aux_next_content, 0);
7265            }
7266            lua_pushinteger(L, n);
7267            lua_pushnil(L);
7268            return 3;
7269        } else {
7270            lua_pushcclosure(L, nodelib_aux_nil, 0);
7271            return 1;
7272        }
7273    }
7274}
7275
7276/* node.traverse */
7277/* node.traverseid */
7278/* node.traversechar */
7279/* node.traverseglyph */
7280/* node.traverselist */
7281
7282static int nodelib_aux_next(lua_State *L)
7283{
7284    halfword t;
7285    if (lua_isnil(L, 2)) {
7286        t = lmt_check_isnode(L, 1);
7287        lua_settop(L, 1);
7288    } else {
7289        t = lmt_check_isnode(L, 2);
7290        t = node_next(t);
7291        lua_settop(L, 2);
7292    }
7293    if (t) {
7294        nodelib_push_node_on_top(L, t);
7295        lua_pushinteger(L, node_type(t));
7296        lua_pushinteger(L, node_subtype(t));
7297        return 3;
7298    } else {
7299        lua_pushnil(L);
7300        return 1;
7301    }
7302}
7303
7304static int nodelib_aux_prev(lua_State *L)
7305{
7306    halfword t;
7307    if (lua_isnil(L, 2)) {
7308        t = lmt_check_isnode(L, 1);
7309        lua_settop(L, 1);
7310    } else {
7311        t = lmt_check_isnode(L, 2);
7312        t = node_prev(t);
7313        lua_settop(L, 2);
7314    }
7315    if (t) {
7316        nodelib_push_node_on_top(L, t);
7317        lua_pushinteger(L, node_type(t));
7318        lua_pushinteger(L, node_subtype(t));
7319        return 3;
7320    } else {
7321        lua_pushnil(L);
7322        return 1;
7323    }
7324}
7325
7326static int nodelib_userdata_traverse(lua_State *L)
7327{
7328    if (lua_isnil(L, 1)) {
7329        lua_pushcclosure(L, nodelib_aux_nil, 0);
7330        return 1;
7331    } else {
7332        halfword n = lmt_check_isnode(L, 1);
7333        if (lua_toboolean(L, 2)) {
7334            if (lua_toboolean(L, 3)) {
7335                n = tex_tail_of_node_list(n);
7336            }
7337            lua_pushcclosure(L, nodelib_aux_prev, 0);
7338        } else {
7339            lua_pushcclosure(L, nodelib_aux_next, 0);
7340        }
7341        lmt_push_node_fast(L, n);
7342        lua_pushnil(L);
7343        return 3;
7344    }
7345}
7346
7347static int nodelib_aux_next_filtered(lua_State *L)
7348{
7349    halfword t;
7350    int i = (int) lua_tointeger(L, lua_upvalueindex(1));
7351    if (lua_isnil(L, 2)) {
7352        /* first call */
7353        t = lmt_check_isnode(L, 1);
7354        lua_settop(L,1);
7355    } else {
7356        t = lmt_check_isnode(L, 2);
7357        t = node_next(t);
7358        lua_settop(L,2);
7359    }
7360    while (t && node_type(t) != i) {
7361        t = node_next(t);
7362    }
7363    if (t) {
7364        nodelib_push_node_on_top(L, t);
7365        lua_pushinteger(L, node_subtype(t));
7366        return 2;
7367    } else {
7368        lua_pushnil(L);
7369        return 1;
7370    }
7371}
7372
7373static int nodelib_aux_prev_filtered(lua_State *L)
7374{
7375    halfword t;
7376    int i = (int) lua_tointeger(L, lua_upvalueindex(1));
7377    if (lua_isnil(L, 2)) {
7378        /* first call */
7379        t = lmt_check_isnode(L, 1);
7380        lua_settop(L,1);
7381    } else {
7382        t = lmt_check_isnode(L, 2);
7383        t = node_prev(t);
7384        lua_settop(L,2);
7385    }
7386    while (t && node_type(t) != i) {
7387        t = node_prev(t);
7388    }
7389    if (t) {
7390        nodelib_push_node_on_top(L, t);
7391        lua_pushinteger(L, node_subtype(t));
7392        return 2;
7393    } else {
7394        lua_pushnil(L);
7395        return 1;
7396    }
7397}
7398
7399static int nodelib_userdata_traverse_id(lua_State *L)
7400{
7401    if (lua_isnil(L, 2)) {
7402        lua_pushcclosure(L, nodelib_aux_nil, 0);
7403        return 1;
7404    } else {
7405        halfword n = lmt_check_isnode(L, 2);
7406        if (lua_toboolean(L, 3)) {
7407            if (lua_toboolean(L, 4)) {
7408                n = tex_tail_of_node_list(n);
7409            }
7410            lua_settop(L, 1);
7411            lua_pushcclosure(L, nodelib_aux_prev_filtered, 1);
7412        } else {
7413            lua_settop(L, 1);
7414            lua_pushcclosure(L, nodelib_aux_next_filtered, 1);
7415        }
7416        lmt_push_node_fast(L, n);
7417        lua_pushnil(L);
7418        return 3;
7419    }
7420}
7421
7422/* node.direct.length */
7423/* node.direct.count */
7424
7425/*tex As with some other function that have a |last| we don't take that one into account. */
7426
7427# define length_usage common_usage
7428# define count_usage  common_usage
7429
7430static int nodelib_direct_length(lua_State *L)
7431{
7432    halfword first = nodelib_valid_direct_from_index(L, 1);
7433    halfword last = nodelib_valid_direct_from_index(L, 2);
7434    int count = 0;
7435    if (first) {
7436        while (first != last) {
7437            count++;
7438            first = node_next(first);
7439        }
7440    }
7441    lua_pushinteger(L, count);
7442    return 1;
7443}
7444
7445static int nodelib_direct_count(lua_State *L)
7446{
7447    quarterword id = lmt_toquarterword(L, 1);
7448    halfword first = nodelib_valid_direct_from_index(L, 2);
7449    halfword last = nodelib_valid_direct_from_index(L, 3);
7450    int count = 0;
7451    if (first) {
7452        while (first != last) {
7453            if (node_type(first) == id) {
7454                count++;
7455            }
7456            first = node_next(first);
7457        }
7458    }
7459    lua_pushinteger(L, count);
7460    return 1;
7461}
7462
7463/*tex A few helpers for later usage: */
7464
7465static inline int nodelib_getattribute_value(lua_State *L, halfword n, int index)
7466{
7467    halfword key = (halfword) lua_tointeger(L, index);
7468    halfword val = tex_has_attribute(n, key, unused_attribute_value);
7469    if (val == unused_attribute_value) {
7470        lua_pushnil(L);
7471    } else {
7472        lua_pushinteger(L, val);
7473    }
7474    return 1;
7475}
7476
7477static inline void nodelib_setattribute_value(lua_State *L, halfword n, int kindex, int vindex)
7478{
7479    if (lua_gettop(L) >= kindex) {
7480        halfword key = lmt_tohalfword(L, kindex);
7481        halfword val = lmt_opthalfword(L, vindex, unused_attribute_value);
7482        if (val == unused_attribute_value) {
7483            tex_unset_attribute(n, key, val);
7484        } else {
7485            tex_set_attribute(n, key, val);
7486        }
7487    } else {
7488       luaL_error(L, "incorrect number of arguments");
7489    }
7490}
7491
7492/* node.direct.getfield */
7493/* node.getfield */
7494
7495/*tex
7496
7497    The order is somewhat determined by the occurance of nodes and importance of fields. We use
7498    |somenode[9]| as interface to attributes ... 30\% faster than has_attribute (1) because there
7499    is no \LUA\ function overhead, and (2) because we already know that we deal with a node so no
7500    checking is needed. The fast typecheck is needed (lua_check... is a slow down actually).
7501
7502    This is just a reminder for me: when used in the build page routine the |last_insert_ptr| and
7503    |best_insert_ptr| are sort of tricky as the first in a list can be a fake node (zero zero list
7504    being next). Because no properties are accessed this works ok. In the getfield routines we
7505    can assume that these nodes are never seen (the pagebuilder constructs insert nodes using that
7506    data). But it is something to keep an eye on when we open up more or add callbacks. So there
7507    is a comment below.
7508
7509*/
7510
7511typedef enum lua_field_errors {
7512    lua_no_field_error,
7513    lua_set_field_error,
7514    lua_get_field_error,
7515} lua_field_errors ;
7516
7517static int last_field_error   = lua_no_field_error;
7518static int ignore_field_error = 0;
7519
7520static int nodelib_common_getfield(lua_State *L, int direct, halfword n)
7521{
7522    last_field_error = lua_no_field_error;
7523    switch (lua_type(L, 2)) {
7524        case LUA_TNUMBER:
7525            {
7526                return nodelib_getattribute_value(L, n, 2);
7527            }
7528        case LUA_TSTRING:
7529            {
7530                const char *s = lua_tostring(L, 2);
7531                int t = node_type(n);
7532                if (lua_key_eq(s, id)) {
7533                    lua_pushinteger(L, t);
7534                } else if (lua_key_eq(s, next)) {
7535                    if (tex_nodetype_has_next(t)) {
7536                        nodelib_push_direct_or_node(L, direct, node_next(n));
7537                    } else {
7538                         goto CANTGET;
7539                    }
7540                } else if (lua_key_eq(s, prev)) {
7541                    if (tex_nodetype_has_prev(t)) {
7542                        nodelib_push_direct_or_node(L, direct, node_prev(n));
7543                    } else {
7544                         goto CANTGET;
7545                    }
7546                } else if (lua_key_eq(s, attr)) {
7547                    if (tex_nodetype_has_attributes(t)) {
7548                        nodelib_push_direct_or_node(L, direct, node_attr(n));
7549                    } else {
7550                         goto CANTGET;
7551                    }
7552                } else if (lua_key_eq(s, subtype)) {
7553                    if (tex_nodetype_has_subtype(t)) {
7554                        lua_pushinteger(L, node_subtype(n));
7555                    } else {
7556                         goto CANTGET;
7557                    }
7558                } else {
7559                    switch(t) {
7560                        case glyph_node:
7561                            if (lua_key_eq(s, font)) {
7562                                lua_pushinteger(L, glyph_font(n));
7563                            } else if (lua_key_eq(s, char)) {
7564                                lua_pushinteger(L, glyph_character(n));
7565                            } else if (lua_key_eq(s, xoffset)) {
7566                                lua_pushinteger(L, glyph_x_offset(n));
7567                            } else if (lua_key_eq(s, yoffset)) {
7568                                lua_pushinteger(L, glyph_y_offset(n));
7569                            } else if (lua_key_eq(s, data)) {
7570                                lua_pushinteger(L, glyph_data(n));
7571                            } else if (lua_key_eq(s, width)) {
7572                                lua_pushinteger(L, tex_glyph_width(n));
7573                            } else if (lua_key_eq(s, height)) {
7574                                lua_pushinteger(L, tex_glyph_height(n));
7575                            } else if (lua_key_eq(s, depth)) {
7576                             // lua_pushinteger(L, char_depth_from_glyph(n));
7577                                lua_pushinteger(L, tex_glyph_depth(n));
7578                            } else if (lua_key_eq(s, total)) {
7579                             // lua_pushinteger(L, char_total_from_glyph(n));
7580                                lua_pushinteger(L, tex_glyph_total(n));
7581                            } else if (lua_key_eq(s, scale)) {
7582                                lua_pushinteger(L, glyph_scale(n));
7583                            } else if (lua_key_eq(s, xscale)) {
7584                                lua_pushinteger(L, glyph_x_scale(n));
7585                            } else if (lua_key_eq(s, yscale)) {
7586                                lua_pushinteger(L, glyph_y_scale(n));
7587                            } else if (lua_key_eq(s, expansion)) {
7588                                lua_pushinteger(L, glyph_expansion(n));
7589                            } else if (lua_key_eq(s, state)) {
7590                                lua_pushinteger(L, get_glyph_state(n));
7591                            } else if (lua_key_eq(s, script)) {
7592                                lua_pushinteger(L, get_glyph_script(n));
7593                            } else if (lua_key_eq(s, language)) {
7594                                lua_pushinteger(L, get_glyph_language(n));
7595                            } else if (lua_key_eq(s, lhmin)) {
7596                                lua_pushinteger(L, get_glyph_lhmin(n));
7597                            } else if (lua_key_eq(s, rhmin)) {
7598                                lua_pushinteger(L, get_glyph_rhmin(n));
7599                            } else if (lua_key_eq(s, left)) {
7600                                lua_pushinteger(L, get_glyph_left(n));
7601                            } else if (lua_key_eq(s, right)) {
7602                                lua_pushinteger(L, get_glyph_right(n));
7603                            } else if (lua_key_eq(s, slant)) {
7604                                lua_pushinteger(L, glyph_slant(n));
7605                            } else if (lua_key_eq(s, weight)) {
7606                                lua_pushinteger(L, glyph_weight(n));
7607                            } else if (lua_key_eq(s, uchyph)) {
7608                                lua_pushinteger(L, get_glyph_uchyph(n));
7609                            } else if (lua_key_eq(s, hyphenate)) {
7610                                lua_pushinteger(L, get_glyph_hyphenate(n));
7611                            } else if (lua_key_eq(s, options)) {
7612                                lua_pushinteger(L, get_glyph_options(n));
7613                            } else if (lua_key_eq(s, discpart)) {
7614                                lua_pushinteger(L, get_glyph_discpart(n));
7615                            } else if (lua_key_eq(s, protected)) {
7616                                lua_pushinteger(L, glyph_protected(n));
7617                            } else if (lua_key_eq(s, properties)) {
7618                                lua_pushinteger(L, glyph_properties(n));
7619                            } else if (lua_key_eq(s, group)) {
7620                                lua_pushinteger(L, glyph_group(n));
7621                            } else if (lua_key_eq(s, index)) {
7622                                lua_pushinteger(L, glyph_index(n));
7623                            } else if (lua_key_eq(s, control)) {
7624                                lua_pushinteger(L, get_glyph_control(n));
7625                            } else {
7626                                goto CANTGET;
7627                            }
7628                            break;
7629                        case hlist_node:
7630                        case vlist_node:
7631                            /* candidates: whd (width,height,depth) */
7632                            if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
7633                                nodelib_push_direct_or_node_node_prev(L, direct, box_list(n));
7634                            } else if (lua_key_eq(s, width)) {
7635                                lua_pushinteger(L, box_width(n));
7636                            } else if (lua_key_eq(s, height)) {
7637                                lua_pushinteger(L, box_height(n));
7638                            } else if (lua_key_eq(s, depth)) {
7639                                lua_pushinteger(L, box_depth(n));
7640                            } else if (lua_key_eq(s, total)) {
7641                                lua_pushinteger(L, box_total(n));
7642                            } else if (lua_key_eq(s, direction)) {
7643                                lua_pushinteger(L, checked_direction_value(box_dir(n)));
7644                            } else if (lua_key_eq(s, shift)) {
7645                                lua_pushinteger(L, box_shift_amount(n));
7646                            } else if (lua_key_eq(s, glueorder)) {
7647                                lua_pushinteger(L, box_glue_order(n));
7648                            } else if (lua_key_eq(s, gluesign)) {
7649                                lua_pushinteger(L, box_glue_sign(n));
7650                            } else if (lua_key_eq(s, glueset)) {
7651                                lua_pushnumber(L, (double) box_glue_set(n)); /* float */
7652                            } else if (lua_key_eq(s, geometry)) {
7653                                lua_pushinteger(L, box_geometry(n));
7654                            } else if (lua_key_eq(s, orientation)) {
7655                                lua_pushinteger(L, box_orientation(n));
7656                            } else if (lua_key_eq(s, anchor)) {
7657                                lua_pushinteger(L, box_anchor(n));
7658                            } else if (lua_key_eq(s, source)) {
7659                                lua_pushinteger(L, box_source_anchor(n));
7660                            } else if (lua_key_eq(s, target)) {
7661                                lua_pushinteger(L, box_target_anchor(n));
7662                            } else if (lua_key_eq(s, xoffset)) {
7663                                lua_pushinteger(L, box_x_offset(n));
7664                            } else if (lua_key_eq(s, yoffset)) {
7665                                lua_pushinteger(L, box_y_offset(n));
7666                            } else if (lua_key_eq(s, woffset)) {
7667                                lua_pushinteger(L, box_w_offset(n));
7668                            } else if (lua_key_eq(s, hoffset)) {
7669                                lua_pushinteger(L, box_h_offset(n));
7670                            } else if (lua_key_eq(s, doffset)) {
7671                                lua_pushinteger(L, box_d_offset(n));
7672                            } else if (lua_key_eq(s, pre)) {
7673                                nodelib_push_direct_or_node(L, direct, box_pre_migrated(n));
7674                            } else if (lua_key_eq(s, post)) {
7675                                nodelib_push_direct_or_node(L, direct, box_post_migrated(n));
7676                            } else if (lua_key_eq(s, state)) {
7677                                lua_pushinteger(L, box_package_state(n));
7678                            } else if (lua_key_eq(s, index)) {
7679                                lua_pushinteger(L, box_index(n));
7680                            } else if (lua_key_eq(s, except)) {
7681                                nodelib_push_direct_or_node(L, direct, box_except(n));
7682                            } else {
7683                                goto CANTGET;
7684                            }
7685                            break;
7686                        case disc_node:
7687                            if (lua_key_eq(s, pre)) {
7688                                nodelib_push_direct_or_node(L, direct, disc_pre_break_head(n));
7689                            } else if (lua_key_eq(s, post)) {
7690                                nodelib_push_direct_or_node(L, direct, disc_post_break_head(n));
7691                            } else if (lua_key_eq(s, replace)) {
7692                                nodelib_push_direct_or_node(L, direct, disc_no_break_head(n));
7693                            } else if (lua_key_eq(s, penalty)) {
7694                                lua_pushinteger(L, disc_penalty(n));
7695                            } else if (lua_key_eq(s, options)) {
7696                                lua_pushinteger(L, disc_options(n));
7697                            } else if (lua_key_eq(s, class)) {
7698                                lua_pushinteger(L, disc_class(n));
7699                            } else {
7700                                goto CANTGET;
7701                            }
7702                            break;
7703                        case glue_node:
7704                            if (lua_key_eq(s, width)) {
7705                                lua_pushinteger(L, glue_amount(n));
7706                            } else if (lua_key_eq(s, stretch)) {
7707                                lua_pushinteger(L, glue_stretch(n));
7708                            } else if (lua_key_eq(s, shrink)) {
7709                                lua_pushinteger(L, glue_shrink(n));
7710                            } else if (lua_key_eq(s, stretchorder)) {
7711                                lua_pushinteger(L, glue_stretch_order(n));
7712                            } else if (lua_key_eq(s, shrinkorder)) {
7713                                lua_pushinteger(L, glue_shrink_order(n));
7714                            } else if (lua_key_eq(s, leader)) {
7715                                nodelib_push_direct_or_node(L, direct, glue_leader_ptr(n));
7716                            } else if (lua_key_eq(s, font)) {
7717                                lua_pushinteger(L, glue_font(n));
7718                            } else if (lua_key_eq(s, data)) {
7719                                lua_pushinteger(L, glue_data(n));
7720                            } else if (lua_key_eq(s, options)) {
7721                                lua_pushinteger(L, glue_options(n));
7722                            } else {
7723                                goto CANTGET;
7724                            }
7725                            break;
7726                        case kern_node:
7727                            if (lua_key_eq(s, kern)) {
7728                                lua_pushinteger(L, kern_amount(n));
7729                            } else if (lua_key_eq(s, expansion)) {
7730                                lua_pushinteger(L, kern_expansion(n));
7731                            } else {
7732                                goto CANTGET;
7733                            }
7734                            break;
7735                        case penalty_node:
7736                            if (lua_key_eq(s, penalty)) {
7737                                lua_pushinteger(L, penalty_amount(n));
7738                            } else if (lua_key_eq(s, options)) {
7739                                lua_pushinteger(L, penalty_options(n));
7740                            } else {
7741                                goto CANTGET;
7742                            }
7743                            break;
7744                        case rule_node:
7745                            /* candidates: whd (width,height,depth) */
7746                            if (lua_key_eq(s, width)) {
7747                                lua_pushinteger(L, rule_width(n));
7748                            } else if (lua_key_eq(s, height)) {
7749                                lua_pushinteger(L, rule_height(n));
7750                            } else if (lua_key_eq(s, depth)) {
7751                                lua_pushinteger(L, rule_depth(n));
7752                            } else if (lua_key_eq(s, total)) {
7753                                lua_pushinteger(L, rule_total(n));
7754                            } else if (lua_key_eq(s, xoffset)) {
7755                                lua_pushinteger(L, rule_x_offset(n));
7756                            } else if (lua_key_eq(s, yoffset)) {
7757                                lua_pushinteger(L, rule_y_offset(n));
7758                            } else if (lua_key_eq(s, left)) {
7759                                lua_pushinteger(L, tex_get_rule_left(n));
7760                            } else if (lua_key_eq(s, right)) {
7761                                lua_pushinteger(L, tex_get_rule_right(n));
7762                            } else if (lua_key_eq(s, on)) {
7763                                lua_pushinteger(L, tex_get_rule_on(n));
7764                            } else if (lua_key_eq(s, off)) {
7765                                lua_pushinteger(L, tex_get_rule_off(n));
7766                            } else if (lua_key_eq(s, data)) {
7767                                lua_pushinteger(L, rule_data(n));
7768                            } else if (lua_key_eq(s, options)) {
7769                                lua_pushinteger(L, rule_options(n));
7770                            } else if (lua_key_eq(s, font)) {
7771                                lua_pushinteger(L, tex_get_rule_font(n, text_style));
7772                            } else if (lua_key_eq(s, fam)) {
7773                                lua_pushinteger(L, tex_get_rule_font(n, text_style));
7774                            } else if (lua_key_eq(s, char)) {
7775                                lua_pushinteger(L, rule_strut_character(n));
7776                            } else {
7777                                goto CANTGET;
7778                            }
7779                            break;
7780                        case dir_node:
7781                            if (lua_key_eq(s, direction)) {
7782                                lua_pushinteger(L, dir_direction(n));
7783                            } else if (lua_key_eq(s, level)) {
7784                                lua_pushinteger(L, dir_level(n));
7785                            } else {
7786                                goto CANTGET;
7787                            }
7788                            break;
7789                        case whatsit_node:
7790                            goto CANTGET;
7791                        case par_node:
7792                            /* not all of them here */
7793                            if (lua_key_eq(s, interlinepenalty)) {
7794                                lua_pushinteger(L, tex_get_local_interline_penalty(n));
7795                            } else if (lua_key_eq(s, brokenpenalty)) {
7796                                lua_pushinteger(L, tex_get_local_broken_penalty(n));
7797                            } else if (lua_key_eq(s, tolerance)) {
7798                                lua_pushinteger(L, tex_get_local_tolerance(n));
7799                            } else if (lua_key_eq(s, pretolerance)) {
7800                                lua_pushinteger(L, tex_get_local_pre_tolerance(n));
7801                            } else if (lua_key_eq(s, direction)) {
7802                                lua_pushinteger(L, par_dir(n));
7803                            } else if (lua_key_eq(s, leftbox)) {
7804                                nodelib_push_direct_or_node(L, direct, par_box_left(n));
7805                            } else if (lua_key_eq(s, leftboxwidth)) {
7806                                lua_pushinteger(L, tex_get_local_left_width(n));
7807                            } else if (lua_key_eq(s, rightbox)) {
7808                                nodelib_push_direct_or_node(L, direct, par_box_right(n));
7809                            } else if (lua_key_eq(s, rightboxwidth)) {
7810                                lua_pushinteger(L, tex_get_local_right_width(n));
7811                            } else if (lua_key_eq(s, middlebox)) {
7812                                nodelib_push_direct_or_node(L, direct, par_box_middle(n));
7813                            } else {
7814                                goto CANTGET;
7815                            }
7816                            break;
7817                        case math_char_node:
7818                        case math_text_char_node:
7819                            if (lua_key_eq(s, fam)) {
7820                                lua_pushinteger(L, kernel_math_family(n));
7821                            } else if (lua_key_eq(s, char)) {
7822                                lua_pushinteger(L, kernel_math_character(n));
7823                            } else if (lua_key_eq(s, font)) {
7824                                lua_pushinteger(L, tex_fam_fnt(kernel_math_family(n), 0));
7825                            } else if (lua_key_eq(s, options)) {
7826                                lua_pushinteger(L, kernel_math_options(n));
7827                            } else if (lua_key_eq(s, properties)) {
7828                                lua_pushinteger(L, kernel_math_properties(n));
7829                            } else if (lua_key_eq(s, group)) {
7830                                lua_pushinteger(L, kernel_math_group(n));
7831                            } else if (lua_key_eq(s, index)) {
7832                                lua_pushinteger(L, kernel_math_index(n));
7833                            } else {
7834                                goto CANTGET;
7835                            }
7836                            break;
7837                        case mark_node:
7838                            if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
7839                                lua_pushinteger(L, mark_index(n));
7840                            } else if (lua_key_eq(s, data) || lua_key_eq(s, mark)) {
7841                                if (lua_toboolean(L, 3)) {
7842                                    lmt_token_list_to_luastring(L, mark_ptr(n), 0, 0, 0);
7843                                } else {
7844                                    lmt_token_list_to_lua(L, mark_ptr(n));
7845                                }
7846                            } else {
7847                                goto CANTGET;
7848                            }
7849                            break;
7850                        case insert_node:
7851                            if (lua_key_eq(s, index)) {
7852                                lua_pushinteger(L, insert_index(n));
7853                            } else if (lua_key_eq(s, cost)) {
7854                                lua_pushinteger(L, insert_float_cost(n));
7855                            } else if (lua_key_eq(s, depth)) {
7856                                lua_pushinteger(L, insert_max_depth(n));
7857                            } else if (lua_key_eq(s, height) || lua_key_eq(s, total)) {
7858                                lua_pushinteger(L, insert_total_height(n));
7859                            } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
7860                                nodelib_push_direct_or_node_node_prev(L, direct, insert_list(n));
7861                            } else if (lua_key_eq(s, data)) {
7862                                lua_pushinteger(L, insert_identifier(n));
7863                            } else if (lua_key_eq(s, lineheight)) {
7864                                lua_pushinteger(L, insert_line_height(n));
7865                            } else if (lua_key_eq(s, linedepth)) {
7866                                lua_pushinteger(L, insert_line_depth(n));
7867                            } else if (lua_key_eq(s, stretch)) {
7868                                lua_pushinteger(L, insert_stretch(n));
7869                            } else if (lua_key_eq(s, shrink)) {
7870                                lua_pushinteger(L, insert_shrink(n));
7871                            } else {
7872                                goto CANTGET;
7873                            }
7874                            break;
7875                        case math_node:
7876                            if (lua_key_eq(s, surround)) {
7877                                lua_pushinteger(L, math_surround(n));
7878                            } else if (lua_key_eq(s, width)) {
7879                                lua_pushinteger(L, math_amount(n));
7880                            } else if (lua_key_eq(s, stretch)) {
7881                                lua_pushinteger(L, math_stretch(n));
7882                            } else if (lua_key_eq(s, shrink)) {
7883                                lua_pushinteger(L, math_shrink(n));
7884                            } else if (lua_key_eq(s, stretchorder)) {
7885                                lua_pushinteger(L, math_stretch_order(n));
7886                            } else if (lua_key_eq(s, shrinkorder)) {
7887                                lua_pushinteger(L, math_shrink_order(n));
7888                            } else if (lua_key_eq(s, penalty)) {
7889                                lua_pushinteger(L, math_penalty(n));
7890                            } else if (lua_key_eq(s, options)) {
7891                                lua_pushinteger(L, math_options(n));
7892                            } else {
7893                                goto CANTGET;
7894                            }
7895                            break;
7896                        case style_node:
7897                            if (lua_key_eq(s, style)) {
7898                                lmt_push_math_style_name(L, style_style(n));
7899                            } else {
7900                                goto CANTGET;
7901                            }
7902                            break;
7903                        case parameter_node:
7904                            if (lua_key_eq(s, style)) {
7905                                lmt_push_math_style_name(L, parameter_style(n));
7906                            } else if (lua_key_eq(s, name)) {
7907                                lmt_push_math_parameter(L, parameter_name(n));
7908                            } else if (lua_key_eq(s, value)) {
7909                                halfword code = parameter_name(n);
7910                                if (code < 0 || code >= math_parameter_last) {
7911                                    /* error */
7912                                    lua_pushnil(L);
7913                                } else if (math_parameter_value_type(code)) {
7914                                    /* todo, see tex_getmathparm */
7915                                    lua_pushnil(L);
7916                                } else {
7917                                    lua_pushinteger(L, parameter_value(n));
7918                                }
7919                            } else {
7920                                goto CANTGET;
7921                            }
7922                            break;
7923                        case simple_noad:
7924                        case radical_noad:
7925                        case fraction_noad:
7926                        case accent_noad:
7927                        case fence_noad:
7928                            if (lua_key_eq(s, nucleus)) {
7929                                nodelib_push_direct_or_nil(L, noad_nucleus(n));
7930                            } else if (lua_key_eq(s, sub)) {
7931                                nodelib_push_direct_or_nil(L, noad_subscr(n));
7932                            } else if (lua_key_eq(s, sup)) {
7933                                nodelib_push_direct_or_nil(L, noad_supscr(n));
7934                            } else if (lua_key_eq(s, prime)) {
7935                                nodelib_push_direct_or_nil(L, noad_prime(n));
7936                            } else if (lua_key_eq(s, subpre)) {
7937                                nodelib_push_direct_or_nil(L, noad_subprescr(n));
7938                            } else if (lua_key_eq(s, suppre)) {
7939                                nodelib_push_direct_or_nil(L, noad_supprescr(n));
7940                            } else if (lua_key_eq(s, options)) {
7941                                lua_pushinteger(L, noad_options(n));
7942                            } else if (lua_key_eq(s, source)) {
7943                                lua_pushinteger(L, noad_source(n));
7944                            } else if (lua_key_eq(s, scriptorder)) {
7945                                lua_pushinteger(L, noad_script_order(n));
7946                            } else if (lua_key_eq(s, class)) {
7947                                lua_pushinteger(L, get_noad_main_class(n));
7948                                lua_pushinteger(L, get_noad_left_class(n));
7949                                lua_pushinteger(L, get_noad_right_class(n));
7950                                return 3;
7951                            } else if (lua_key_eq(s, fam)) {
7952                                lua_pushinteger(L, noad_family(n));
7953                            } else {
7954                                switch(t) {
7955                                    case simple_noad:
7956                                         goto CANTGET;
7957                                    case radical_noad:
7958                                        if (lua_key_eq(s, left) || lua_key_eq(s, delimiter)) {
7959                                            nodelib_push_direct_or_node(L, direct, radical_left_delimiter(n));
7960                                        } else if (lua_key_eq(s, right)) {
7961                                            nodelib_push_direct_or_node(L, direct, radical_right_delimiter(n));
7962                                        } else if (lua_key_eq(s, top)) {
7963                                            nodelib_push_direct_or_node(L, direct, radical_top_delimiter(n));
7964                                        } else if (lua_key_eq(s, bottom)) {
7965                                            nodelib_push_direct_or_node(L, direct, radical_bottom_delimiter(n));
7966                                        } else if (lua_key_eq(s, degree)) {
7967                                            nodelib_push_direct_or_node(L, direct, radical_degree(n));
7968                                        } else if (lua_key_eq(s, width)) {
7969                                            lua_pushinteger(L, noad_width(n));
7970                                        } else {
7971                                             goto CANTGET;
7972                                        }
7973                                        break;
7974                                    case fraction_noad:
7975                                        if (lua_key_eq(s, width)) {
7976                                            lua_pushinteger(L, fraction_rule_thickness(n));
7977                                        } else if (lua_key_eq(s, numerator)) {
7978                                            nodelib_push_direct_or_nil(L, fraction_numerator(n));
7979                                        } else if (lua_key_eq(s, denominator)) {
7980                                            nodelib_push_direct_or_nil(L, fraction_denominator(n));
7981                                        } else if (lua_key_eq(s, left)) {
7982                                            nodelib_push_direct_or_nil(L, fraction_left_delimiter(n));
7983                                        } else if (lua_key_eq(s, right)) {
7984                                            nodelib_push_direct_or_nil(L, fraction_right_delimiter(n));
7985                                        } else if (lua_key_eq(s, middle)) {
7986                                            nodelib_push_direct_or_nil(L, fraction_middle_delimiter(n));
7987                                        } else {
7988                                             goto CANTGET;
7989                                        }
7990                                        break;
7991                                    case accent_noad:
7992                                        if (lua_key_eq(s, top) || lua_key_eq(s, topaccent)) {
7993                                            nodelib_push_direct_or_node(L, direct, accent_top_character(n));
7994                                        } else if (lua_key_eq(s, bottom) || lua_key_eq(s, bottomaccent)) {
7995                                            nodelib_push_direct_or_node(L, direct, accent_bottom_character(n));
7996                                        } else if (lua_key_eq(s, middle) || lua_key_eq(s, overlayaccent)) {
7997                                            nodelib_push_direct_or_node(L, direct, accent_middle_character(n));
7998                                        } else if (lua_key_eq(s, fraction)) {
7999                                            lua_pushinteger(L, accent_fraction(n));
8000                                        } else {
8001                                             goto CANTGET;
8002                                        }
8003                                        break;
8004                                    case fence_noad:
8005                                        if (lua_key_eq(s, delimiter)) {
8006                                            nodelib_push_direct_or_node(L, direct, fence_delimiter(n));
8007                                        } else if (lua_key_eq(s, top)) {
8008                                            nodelib_push_direct_or_node(L, direct, fence_delimiter_top(n));
8009                                        } else if (lua_key_eq(s, bottom)) {
8010                                            nodelib_push_direct_or_node(L, direct, fence_delimiter_bottom(n));
8011                                        } else if (lua_key_eq(s, italic)) {
8012                                            lua_pushinteger(L, noad_italic(n));
8013                                        } else if (lua_key_eq(s, height)) {
8014                                            lua_pushinteger(L, noad_height(n));
8015                                        } else if (lua_key_eq(s, depth)) {
8016                                            lua_pushinteger(L, noad_depth(n));
8017                                        } else if (lua_key_eq(s, total)) {
8018                                            lua_pushinteger(L, noad_total(n));
8019                                        } else if (lua_key_eq(s, variant)) {
8020                                            lua_pushinteger(L, fence_delimiter_variant(n));
8021                                        } else {
8022                                             goto CANTGET;
8023                                        }
8024                                        break;
8025                                }
8026                            }
8027                            break;
8028                        case delimiter_node:
8029                            if (lua_key_eq(s, smallfamily)) {
8030                                lua_pushinteger(L, delimiter_small_family(n));
8031                            } else if (lua_key_eq(s, smallchar)) {
8032                                lua_pushinteger(L, delimiter_small_character(n));
8033                            } else if (lua_key_eq(s, largefamily)) {
8034                                lua_pushinteger(L, delimiter_large_family(n));
8035                            } else if (lua_key_eq(s, largechar)) {
8036                                lua_pushinteger(L, delimiter_large_character(n));
8037                            } else {
8038                                 goto CANTGET;
8039                            }
8040                            break;
8041                        case sub_box_node:
8042                        case sub_mlist_node:
8043                            if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8044                                nodelib_push_direct_or_node_node_prev(L, direct,  kernel_math_list(n));
8045                            } else {
8046                                 goto CANTGET;
8047                            }
8048                            break;
8049                        case split_node:
8050                            if (lua_key_eq(s, index)) {
8051                                lua_push_integer(L, split_insert_index(n));
8052                            } else if (lua_key_eq(s, lastinsert)) {
8053                                nodelib_push_direct_or_node(L, direct, split_last_insert(n)); /* see comment */
8054                            } else if (lua_key_eq(s, bestinsert)) {
8055                                nodelib_push_direct_or_node(L, direct, split_best_insert(n)); /* see comment */
8056                            } else if (lua_key_eq(s, broken)) {
8057                                nodelib_push_direct_or_node(L, direct, split_broken(n));
8058                            } else if (lua_key_eq(s, brokeninsert)) {
8059                                nodelib_push_direct_or_node(L, direct, split_broken_insert(n));
8060                            } else {
8061                                 goto CANTGET;
8062                            }
8063                            break;
8064                        case choice_node:
8065                            /*tex We could check and combine some here but who knows how things evolve. */
8066                            if (lua_key_eq(s, display)) {
8067                                nodelib_push_direct_or_node(L, direct, choice_display_mlist(n));
8068                            } else if (lua_key_eq(s, text)) {
8069                                nodelib_push_direct_or_node(L, direct, choice_text_mlist(n));
8070                            } else if (lua_key_eq(s, script)) {
8071                                nodelib_push_direct_or_node(L, direct, choice_script_mlist(n));
8072                            } else if (lua_key_eq(s, scriptscript)) {
8073                                nodelib_push_direct_or_node(L, direct, choice_script_script_mlist(n));
8074                            } else if (lua_key_eq(s, pre)) {
8075                                nodelib_push_direct_or_node(L, direct, choice_pre_break(n));
8076                            } else if (lua_key_eq(s, post)) {
8077                                nodelib_push_direct_or_node(L, direct, choice_post_break(n));
8078                            } else if (lua_key_eq(s, replace)) {
8079                                nodelib_push_direct_or_node(L, direct, choice_no_break(n));
8080                            } else {
8081                                 goto CANTGET;
8082                            }
8083                            break;
8084                        case attribute_node:
8085                            switch (node_subtype(n)) {
8086                                case attribute_list_subtype:
8087                                    if (lua_key_eq(s, count)) {
8088                                        lua_pushinteger(L, attribute_count(n));
8089                                    } else if (lua_key_eq(s, data)) {
8090                                        nodelib_push_attribute_data(L, node_attr(n));
8091                                    } else {
8092                                        goto CANTGET;
8093                                    }
8094                                    break;
8095                                case attribute_value_subtype:
8096                                    if (lua_key_eq(s, index) || lua_key_eq(s, number)) {
8097                                        lua_pushinteger(L, attribute_index(n));
8098                                    } else if (lua_key_eq(s, value)) {
8099                                        lua_pushinteger(L, attribute_value(n));
8100                                    } else {
8101                                        goto CANTGET;
8102                                    }
8103                                    break;
8104                                default:
8105                                    goto CANTGET;
8106                            }
8107                            break;
8108                        case adjust_node:
8109                            if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8110                                nodelib_push_direct_or_node_node_prev(L, direct, adjust_list(n));
8111                            } else if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
8112                                lua_pushinteger(L, adjust_index(n));
8113                            } else {
8114                                goto CANTGET;
8115                            }
8116                            break;
8117                        case unset_node:
8118                            if (lua_key_eq(s, width)) {
8119                                lua_pushinteger(L, box_width(n));
8120                            } else if (lua_key_eq(s, height)) {
8121                                lua_pushinteger(L, box_height(n));
8122                            } else if (lua_key_eq(s, depth)) {
8123                                lua_pushinteger(L, box_depth(n));
8124                            } else if (lua_key_eq(s, total)) {
8125                                lua_pushinteger(L, box_total(n));
8126                            } else if (lua_key_eq(s, direction)) {
8127                                lua_pushinteger(L, checked_direction_value(box_dir(n)));
8128                            } else if (lua_key_eq(s, shrink)) {
8129                                lua_pushinteger(L, box_glue_shrink(n));
8130                            } else if (lua_key_eq(s, glueorder)) {
8131                                lua_pushinteger(L, box_glue_order(n));
8132                            } else if (lua_key_eq(s, gluesign)) {
8133                                lua_pushinteger(L, box_glue_sign(n));
8134                            } else if (lua_key_eq(s, stretch)) {
8135                                lua_pushinteger(L, box_glue_stretch(n));
8136                            } else if (lua_key_eq(s, count)) {
8137                                lua_pushinteger(L, box_span_count(n));
8138                            } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8139                                nodelib_push_direct_or_node_node_prev(L, direct, box_list(n));
8140                            } else {
8141                                goto CANTGET;
8142                            }
8143                            break;
8144                        /*
8145                        case attribute_list_node:
8146                            lua_pushnil(L);
8147                            break;
8148                        */
8149                        case boundary_node:
8150                            if (lua_key_eq(s, data) || lua_key_eq(s, value)) {
8151                                lua_pushinteger(L, boundary_data(n));
8152                            } else if (lua_key_eq(s, reserved)) {
8153                                lua_pushinteger(L, boundary_reserved(n));
8154                            } else {
8155                                goto CANTGET;
8156                            }
8157                            break;
8158                        case glue_spec_node:
8159                            if (lua_key_eq(s, width)) {
8160                                lua_pushinteger(L, glue_amount(n));
8161                            } else if (lua_key_eq(s, stretch)) {
8162                                lua_pushinteger(L, glue_stretch(n));
8163                            } else if (lua_key_eq(s, shrink)) {
8164                                lua_pushinteger(L, glue_shrink(n));
8165                            } else if (lua_key_eq(s, stretchorder)) {
8166                                lua_pushinteger(L, glue_stretch_order(n));
8167                            } else if (lua_key_eq(s, shrinkorder)) {
8168                                lua_pushinteger(L, glue_shrink_order(n));
8169                            } else {
8170                                last_field_error = lua_get_field_error;
8171                                lua_pushnil(L);
8172                            }
8173                            break;
8174                        default:
8175                            goto CANTGET;
8176                    }
8177                }
8178                break;
8179            }
8180        default:
8181            {
8182              CANTGET:
8183                last_field_error = lua_get_field_error;
8184                lua_pushnil(L);
8185                break;
8186            }
8187    }
8188    return 1;
8189}
8190
8191# define getfield_usage common_usage
8192
8193static int nodelib_direct_getfield(lua_State *L)
8194{
8195    halfword n = nodelib_valid_direct_from_index(L, 1);
8196    if (n) {
8197        return nodelib_common_getfield(L, 1, n);
8198    } else {
8199        lua_pushnil(L);
8200        return 1;
8201    }
8202}
8203
8204static int nodelib_userdata_index(lua_State *L)
8205{
8206    halfword n = *((halfword *) lua_touserdata(L, 1));
8207    if (n) {
8208        return nodelib_common_getfield(L, 0, n);
8209    } else {
8210        lua_pushnil(L);
8211        return 1;
8212    }
8213}
8214
8215static int nodelib_userdata_getfield(lua_State *L)
8216{
8217    halfword n = lmt_maybe_isnode(L, 1);
8218    if (n) {
8219        return nodelib_common_getfield(L, 0, n);
8220    } else {
8221        lua_pushnil(L);
8222        return 1;
8223    }
8224}
8225
8226/* node.setfield */
8227/* node.direct.setfield */
8228
8229/*
8230    We used to check for glue_spec nodes in some places but if you do such a you have it coming
8231    anyway.
8232*/
8233
8234static int nodelib_common_setfield(lua_State *L, int direct, halfword n)
8235{
8236    last_field_error = lua_no_field_error;
8237    switch (lua_type(L, 2)) {
8238        case LUA_TNUMBER:
8239            {
8240                nodelib_setattribute_value(L, n, 2, 3);
8241                break;
8242            }
8243        case LUA_TSTRING:
8244            {
8245                const char *s = lua_tostring(L, 2);
8246                int t = node_type(n);
8247                if (lua_key_eq(s, next)) {
8248                    if (tex_nodetype_has_next(t)) {
8249                        node_next(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8250                    } else {
8251                        goto CANTSET;
8252                    }
8253                } else if (lua_key_eq(s, prev)) {
8254                    if (tex_nodetype_has_prev(t)) {
8255                        node_prev(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8256                    } else {
8257                        goto CANTSET;
8258                    }
8259                } else if (lua_key_eq(s, attr)) {
8260                    if (tex_nodetype_has_attributes(t)) {
8261                        tex_attach_attribute_list_attribute(n, nodelib_direct_or_node_from_index(L, direct, 3));
8262                    } else {
8263                        goto CANTSET;
8264                    }
8265                } else if (lua_key_eq(s, subtype)) {
8266                    if (tex_nodetype_has_subtype(t)) {
8267                        node_subtype(n) = lmt_toquarterword(L, 3);
8268                    } else {
8269                        goto CANTSET;
8270                    }
8271                } else {
8272                    switch(t) {
8273                        case glyph_node:
8274                            if (lua_key_eq(s, font)) {
8275                                glyph_font(n) = tex_checked_font(lmt_tohalfword(L, 3));
8276                            } else if (lua_key_eq(s, char)) {
8277                                glyph_character(n) = lmt_tohalfword(L, 3);
8278                            } else if (lua_key_eq(s, xoffset)) {
8279                                glyph_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
8280                            } else if (lua_key_eq(s, yoffset)) {
8281                                glyph_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
8282                            } else if (lua_key_eq(s, scale)) {
8283                                 glyph_scale(n) = (halfword) lmt_roundnumber(L, 3);
8284                                 if (! glyph_scale(n)) {
8285                                     glyph_scale(n) = scaling_factor;
8286                                 }
8287                            } else if (lua_key_eq(s, xscale)) {
8288                                 glyph_x_scale(n) = (halfword) lmt_roundnumber(L, 3);
8289                                 if (! glyph_x_scale(n)) {
8290                                     glyph_x_scale(n) = scaling_factor;
8291                                 }
8292                            } else if (lua_key_eq(s, yscale)) {
8293                                 glyph_y_scale(n) = (halfword) lmt_roundnumber(L, 3);
8294                                 if (! glyph_y_scale(n)) {
8295                                     glyph_y_scale(n) = scaling_factor;
8296                                 }
8297                            } else if (lua_key_eq(s, data)) {
8298                                glyph_data(n) = lmt_opthalfword(L, 3, unused_attribute_value);
8299                            } else if (lua_key_eq(s, expansion)) {
8300                                glyph_expansion(n) = (halfword) lmt_roundnumber(L, 3);
8301                            } else if (lua_key_eq(s, state)) {
8302                                set_glyph_state(n, lmt_tohalfword(L, 3));
8303                            } else if (lua_key_eq(s, script)) {
8304                                set_glyph_script(n, lmt_tohalfword(L, 3));
8305                            } else if (lua_key_eq(s, language)) {
8306                                set_glyph_language(n, lmt_tohalfword(L, 3));
8307                            } else if (lua_key_eq(s, left)) {
8308                                set_glyph_left(n, lmt_tohalfword(L, 3));
8309                            } else if (lua_key_eq(s, right)) {
8310                                set_glyph_right(n, lmt_tohalfword(L, 3));
8311                            } else if (lua_key_eq(s, slant)) {
8312                                set_glyph_slant(n, lmt_tohalfword(L, 3));
8313                            } else if (lua_key_eq(s, weight)) {
8314                                set_glyph_weight(n, lmt_tohalfword(L, 3));
8315                            } else if (lua_key_eq(s, lhmin)) {
8316                                set_glyph_lhmin(n, lmt_tohalfword(L, 3));
8317                            } else if (lua_key_eq(s, rhmin)) {
8318                                set_glyph_rhmin(n, lmt_tohalfword(L, 3));
8319                            } else if (lua_key_eq(s, uchyph)) {
8320                                set_glyph_uchyph(n, lmt_tohalfword(L, 3));
8321                            } else if (lua_key_eq(s, hyphenate)) {
8322                                set_glyph_hyphenate(n, lmt_tohalfword(L, 3));
8323                            } else if (lua_key_eq(s, options)) {
8324                                set_glyph_options(n, lmt_tohalfword(L, 3) & glyph_option_valid);
8325                            } else if (lua_key_eq(s, discpart)) {
8326                                set_glyph_discpart(n, lmt_tohalfword(L, 3));
8327                            } else if (lua_key_eq(s, protected)) {
8328                                glyph_protected(n) = lmt_tosingleword(L, 3);
8329                            } else if (lua_key_eq(s, width)) {
8330                                /* not yet */
8331                            } else if (lua_key_eq(s, height)) {
8332                                /* not yet */
8333                            } else if (lua_key_eq(s, depth)) {
8334                                /* not yet */
8335                            } else if (lua_key_eq(s, properties)) {
8336                                glyph_properties(n) = lmt_toquarterword(L, 3);
8337                            } else if (lua_key_eq(s, group)) {
8338                                glyph_group(n) = lmt_toquarterword(L, 3);
8339                            } else if (lua_key_eq(s, index)) {
8340                                glyph_index(n) = lmt_tohalfword(L, 3);
8341                            } else if (lua_key_eq(s, control)) {
8342                                set_glyph_control(n, lmt_tohalfword(L, 3));
8343                            } else {
8344                                goto CANTSET;
8345                            }
8346                            return 0;
8347                        case hlist_node:
8348                        case vlist_node:
8349                            if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8350                                box_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8351                            } else if (lua_key_eq(s, width)) {
8352                                box_width(n) = (halfword) lmt_roundnumber(L, 3);
8353                            } else if (lua_key_eq(s, height)) {
8354                                box_height(n) = (halfword) lmt_roundnumber(L, 3);
8355                            } else if (lua_key_eq(s, depth)) {
8356                                box_depth(n) = (halfword) lmt_roundnumber(L, 3);
8357                            } else if (lua_key_eq(s, direction)) {
8358                                box_dir(n) = (singleword) nodelib_getdirection(L, 3);
8359                            } else if (lua_key_eq(s, shift)) {
8360                                box_shift_amount(n) = (halfword) lmt_roundnumber(L, 3);
8361                            } else if (lua_key_eq(s, glueorder)) {
8362                                box_glue_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8363                            } else if (lua_key_eq(s, gluesign)) {
8364                                box_glue_sign(n) = tex_checked_glue_sign(lmt_tohalfword(L, 3));
8365                            } else if (lua_key_eq(s, glueset)) {
8366                                box_glue_set(n) = (glueratio) lua_tonumber(L, 3);  /* integer or float */
8367                            } else if (lua_key_eq(s, geometry)) {
8368                                box_geometry(n) = (singleword) lmt_tohalfword(L, 3);
8369                            } else if (lua_key_eq(s, orientation)) {
8370                                box_orientation(n) = lmt_tohalfword(L, 3);
8371                                tex_check_box_geometry(n);
8372                            } else if (lua_key_eq(s, anchor)) {
8373                                box_anchor(n) = lmt_tohalfword(L, 3);
8374                                tex_check_box_geometry(n);
8375                            } else if (lua_key_eq(s, source)) {
8376                                box_source_anchor(n) = lmt_tohalfword(L, 3);
8377                                tex_check_box_geometry(n);
8378                            } else if (lua_key_eq(s, target)) {
8379                                box_target_anchor(n) = lmt_tohalfword(L, 3);
8380                                tex_check_box_geometry(n);
8381                            } else if (lua_key_eq(s, xoffset)) {
8382                                box_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
8383                                tex_check_box_geometry(n);
8384                            } else if (lua_key_eq(s, yoffset)) {
8385                                box_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
8386                                tex_check_box_geometry(n);
8387                            } else if (lua_key_eq(s, woffset)) {
8388                                box_w_offset(n) = (halfword) lmt_roundnumber(L, 3);
8389                                tex_check_box_geometry(n);
8390                            } else if (lua_key_eq(s, hoffset)) {
8391                                box_h_offset(n) = (halfword) lmt_roundnumber(L, 3);
8392                                tex_check_box_geometry(n);
8393                            } else if (lua_key_eq(s, doffset)) {
8394                                box_d_offset(n) = (halfword) lmt_roundnumber(L, 3);
8395                                tex_check_box_geometry(n);
8396                            } else if (lua_key_eq(s, pre)) {
8397                                box_pre_migrated(n) = nodelib_direct_or_node_from_index(L, direct, 3);;
8398                            } else if (lua_key_eq(s, post)) {
8399                                box_post_migrated(n) = nodelib_direct_or_node_from_index(L, direct, 3);;
8400                            } else if (lua_key_eq(s, state)) {
8401                                box_package_state(n) = (singleword) lmt_tohalfword(L, 3);
8402                            } else if (lua_key_eq(s, index)) {
8403                                box_index(n) = lmt_tohalfword(L, 3);
8404                            } else if (lua_key_eq(s, except)) {
8405                                box_except(n) = nodelib_direct_or_node_from_index(L, direct, 3);;
8406                            } else {
8407                                goto CANTSET;
8408                            }
8409                            return 0;
8410                        case disc_node:
8411                            if (lua_key_eq(s, pre)) {
8412                                tex_set_disc_field(n, pre_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
8413                            } else if (lua_key_eq(s, post)) {
8414                                tex_set_disc_field(n, post_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
8415                            } else if (lua_key_eq(s, replace)) {
8416                                tex_set_disc_field(n, no_break_code, nodelib_direct_or_node_from_index(L, direct, 3));
8417                            } else if (lua_key_eq(s, penalty)) {
8418                                disc_penalty(n) = lmt_tohalfword(L, 3);
8419                             } else if (lua_key_eq(s, options)) {
8420                                disc_options(n) = lmt_tohalfword(L, 3) & disc_option_valid;
8421                             } else if (lua_key_eq(s, class)) {
8422                                disc_class(n) = lmt_tohalfword(L, 3);
8423                            } else {
8424                                goto CANTSET;
8425                            }
8426                            return 0;
8427                        case glue_node:
8428                            if (lua_key_eq(s, width)) {
8429                                glue_amount(n) = (halfword) lmt_roundnumber(L, 3);
8430                            } else if (lua_key_eq(s, stretch)) {
8431                                glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
8432                            } else if (lua_key_eq(s, shrink)) {
8433                                glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
8434                            } else if (lua_key_eq(s, stretchorder)) {
8435                                glue_stretch_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8436                            } else if (lua_key_eq(s, shrinkorder)) {
8437                                glue_shrink_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8438                            } else if (lua_key_eq(s, leader)) {
8439                                glue_leader_ptr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8440                            } else if (lua_key_eq(s, font)) {
8441                                glue_font(n) = tex_checked_font(lmt_tohalfword(L, 3));
8442                            } else if (lua_key_eq(s, data)) {
8443                                glue_data(n) = (halfword) lmt_roundnumber(L, 3);
8444                            } else if (lua_key_eq(s, options)) {
8445                                glue_options(n) |= lmt_tohalfword(L, 3);
8446                            } else {
8447                                goto CANTSET;
8448                            }
8449                            return 0;
8450                        case kern_node:
8451                            if (lua_key_eq(s, kern)) {
8452                                kern_amount(n) = (halfword) lmt_roundnumber(L, 3);
8453                            } else if (lua_key_eq(s, expansion)) {
8454                                kern_expansion(n) = (halfword) lmt_roundnumber(L, 3);
8455                            } else {
8456                                goto CANTSET;
8457                            }
8458                            return 0;
8459                        case penalty_node:
8460                            if (lua_key_eq(s, penalty)) {
8461                                penalty_amount(n) = lmt_tohalfword(L, 3);
8462                            } else if (lua_key_eq(s, options)) {
8463                                penalty_options(n) |= lmt_tohalfword(L, 3);
8464                            } else {
8465                                goto CANTSET;
8466                            }
8467                            return 0;
8468                        case rule_node:
8469                            if (lua_key_eq(s, width)) {
8470                                rule_width(n) = (halfword) lmt_roundnumber(L, 3);
8471                            } else if (lua_key_eq(s, height)) {
8472                                rule_height(n) = (halfword) lmt_roundnumber(L, 3);
8473                            } else if (lua_key_eq(s, depth)) {
8474                                rule_depth(n) = (halfword) lmt_roundnumber(L, 3);
8475                            } else if (lua_key_eq(s, xoffset)) {
8476                                rule_x_offset(n) = (halfword) lmt_roundnumber(L, 3);
8477                            } else if (lua_key_eq(s, yoffset)) {
8478                                rule_y_offset(n) = (halfword) lmt_roundnumber(L, 3);
8479                            } else if (lua_key_eq(s, left)) {
8480                                tex_set_rule_left(n, (halfword) lmt_roundnumber(L, 3));
8481                            } else if (lua_key_eq(s, right)) {
8482                                tex_set_rule_right(n, (halfword) lmt_roundnumber(L, 3));
8483                            } else if (lua_key_eq(s, on)) {
8484                                tex_set_rule_on(n, (halfword) lmt_roundnumber(L, 3));
8485                            } else if (lua_key_eq(s, off)) {
8486                                tex_set_rule_off(n, (halfword) lmt_roundnumber(L, 3));
8487                            } else if (lua_key_eq(s, data)) {
8488                                rule_data(n) = lmt_tohalfword(L, 3);
8489                             } else if (lua_key_eq(s, options)) {
8490                                rule_options(n) = lmt_tohalfword(L, 3) & rule_option_valid;
8491                            } else if (lua_key_eq(s, font)) {
8492                                tex_set_rule_font(n, lmt_tohalfword(L, 3));
8493                            } else if (lua_key_eq(s, fam)) {
8494                                tex_set_rule_family(n, lmt_tohalfword(L, 3));
8495                            } else if (lua_key_eq(s, char)) {
8496                                rule_strut_character(n) = lmt_tohalfword(L, 3);
8497                            } else {
8498                                goto CANTSET;
8499                            }
8500                            return 0;
8501                        case dir_node:
8502                            if (lua_key_eq(s, direction)) {
8503                                dir_direction(n) = nodelib_getdirection(L, 3);
8504                            } else if (lua_key_eq(s, level)) {
8505                                dir_level(n) = lmt_tohalfword(L, 3);
8506                            } else {
8507                                goto CANTSET;
8508                            }
8509                            return 0;
8510                        case whatsit_node:
8511                            return 0;
8512                        case par_node:
8513                            /* not all of them here */
8514                            if (lua_key_eq(s, interlinepenalty)) {
8515                                tex_set_local_interline_penalty(n, lmt_tohalfword(L, 3));
8516                            } else if (lua_key_eq(s, brokenpenalty)) {
8517                                tex_set_local_broken_penalty(n, lmt_tohalfword(L, 3));
8518                            } else if (lua_key_eq(s, tolerance)) {
8519                                tex_set_local_tolerance(n, lmt_tohalfword(L, 3));
8520                            } else if (lua_key_eq(s, pretolerance)) {
8521                                tex_set_local_pre_tolerance(n, lmt_tohalfword(L, 3));
8522                            } else if (lua_key_eq(s, direction)) {
8523                                par_dir(n) = nodelib_getdirection(L, 3);
8524                            } else if (lua_key_eq(s, leftbox)) {
8525                                par_box_left(n) = nodelib_getlist(L, 3);
8526                            } else if (lua_key_eq(s, leftboxwidth)) {
8527                                tex_set_local_left_width(n, lmt_roundnumber(L, 3));
8528                            } else if (lua_key_eq(s, rightbox)) {
8529                                par_box_right(n) = nodelib_getlist(L, 3);
8530                            } else if (lua_key_eq(s, rightboxwidth)) {
8531                                tex_set_local_right_width(n, lmt_roundnumber(L, 3));
8532                            } else if (lua_key_eq(s, middlebox)) {
8533                                par_box_middle(n) = nodelib_getlist(L, 3);
8534                            } else {
8535                                goto CANTSET;
8536                            }
8537                            return 0;
8538                        case math_char_node:
8539                        case math_text_char_node:
8540                            if (lua_key_eq(s, fam)) {
8541                                kernel_math_family(n) = lmt_tohalfword(L, 3);
8542                            } else if (lua_key_eq(s, char)) {
8543                                kernel_math_character(n) = lmt_tohalfword(L, 3);
8544                            } else if (lua_key_eq(s, options)) {
8545                                kernel_math_options(n) = lmt_tohalfword(L, 3);
8546                            } else if (lua_key_eq(s, properties)) {
8547                                kernel_math_properties(n) = lmt_toquarterword(L, 3);
8548                            } else if (lua_key_eq(s, group)) {
8549                                kernel_math_group(n) = lmt_toquarterword(L, 3);
8550                            } else if (lua_key_eq(s, index)) {
8551                                kernel_math_index(n) = lmt_tohalfword(L, 3);
8552                            } else {
8553                                goto CANTSET;
8554                            }
8555                            return 0;
8556                        case mark_node:
8557                            if (lua_key_eq(s, index) || lua_key_eq(s, class)) {
8558                                halfword m = ignore_field_error ? lmt_opthalfword(L, 3, 1) : lmt_tohalfword(L, 3);
8559                                if (tex_valid_mark(m)) {
8560                                    mark_index(n) = m;
8561                                }
8562                            } else if (lua_key_eq(s, data) || lua_key_eq(s, mark)) {
8563                                tex_delete_token_reference(mark_ptr(n));
8564                                mark_ptr(n) = lmt_token_list_from_lua(L, 3); /* check ref */
8565                            } else {
8566                                goto CANTSET;
8567                            }
8568                            return 0;
8569                        case insert_node:
8570                            if (lua_key_eq(s, index)) {
8571                                halfword index = ignore_field_error ? lmt_opthalfword(L, 3, 1) : lmt_tohalfword(L, 3);
8572                                if (tex_valid_insert_id(index)) {
8573                                    insert_index(n) = index;
8574                                }
8575                            } else if (lua_key_eq(s, cost)) {
8576                                insert_float_cost(n) = lmt_tohalfword(L, 3);
8577                            } else if (lua_key_eq(s, depth)) {
8578                                insert_max_depth(n) = (halfword) lmt_roundnumber(L, 3);
8579                            } else if (lua_key_eq(s, height) || lua_key_eq(s, total)) {
8580                                insert_total_height(n) = (halfword) lmt_roundnumber(L, 3);
8581                            } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8582                                insert_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8583                            } else if (lua_key_eq(s, data)) {
8584                                insert_identifier(n) = (halfword) lmt_roundnumber(L, 3); 
8585                            } else if (lua_key_eq(s, lineheight)) {
8586                                insert_line_height(n) = (halfword) lmt_roundnumber(L, 3); 
8587                            } else if (lua_key_eq(s, linedepth)) {
8588                                insert_line_depth(n) = (halfword) lmt_roundnumber(L, 3); 
8589                            } else if (lua_key_eq(s, stretch)) {
8590                                insert_stretch(n) = (halfword) lmt_roundnumber(L, 3); 
8591                            } else if (lua_key_eq(s, shrink)) {
8592                                insert_shrink(n) = (halfword) lmt_roundnumber(L, 3); 
8593                            } else {
8594                                goto CANTSET;
8595                            }
8596                            return 0;
8597                        case math_node:
8598                            if (lua_key_eq(s, surround)) {
8599                                math_surround(n) = (halfword) lmt_roundnumber(L, 3);
8600                            } else if (lua_key_eq(s, width)) {
8601                                math_amount(n) = (halfword) lmt_roundnumber(L, 3);
8602                            } else if (lua_key_eq(s, stretch)) {
8603                                math_stretch(n) = (halfword) lmt_roundnumber(L, 3);
8604                            } else if (lua_key_eq(s, shrink)) {
8605                                math_shrink(n) = (halfword) lmt_roundnumber(L, 3);
8606                            } else if (lua_key_eq(s, stretchorder)) {
8607                                math_stretch_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8608                            } else if (lua_key_eq(s, shrinkorder)) {
8609                                math_shrink_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8610                            } else if (lua_key_eq(s, penalty)) {
8611                                math_penalty(n) = lmt_tohalfword(L, 3);
8612                            } else if (lua_key_eq(s, options)) {
8613                                math_options(n) |= lmt_tohalfword(L, 3);
8614                            } else {
8615                                goto CANTSET;
8616                            }
8617                            return 0;
8618                        case style_node:
8619                            if (lua_key_eq(s, style)) {
8620                                style_style(n) = (quarterword) lmt_get_math_style(L, 2, text_style);
8621                            } else {
8622                                /* return nodelib_cantset(L, n, s); */
8623                            }
8624                            return 0;
8625                        case parameter_node:
8626                            if (lua_key_eq(s, style)) {
8627                                parameter_style(n) = (quarterword) lmt_get_math_style(L, 2, text_style);
8628                            } else if (lua_key_eq(s, name)) {
8629                                parameter_name(n) = lmt_get_math_parameter(L, 2, parameter_name(n));
8630                            } else if (lua_key_eq(s, value)) {
8631                                halfword code = parameter_name(n);
8632                                if (code < 0 || code >= math_parameter_last) {
8633                                    /* error */
8634                                } else if (math_parameter_value_type(code)) {
8635                                    /* todo, see tex_setmathparm */
8636                                } else {
8637                                    parameter_value(n) = lmt_tohalfword(L, 3);
8638                                }
8639                            }
8640                            return 0;
8641                        case simple_noad:
8642                        case radical_noad:
8643                        case fraction_noad:
8644                        case accent_noad:
8645                        case fence_noad:
8646                            /* fence has less */
8647                            if (lua_key_eq(s, nucleus)) {
8648                                noad_nucleus(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8649                            } else if (lua_key_eq(s, sub)) {
8650                                noad_subscr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8651                            } else if (lua_key_eq(s, sup)) {
8652                                noad_supscr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8653                            } else if (lua_key_eq(s, subpre)) {
8654                                noad_subprescr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8655                            } else if (lua_key_eq(s, suppre)) {
8656                                noad_supprescr(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8657                            } else if (lua_key_eq(s, prime)) {
8658                                noad_prime(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8659                            } else if (lua_key_eq(s, source)) {
8660                                noad_source(n) = lmt_tohalfword(L, 3);
8661                            } else if (lua_key_eq(s, options)) {
8662                                noad_options(n) = lmt_tofullword(L, 3);
8663                            } else if (lua_key_eq(s, scriptorder)) {
8664                                noad_script_order(n) = lmt_tosingleword(L, 3);
8665                            } else if (lua_key_eq(s, class)) {
8666                                halfword c = lmt_tohalfword(L, 3);
8667                                set_noad_main_class(n, c);
8668                                set_noad_left_class(n, lmt_opthalfword(L, 4, c));
8669                                set_noad_right_class(n, lmt_opthalfword(L, 5, c));
8670                            } else if (lua_key_eq(s, fam)) {
8671                                set_noad_family(n, lmt_tohalfword(L, 3));
8672                            } else {
8673                                switch (t) {
8674                                    case simple_noad:
8675                                        break;
8676                                    case radical_noad:
8677                                        if (lua_key_eq(s, left) || lua_key_eq(s, delimiter)) {
8678                                            radical_left_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8679                                        } else if (lua_key_eq(s, right)) {
8680                                            radical_right_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8681                                        } else if (lua_key_eq(s, top)) {
8682                                            radical_top_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8683                                        } else if (lua_key_eq(s, bottom)) {
8684                                            radical_bottom_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8685                                        } else if (lua_key_eq(s, degree)) {
8686                                            radical_degree(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8687                                        } else if (lua_key_eq(s, width)) {
8688                                            noad_width(n) = lmt_roundnumber(L, 3);
8689                                        } else {
8690                                            goto CANTSET;
8691                                        }
8692                                        return 0;
8693                                    case fraction_noad:
8694                                        if (lua_key_eq(s, width)) {
8695                                            fraction_rule_thickness(n) = (halfword) lmt_roundnumber(L, 3);
8696                                        } else if (lua_key_eq(s, numerator)) {
8697                                            fraction_numerator(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8698                                        } else if (lua_key_eq(s, denominator)) {
8699                                            fraction_denominator(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8700                                        } else if (lua_key_eq(s, left)) {
8701                                            fraction_left_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8702                                        } else if (lua_key_eq(s, right)) {
8703                                            fraction_right_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8704                                        } else if (lua_key_eq(s, middle)) {
8705                                            fraction_middle_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8706                                        } else {
8707                                            goto CANTSET;
8708                                        }
8709                                        return 0;
8710                                    case accent_noad:
8711                                        if (lua_key_eq(s, top) || lua_key_eq(s, topaccent)) {
8712                                            accent_top_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8713                                        } else if (lua_key_eq(s, bottom) || lua_key_eq(s, bottomaccent)) {
8714                                            accent_bottom_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8715                                        } else if (lua_key_eq(s, middle) || lua_key_eq(s, overlayaccent)) {
8716                                            accent_middle_character(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8717                                        } else if (lua_key_eq(s, fraction)) {
8718                                            accent_fraction(n) = (halfword) lmt_roundnumber(L, 3);
8719                                        } else {
8720                                            goto CANTSET;
8721                                        }
8722                                        return 0;
8723                                    case fence_noad:
8724                                        if (lua_key_eq(s, delimiter)) {
8725                                            fence_delimiter(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8726                                        } else if (lua_key_eq(s, top)) {
8727                                            fence_delimiter_top(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8728                                        } else if (lua_key_eq(s, bottom)) {
8729                                            fence_delimiter_bottom(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8730                                        } else if (lua_key_eq(s, italic)) {
8731                                            noad_italic(n) = (halfword) lmt_roundnumber(L, 3);
8732                                        } else if (lua_key_eq(s, height)) {
8733                                            noad_height(n) = (halfword) lmt_roundnumber(L, 3);
8734                                        } else if (lua_key_eq(s, depth)) {
8735                                            noad_depth(n) = (halfword) lmt_roundnumber(L, 3);
8736                                        } else if (lua_key_eq(s, variant)) {
8737                                            fence_delimiter_variant(n) = (halfword) lmt_roundnumber(L, 3);
8738                                        } else {
8739                                            goto CANTSET;
8740                                        }
8741                                        return 0;
8742                                    }
8743                            }
8744                            return 0;
8745                        case delimiter_node:
8746                            if (lua_key_eq(s, smallfamily)) {
8747                                delimiter_small_family(n) = lmt_tohalfword(L, 3);
8748                            } else if (lua_key_eq(s, smallchar)) {
8749                                delimiter_small_character(n) = lmt_tohalfword(L, 3);
8750                            } else if (lua_key_eq(s, largefamily)) {
8751                                delimiter_large_family(n) = lmt_tohalfword(L, 3);
8752                            } else if (lua_key_eq(s, largechar)) {
8753                                delimiter_large_character(n) = lmt_tohalfword(L, 3);
8754                            } else {
8755                                goto CANTSET;
8756                            }
8757                            return 0;
8758                        case sub_box_node:
8759                        case sub_mlist_node:
8760                            if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8761                                kernel_math_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8762                            } else {
8763                                goto CANTSET;
8764                            }
8765                            return 0;
8766                        case split_node: /* might go away */
8767                            if (lua_key_eq(s, index)) {
8768                                halfword index = lmt_tohalfword(L, 3);
8769                                if (tex_valid_insert_id(index)) {
8770                                    split_insert_index(n) = index;
8771                                }
8772                            } else if (lua_key_eq(s, lastinsert)) {
8773                                split_last_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8774                            } else if (lua_key_eq(s, bestinsert)) {
8775                                split_best_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8776                            } else if (lua_key_eq(s, broken)) {
8777                                split_broken(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8778                            } else if (lua_key_eq(s, brokeninsert)) {
8779                                split_broken_insert(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8780                            } else {
8781                                goto CANTSET;
8782                            }
8783                            return 0;
8784                        case choice_node:
8785                            if (lua_key_eq(s, display)) {
8786                                choice_display_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8787                            } else if (lua_key_eq(s, text)) {
8788                                choice_text_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8789                            } else if (lua_key_eq(s, script)) {
8790                                choice_script_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8791                            } else if (lua_key_eq(s, scriptscript)) {
8792                                choice_script_script_mlist(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8793                            } else {
8794                                goto CANTSET;
8795                            }
8796                            return 0;
8797                        case attribute_node:
8798                            switch (node_subtype(n)) {
8799                                case attribute_list_subtype:
8800                                    if (lua_key_eq(s, count)) {
8801                                        attribute_count(n) = lmt_tohalfword(L, 3);
8802                                    } else {
8803                                        goto CANTSET;
8804                                    }
8805                                    return 0;
8806                                case attribute_value_subtype:
8807                                    if (lua_key_eq(s, index) || lua_key_eq(s, number)) {
8808                                        attribute_index(n) = (quarterword) lmt_tohalfword(L, 3);
8809                                    } else if (lua_key_eq(s, value)) {
8810                                        attribute_value(n) = lmt_tohalfword(L, 3);
8811                                    } else {
8812                                        goto CANTSET;
8813                                    }
8814                                    return 0;
8815                                default:
8816                                    /* just ignored */
8817                                    return 0;
8818                            }
8819                         // break;
8820                        case adjust_node:
8821                            if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8822                                adjust_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8823                            } else if (lua_key_eq(s, index)) {
8824                                halfword index = lmt_tohalfword(L, 3);
8825                                if (tex_valid_adjust_index(index)) {
8826                                    adjust_index(n) = index;
8827                                }
8828                            } else {
8829                                goto CANTSET;
8830                            }
8831                            return 0;
8832                        case unset_node:
8833                            if (lua_key_eq(s, width)) {
8834                                box_width(n) = (halfword) lmt_roundnumber(L, 3);
8835                            } else if (lua_key_eq(s, height)) {
8836                                box_height(n) = (halfword) lmt_roundnumber(L, 3);
8837                            } else if (lua_key_eq(s, depth)) {
8838                                box_depth(n) = (halfword) lmt_roundnumber(L, 3);
8839                            } else if (lua_key_eq(s, direction)) {
8840                                box_dir(n) = (singleword) nodelib_getdirection(L, 3);
8841                            } else if (lua_key_eq(s, shrink)) {
8842                                box_glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
8843                            } else if (lua_key_eq(s, glueorder)) {
8844                                box_glue_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8845                            } else if (lua_key_eq(s, gluesign)) {
8846                                box_glue_sign(n) = tex_checked_glue_sign(lmt_tohalfword(L, 3));
8847                            } else if (lua_key_eq(s, stretch)) {
8848                                box_glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
8849                            } else if (lua_key_eq(s, count)) {
8850                                box_span_count(n) = lmt_tohalfword(L, 3);
8851                            } else if (lua_key_eq(s, list) || lua_key_eq(s, head)) {
8852                                box_list(n) = nodelib_direct_or_node_from_index(L, direct, 3);
8853                            } else {
8854                                goto CANTSET;
8855                            }
8856                            return 0;
8857                        case boundary_node:
8858                            if (lua_key_eq(s, value) || lua_key_eq(s, data)) {
8859                                boundary_data(n) = lmt_tohalfword(L, 3);
8860                            } else if (lua_key_eq(s, reserved)) {
8861                                boundary_reserved(n) = lmt_tohalfword(L, 3);
8862                            } else {
8863                                goto CANTSET;
8864                            }
8865                            return 0;
8866                        case glue_spec_node:
8867                            if (lua_key_eq(s, width)) {
8868                                glue_amount(n) = (halfword) lmt_roundnumber(L, 3);
8869                            } else if (lua_key_eq(s, stretch)) {
8870                                glue_stretch(n) = (halfword) lmt_roundnumber(L, 3);
8871                            } else if (lua_key_eq(s, shrink)) {
8872                                glue_shrink(n) = (halfword) lmt_roundnumber(L, 3);
8873                            } else if (lua_key_eq(s, stretchorder)) {
8874                                glue_stretch_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8875                            } else if (lua_key_eq(s, shrinkorder)) {
8876                                glue_shrink_order(n) = tex_checked_glue_order(lmt_tohalfword(L, 3));
8877                            } else {
8878                                goto CANTSET;
8879                            }
8880                            return 0;
8881                        default:
8882                            last_field_error = lua_set_field_error;
8883                            return ignore_field_error ? 0 : luaL_error(L, "you can't assign to a %s node (%d)\n", lmt_interface.node_data[t].name, n);
8884                    }
8885                  CANTSET:
8886                    last_field_error = lua_set_field_error;
8887                    return ignore_field_error ? 0 : luaL_error(L,"you can't set field %s in a %s node (%d)", s, lmt_interface.node_data[t].name, n);
8888                }
8889                return 0;
8890            }
8891    }
8892    return 0;
8893}
8894
8895static int nodelib_shared_setfielderror(lua_State *L)
8896{
8897    ignore_field_error = lua_toboolean(L, 1);
8898    last_field_error = lua_no_field_error;
8899    return 1;
8900}
8901
8902static int nodelib_shared_getfielderror(lua_State *L)
8903{
8904    lua_pushinteger(L, last_field_error);
8905    return 1;
8906}
8907
8908
8909# define setfield_usage common_usage
8910
8911static int nodelib_direct_setfield(lua_State *L)
8912{
8913    halfword n = nodelib_valid_direct_from_index(L, 1);
8914    if (n) {
8915        nodelib_common_setfield(L, 1, n);
8916    }
8917    return 0;
8918}
8919
8920static int nodelib_userdata_newindex(lua_State *L)
8921{
8922    halfword n = *((halfword *) lua_touserdata(L, 1));
8923    if (n) {
8924        nodelib_common_setfield(L, 0, n);
8925    }
8926    return 0;
8927}
8928
8929static int nodelib_userdata_setfield(lua_State *L)
8930{
8931    halfword n = lmt_maybe_isnode(L, 1);
8932    if (n) {
8933        nodelib_common_setfield(L, 0, n);
8934    }
8935    return 0;
8936}
8937
8938/* tex serializing */
8939
8940static int verbose = 1; /* This might become an option (then move this in a state)! */
8941
8942static void nodelib_tostring(lua_State *L, halfword n, const char *tag)
8943{
8944    char msg[256];
8945    char a[7] = { ' ', ' ', ' ', 'n', 'i', 'l', 0 };
8946    char v[7] = { ' ', ' ', ' ', 'n', 'i', 'l', 0 };
8947    halfword t = node_type(n);
8948    halfword s = node_subtype(n);
8949    node_info nd = lmt_interface.node_data[t];
8950    if (tex_nodetype_has_prev(t) && node_prev(n)) {
8951        snprintf(a, 7, "%6d", (int) node_prev(n));
8952    }
8953    if (node_next(n)) {
8954        snprintf(v, 7, "%6d", (int) node_next(n));
8955    }
8956    if (t == whatsit_node) {
8957        snprintf(msg, 255, "<%s : %s < %6d > %s : %s %d>", tag, a, (int) n, v, nd.name, s);
8958    } else if (! tex_nodetype_has_subtype(n)) {
8959        snprintf(msg, 255, "<%s : %s < %6d > %s : %s>", tag, a, (int) n, v, nd.name);
8960    } else if (verbose) {
8961        /*tex Sloooow! But subtype lists can have holes. */
8962        value_info *sd = nd.subtypes;
8963        int j = -1;
8964        if (sd) {
8965         // if (t == glyph_node) {
8966         //     s = tex_subtype_of_glyph(n);
8967         // }
8968            if (s >= nd.first && s <= nd.last) {
8969                for (int i = 0; ; i++) {
8970                    if (sd[i].id == s) {
8971                        j = i;
8972                        break ;
8973                    } else if (sd[i].id < 0) {
8974                        break;
8975                    }
8976                }
8977            }
8978        }
8979        if (j < 0) {
8980            snprintf(msg, 255, "<%s : %s <= %6d => %s : %s %d>", tag, a, (int) n, v, nd.name, s);
8981        } else {
8982            snprintf(msg, 255, "<%s : %s <= %6d => %s : %s %s>", tag, a, (int) n, v, nd.name, sd[j].name);
8983        }
8984    } else {
8985        snprintf(msg, 255, "<%s : %s < %6d > %s : %s %d>", tag, a, (int) n, v, nd.name, s);
8986    }
8987    lua_pushstring(L, (const char *) msg);
8988}
8989
8990/* __tostring node.tostring */
8991
8992static int nodelib_userdata_tostring(lua_State *L)
8993{
8994    halfword n = lmt_check_isnode(L, 1);
8995    if (n) {
8996        nodelib_tostring(L, n, lua_key(node));
8997    } else {
8998        lua_pushnil(L);
8999    }
9000    return 1;
9001}
9002
9003/* node.direct.tostring */
9004
9005static int nodelib_direct_tostring(lua_State *L)
9006{
9007    halfword n = nodelib_valid_direct_from_index(L, 1);
9008    if (n) {
9009        nodelib_tostring(L, n, lua_key(direct));
9010    } else {
9011        lua_pushnil(L);
9012    }
9013    return 1;
9014}
9015
9016/* __eq */
9017
9018static int nodelib_userdata_equal(lua_State *L)
9019{
9020    halfword n = *((halfword *) lua_touserdata(L, 1));
9021    halfword m = *((halfword *) lua_touserdata(L, 2));
9022    lua_pushboolean(L, (n == m));
9023    return 1;
9024}
9025
9026/* node.ligaturing */
9027
9028# define ligaturing_usage common_usage
9029
9030static int nodelib_direct_ligaturing(lua_State *L)
9031{
9032    if (lua_gettop(L) >= 1) {
9033        halfword h = nodelib_valid_direct_from_index(L, 1);
9034        halfword t = nodelib_valid_direct_from_index(L, 2);
9035        if (h) {
9036            halfword tmp_head = tex_new_node(nesting_node, unset_nesting_code);
9037            halfword p = node_prev(h);
9038            tex_couple_nodes(tmp_head, h);
9039            node_tail(tmp_head) = t;
9040            t = tex_handle_ligaturing(tmp_head, t);
9041            if (p) {
9042                node_next(p) = node_next(tmp_head) ;
9043            }
9044            node_prev(node_next(tmp_head)) = p ;
9045            lua_pushinteger(L, node_next(tmp_head));
9046            lua_pushinteger(L, t);
9047            lua_pushboolean(L, 1);
9048            tex_flush_node(tmp_head);
9049            return 3;
9050        }
9051    }
9052    lua_pushnil(L);
9053    lua_pushboolean(L, 0);
9054    return 2;
9055}
9056
9057/* node.kerning */
9058
9059# define kerning_usage common_usage
9060
9061static int nodelib_direct_kerning(lua_State *L)
9062{
9063    if (lua_gettop(L) >= 1) {
9064        halfword h = nodelib_valid_direct_from_index(L, 1);
9065        halfword t = nodelib_valid_direct_from_index(L, 2);
9066        if (h) {
9067            halfword tmp_head = tex_new_node(nesting_node, unset_nesting_code);
9068            halfword p = node_prev(h);
9069            tex_couple_nodes(tmp_head, h);
9070            node_tail(tmp_head) = t;
9071            t = tex_handle_kerning(tmp_head, t);
9072            if (p) {
9073                node_next(p) = node_next(tmp_head) ;
9074            }
9075            node_prev(node_next(tmp_head)) = p ;
9076            lua_pushinteger(L, node_next(tmp_head));
9077            if (t) {
9078                lua_pushinteger(L, t);
9079            } else {
9080                lua_pushnil(L);
9081            }
9082            lua_pushboolean(L, 1);
9083            tex_flush_node(tmp_head);
9084            return 3;
9085        }
9086    }
9087    lua_pushnil(L);
9088    lua_pushboolean(L, 0);
9089    return 2;
9090}
9091
9092/*tex
9093    It's more consistent to have it here (so we will alias in lang later). Todo: if no glyph then
9094    quit.
9095*/
9096
9097# define hyphenating_usage common_usage
9098# define collapsing_usage  common_usage
9099
9100static int nodelib_direct_hyphenating(lua_State *L)
9101{
9102    halfword h = nodelib_valid_direct_from_index(L, 1);
9103    halfword t = nodelib_valid_direct_from_index(L, 2);
9104    if (h) {
9105        if (! t) {
9106            t = h;
9107            while (node_next(t)) {
9108                t = node_next(t);
9109            }
9110        }
9111        tex_hyphenate_list(h, t); /* todo: grab new tail */
9112    } else {
9113        /*tex We could consider setting |h| and |t| to |null|. */
9114    }
9115    lua_pushinteger(L, h);
9116    lua_pushinteger(L, t);
9117    lua_pushboolean(L, 1);
9118    return 3;
9119}
9120
9121static int nodelib_direct_collapsing(lua_State *L)
9122{
9123    halfword h = nodelib_valid_direct_from_index(L, 1);
9124    if (h) {
9125        halfword c1 = lmt_optinteger(L, 2, ex_hyphen_char_par);
9126        halfword c2 = lmt_optinteger(L, 3, 0x2013);
9127        halfword c3 = lmt_optinteger(L, 4, 0x2014);
9128        halfword c4 = lmt_optinteger(L, 5, 0x2019);
9129        tex_collapse_list(h, c1, c2, c3, c4);
9130    }
9131    lua_pushinteger(L, h);
9132    return 1;
9133}
9134
9135/* node.protect_glyphs */
9136/* node.unprotect_glyphs */
9137
9138static inline void nodelib_aux_protect_all(halfword h)
9139{
9140    while (h) {
9141        if (node_type(h) == glyph_node) {
9142            glyph_protected(h) = glyph_protected_text_code;
9143        }
9144        h = node_next(h);
9145    }
9146}
9147static inline void nodelib_aux_unprotect_all(halfword h)
9148{
9149    while (h) {
9150        if (node_type(h) == glyph_node) {
9151            glyph_protected(h) = glyph_unprotected_code;
9152        }
9153        h = node_next(h);
9154    }
9155}
9156
9157static inline void nodelib_aux_protect_node(halfword n)
9158{
9159    switch (node_type(n)) {
9160        case glyph_node:
9161            glyph_protected(n) = glyph_protected_text_code;
9162            break;
9163        case disc_node:
9164            nodelib_aux_protect_all(disc_no_break_head(n));
9165            nodelib_aux_protect_all(disc_pre_break_head(n));
9166            nodelib_aux_protect_all(disc_post_break_head(n));
9167            break;
9168    }
9169}
9170
9171static inline void nodelib_aux_unprotect_node(halfword n)
9172{
9173    switch (node_type(n)) {
9174        case glyph_node:
9175            glyph_protected(n) = glyph_unprotected_code;
9176            break;
9177        case disc_node:
9178            nodelib_aux_unprotect_all(disc_no_break_head(n));
9179            nodelib_aux_unprotect_all(disc_pre_break_head(n));
9180            nodelib_aux_unprotect_all(disc_post_break_head(n));
9181            break;
9182    }
9183}
9184
9185/* node.direct.protect_glyphs */
9186/* node.direct.unprotect_glyphs */
9187
9188# define protectglyph_usage        (glyph_usage | disc_usage)
9189# define unprotectglyph_usage      (glyph_usage | disc_usage)
9190
9191# define protectglyphs_usage       common_usage
9192# define unprotectglyphs_usage     common_usage
9193
9194# define protectglyphsnone_usage   common_usage
9195# define unprotectglyphsnone_usage common_usage
9196
9197static int nodelib_direct_protectglyph(lua_State *L)
9198{
9199    halfword n = nodelib_valid_direct_from_index(L, 1);
9200    if (n) {
9201        nodelib_aux_protect_node(n);
9202    }
9203    return 0;
9204}
9205
9206static int nodelib_direct_unprotectglyph(lua_State *L)
9207{
9208    halfword n = nodelib_valid_direct_from_index(L, 1);
9209    if (n) {
9210        nodelib_aux_unprotect_node(n);
9211    }
9212    return 0;
9213}
9214
9215static int nodelib_direct_protectglyphs(lua_State *L)
9216{
9217    halfword head = nodelib_valid_direct_from_index(L, 1);
9218    halfword tail = nodelib_valid_direct_from_index(L, 2);
9219    if (head) {
9220        while (head) {
9221            nodelib_aux_protect_node(head);
9222            if (head == tail) {
9223                break;
9224            } else {
9225                head = node_next(head);
9226            }
9227        }
9228    }
9229    return 0;
9230}
9231
9232static int nodelib_direct_unprotectglyphs(lua_State *L)
9233{
9234    halfword head = nodelib_valid_direct_from_index(L, 1);
9235    halfword tail = nodelib_valid_direct_from_index(L, 2);
9236    if (head) {
9237        while (head) {
9238            nodelib_aux_unprotect_node(head);
9239            if (head == tail) {
9240                break;
9241            } else {
9242                head = node_next(head);
9243            }
9244        }
9245    }
9246    return 0;
9247}
9248
9249/*tex This is an experiment. */
9250
9251static inline void nodelib_aux_protect_all_none(halfword h)
9252{
9253    while (h) {
9254        if (node_type(h) == glyph_node) {
9255            halfword f =  glyph_font(h);
9256            if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f] && has_font_text_control(f, text_control_none_protected)) {
9257                glyph_protected(h) = glyph_protected_text_code;
9258            }
9259        }
9260        h = node_next(h);
9261    }
9262}
9263
9264static inline void nodelib_aux_protect_node_none(halfword n)
9265{
9266    switch (node_type(n)) {
9267        case glyph_node:
9268            {
9269                halfword f =  glyph_font(n);
9270                if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f] && has_font_text_control(f, text_control_none_protected)) {
9271                    glyph_protected(n) = glyph_protected_text_code;
9272                }
9273            }
9274            break;
9275        case disc_node:
9276            nodelib_aux_protect_all_none(disc_no_break_head(n));
9277            nodelib_aux_protect_all_none(disc_pre_break_head(n));
9278            nodelib_aux_protect_all_none(disc_post_break_head(n));
9279            break;
9280    }
9281}
9282
9283static int nodelib_direct_protectglyphsnone(lua_State *L)
9284{
9285    halfword head = nodelib_valid_direct_from_index(L, 1);
9286    halfword tail = nodelib_valid_direct_from_index(L, 2);
9287    if (head) {
9288        while (head) {
9289            nodelib_aux_protect_node_none(head);
9290            if (head == tail) {
9291                break;
9292            } else {
9293                head = node_next(head);
9294            }
9295        }
9296    }
9297    return 0;
9298}
9299
9300/* node.direct.first_glyphnode */
9301/* node.direct.first_glyph */
9302/* node.direct.first_char */
9303
9304# define firstglyphnode_usage common_usage
9305# define firstglyph_usage     common_usage
9306# define firstchar_usage      common_usage
9307
9308static int nodelib_direct_firstglyphnode(lua_State *L)
9309{
9310    halfword h = nodelib_valid_direct_from_index(L, 1);
9311    halfword t = nodelib_valid_direct_from_index(L, 2);
9312    if (h) {
9313        halfword savetail = null;
9314        if (t) {
9315            savetail = node_next(t);
9316            node_next(t) = null;
9317        }
9318        while (h && node_type(h) != glyph_node) {
9319            h = node_next(h);
9320        }
9321        if (savetail) {
9322            node_next(t) = savetail;
9323        }
9324    }
9325    if (h) {
9326        lua_pushinteger(L, h);
9327    } else {
9328        lua_pushnil(L);
9329    }
9330    return 1;
9331}
9332
9333
9334static int nodelib_direct_firstglyph(lua_State *L)
9335{
9336    halfword h = nodelib_valid_direct_from_index(L, 1);
9337    halfword t = nodelib_valid_direct_from_index(L, 2);
9338    if (h) {
9339        halfword savetail = null;
9340        if (t) {
9341            savetail = node_next(t);
9342            node_next(t) = null;
9343        }
9344        /*tex
9345            We go to the first processed character so that is one with a value <= 0xFF and we
9346            don't care about what the value is.
9347        */
9348        while (h && ! (node_type(h) == glyph_node && glyph_protected(h))) {
9349            h = node_next(h);
9350        }
9351        if (savetail) {
9352            node_next(t) = savetail;
9353        }
9354    }
9355    if (h) {
9356        lua_pushinteger(L, h);
9357    } else {
9358        lua_pushnil(L);
9359    }
9360    return 1;
9361}
9362
9363static int nodelib_direct_firstchar(lua_State *L)
9364{
9365    halfword h = nodelib_valid_direct_from_index(L, 1);
9366    halfword t = nodelib_valid_direct_from_index(L, 2);
9367    if (h) {
9368        halfword savetail = null;
9369        if (t) {
9370            savetail = node_next(t);
9371            node_next(t) = null;
9372        }
9373        /*tex
9374            We go to the first unprocessed character so that is one with a value <= 0xFF and we
9375            don't care about what the value is.
9376        */
9377        while (h && ! (node_type(h) == glyph_node && ! glyph_protected(h))) {
9378            h = node_next(h);
9379        }
9380        if (savetail) {
9381            node_next(t) = savetail;
9382        }
9383    }
9384    if (h) {
9385        lua_pushinteger(L, h);
9386    } else {
9387        lua_pushnil(L);
9388    }
9389    return 1;
9390}
9391
9392/* node.direct.find_node(head)         : node, subtype*/
9393/* node.direct.find_node(head,subtype) : node */
9394
9395# define findnode_usage common_usage
9396
9397static int nodelib_direct_findnode(lua_State *L)
9398{
9399    halfword h = nodelib_valid_direct_from_index(L, 1);
9400    if (h) {
9401        halfword type = lmt_tohalfword(L, 2);
9402        if (lua_gettop(L) > 2) {
9403            halfword subtype = lmt_tohalfword(L, 3);
9404            while (h) {
9405                if (node_type(h) == type && node_subtype(h) == subtype) {
9406                    lua_pushinteger(L, h);
9407                    return 1;
9408                } else {
9409                    h = node_next(h);
9410                }
9411            }
9412        } else {
9413            while (h) {
9414                if (node_type(h) == type) {
9415                    lua_pushinteger(L, h);
9416                    lua_pushinteger(L, node_subtype(h));
9417                    return 2;
9418                } else {
9419                    h = node_next(h);
9420                }
9421            }
9422        }
9423    }
9424    lua_pushnil(L);
9425    return 1;
9426}
9427
9428/* node.direct.has_glyph */
9429
9430# define hasglyph_usage common_usage
9431
9432static int nodelib_direct_hasglyph(lua_State *L)
9433{
9434    halfword h = nodelib_valid_direct_from_index(L, 1);
9435    while (h) {
9436        switch (node_type(h)) {
9437            case glyph_node:
9438            case disc_node:
9439                nodelib_push_direct_or_nil(L, h);
9440                return 1;
9441            default:
9442                h = node_next(h);
9443                break;
9444        }
9445    }
9446    lua_pushnil(L);
9447    return 1;
9448}
9449
9450/* node.getword */
9451
9452static inline int nodelib_aux_in_word(halfword n)
9453{
9454    switch (node_type(n)) {
9455        case glyph_node:
9456        case disc_node:
9457            return 1;
9458        case kern_node:
9459            return node_subtype(n) == font_kern_subtype;
9460        default:
9461            return 0;
9462    }
9463}
9464
9465# define getwordrange_usage common_usage
9466
9467static int nodelib_direct_getwordrange(lua_State *L)
9468{
9469    halfword m = nodelib_valid_direct_from_index(L, 1);
9470    if (m) {
9471        /*tex We don't check on type if |m|. */
9472        halfword l = m;
9473        halfword r = m;
9474        while (node_prev(l) && nodelib_aux_in_word(node_prev(l))) {
9475            l = node_prev(l);
9476        }
9477        while (node_next(r) && nodelib_aux_in_word(node_next(r))) {
9478            r = node_next(r);
9479        }
9480        nodelib_push_direct_or_nil(L, l);
9481        nodelib_push_direct_or_nil(L, r);
9482    } else {
9483        lua_pushnil(L);
9484        lua_pushnil(L);
9485    }
9486    return 2;
9487}
9488
9489/* node.inuse */
9490
9491static int nodelib_userdata_inuse(lua_State *L)
9492{
9493    int counts[max_node_type + 1] = { 0 };
9494    int n = tex_n_of_used_nodes(&counts[0]);
9495    lua_createtable(L, 0, max_node_type);
9496    for (int i = 0; i < max_node_type; i++) {
9497        if (counts[i]) {
9498            lua_pushstring(L, lmt_interface.node_data[i].name);
9499            lua_pushinteger(L, counts[i]);
9500            lua_rawset(L, -3);
9501        }
9502    }
9503    lua_pushinteger(L, n);
9504    return 2;
9505}
9506
9507/*tex A bit of a cheat: some nodes can turn into another one due to the same size. */
9508
9509static int nodelib_userdata_instock(lua_State *L)
9510{
9511    int counts[max_node_type + 1] = { 0 };
9512    int n = 0;
9513    lua_createtable(L, 0, max_node_type);
9514    for (int i = 1; i < max_chain_size; i++) {
9515        halfword p = lmt_node_memory_state.free_chain[i];
9516        while (p) {
9517            if (node_type(p) <= max_node_type) {
9518                 ++counts[node_type(p)];
9519            }
9520            p = node_next(p);
9521        }
9522    }
9523    for (int i = 0; i < max_node_type; i++) {
9524        if (counts[i]) {
9525            lua_pushstring(L, lmt_interface.node_data[i].name);
9526            lua_pushinteger(L, counts[i]);
9527            lua_rawset(L, -3);
9528            n += counts[i];
9529        }
9530    }
9531    lua_pushinteger(L, n);
9532    return 2;
9533}
9534
9535/* node.usedlist */
9536
9537static int nodelib_userdata_usedlist(lua_State *L)
9538{
9539    lmt_push_node_fast(L, tex_list_node_mem_usage());
9540    return 1;
9541}
9542
9543/* node.direct.usedlist */
9544
9545# define usedlist_usage common_usage
9546
9547static int nodelib_direct_usedlist(lua_State *L)
9548{
9549    lua_pushinteger(L, tex_list_node_mem_usage());
9550    return 1;
9551}
9552
9553/* node.direct.protrusionskipable(node m) */
9554
9555# define protrusionskippable_usage common_usage
9556
9557static int nodelib_direct_protrusionskipable(lua_State *L)
9558{
9559    halfword n = lmt_tohalfword(L, 1);
9560    if (n) {
9561        lua_pushboolean(L, tex_protrusion_skipable(n));
9562    } else {
9563        lua_pushnil(L);
9564    }
9565    return 1;
9566}
9567
9568/* node.currentattributes(node m) */
9569
9570static int nodelib_userdata_currentattributes(lua_State* L)
9571{
9572    halfword n = tex_current_attribute_list();
9573    if (n) {
9574        lmt_push_node_fast(L, n);
9575    } else {
9576        lua_pushnil(L);
9577    }
9578    return 1;
9579}
9580
9581/* node.direct.currentattributes(node m) */
9582
9583# define currentattributes_usage common_usage
9584
9585static int nodelib_direct_currentattributes(lua_State* L)
9586{
9587    halfword n = tex_current_attribute_list();
9588    if (n) {
9589        lua_pushinteger(L, n);
9590    } else {
9591        lua_pushnil(L);
9592    }
9593    return 1;
9594}
9595
9596/* node.direct.todirect */
9597
9598# define todirect_usage      common_usage
9599# define tovaliddirect_usage common_usage
9600
9601static int nodelib_shared_todirect(lua_State* L)
9602{
9603    if (lua_type(L, 1) != LUA_TNUMBER) {
9604        /* assume node, no further testing, used in known situations */
9605        void* n = lua_touserdata(L, 1);
9606        if (n) {
9607            lua_pushinteger(L, *((halfword*)n));
9608        }
9609        else {
9610            lua_pushnil(L);
9611        }
9612    } /* else assume direct and returns argument */
9613    return 1;
9614}
9615
9616static int nodelib_direct_tovaliddirect(lua_State* L)
9617{
9618    halfword n = lmt_check_isnode(L, 1);
9619    if (n) {
9620        lua_pushinteger(L, n);
9621    } else {
9622        lua_pushnil(L);
9623    }
9624    return 1;
9625}
9626
9627/* node.direct.tonode */
9628
9629# define tonode_usage common_usage
9630
9631static int nodelib_shared_tonode(lua_State* L)
9632{
9633    halfword n = nodelib_valid_direct_from_index(L, 1);
9634    if (n) {
9635        halfword* a = (halfword*) lua_newuserdatauv(L, sizeof(halfword), 0);
9636        *a = n;
9637        lua_get_metatablelua(node_instance);
9638        lua_setmetatable(L, -2);
9639    } /* else assume node and return argument */
9640    return 1;
9641}
9642
9643/* direct.ischar  */
9644/* direct.isglyph */
9645
9646/*tex
9647
9648    This can save a lookup call, but although there is a little benefit it doesn't pay of in the end
9649    as we have to simulate it in \MKIV.
9650
9651    \starttyping
9652    if (glyph_data(n) != unused_attribute_value) {
9653        lua_pushinteger(L, glyph_data(n));
9654        return 2;
9655    }
9656    \stoptyping
9657
9658    possible return values:
9659
9660    \starttyping
9661    <nil when no node>
9662    <nil when no glyph> <id of node>
9663    <false when glyph and already marked as done or when not>
9664    <character code when font matches or when no font passed>
9665    \stoptyping
9666
9667    data  : when checked should be equal, false or nil is zero
9668    state : when checked should be equal, unless false or zero
9669
9670*/
9671
9672static int nodelib_direct_check_char(lua_State* L, halfword n)
9673{
9674    if (! glyph_protected(n)) {
9675        halfword b = 0;
9676        halfword f = (halfword) lua_tointegerx(L, 2, &b);
9677        if (! b) {
9678            goto OKAY;
9679        } else if (f == glyph_font(n)) {
9680            switch (lua_gettop(L)) {
9681                case 2:
9682                    /* (node,font) */
9683                    goto OKAY;
9684                case 3:
9685                    /* (node,font,data) */
9686                    if ((halfword) lua_tointegerx(L, 3, NULL) == glyph_data(n)) {
9687                        goto OKAY;
9688                    } else {
9689                        break;
9690                    }
9691                case 4:
9692                    /* (node,font,data,state) */
9693                    if ((halfword) lua_tointegerx(L, 3, NULL) == glyph_data(n)) {
9694                        halfword state = (halfword) lua_tointegerx(L, 4, NULL);
9695                        if (! state || state == glyph_state(n)) {
9696                            goto OKAY;
9697                        } else {
9698                            break;
9699                        }
9700                    } else {
9701                        break;
9702                    }
9703                case 5:
9704                    /* (node,scale,xscale,yscale) */
9705                    if (lua_tointeger(L, 3) == glyph_scale(n) && lua_tointeger(L, 4) == glyph_x_scale(n) && lua_tointeger(L, 5) == glyph_y_scale(n)) {
9706                        goto OKAY;
9707                    } else {
9708                        break;
9709                    }
9710                case 6:
9711                    /* (node,font,data,scale,xscale,yscale) */
9712                    if (lua_tointegerx(L, 3, NULL) == glyph_data(n) && lua_tointeger(L, 4) == glyph_scale(n) &&  lua_tointeger(L, 5) == glyph_x_scale(n) && lua_tointeger(L, 6) == glyph_y_scale(n)) {
9713                        goto OKAY;
9714                    } else {
9715                        break;
9716                    }
9717                /* case 7: */
9718                    /* (node,font,data,state,scale,xscale,yscale)*/
9719            }
9720        }
9721    }
9722    return -1;
9723  OKAY:
9724    return glyph_character(n);
9725}
9726
9727# define ischar_usage         common_usage
9728# define isnextchar_usage     common_usage
9729# define isprevchar_usage     common_usage
9730
9731static int nodelib_direct_ischar(lua_State *L)
9732{
9733    halfword n = nodelib_valid_direct_from_index(L, 1);
9734    if (n) {
9735        if (node_type(n) != glyph_node) {
9736            lua_pushnil(L);
9737            lua_pushinteger(L, node_type(n));
9738            return 2;
9739        } else {
9740            halfword chr = nodelib_direct_check_char(L, n);
9741            if (chr >= 0) {
9742                lua_pushinteger(L, chr);
9743            } else {
9744                lua_pushboolean(L, 0);
9745            }
9746            return 1;
9747        }
9748    } else {
9749        lua_pushnil(L);
9750        return 1;
9751    }
9752}
9753
9754/*
9755    This one is kind of special and is a way to quickly test what we are at now and what is
9756    coming. It saves some extra calls but has a rather hybrid set of return values, depending
9757    on the situation:
9758
9759    \starttyping
9760    isnextchar(n,[font],[data],[state],[scale,xscale,yscale])
9761    isprevchar(n,[font],[data],[state],[scale,xscale,yscale])
9762
9763    glyph     : nil | next false | next char | next char nextchar
9764    otherwise : nil | next false id
9765    \stoptyping
9766
9767    Beware: it is not always measurable faster than multiple calls but it can make code look a
9768    bit better (at least in \CONTEXT\ where we can use it a few times). There are more such
9769    hybrid helpers where the return value depends on the node type.
9770
9771    The second glyph is okay when the most meaningful properties are the same. We assume that
9772    states can differ so we don't check for that. One of the few assumptions when using
9773    \CONTEXT.
9774
9775*/
9776
9777static inline int nodelib_aux_similar_glyph(halfword first, halfword second)
9778{
9779    return
9780        node_type(second)     == glyph_node
9781     && glyph_font(second)    == glyph_font(first)
9782     && glyph_data(second)    == glyph_data(first)
9783  /* && glyph_state(second)   == glyph_state(first) */
9784     && glyph_scale(second)   == glyph_scale(first)
9785     && glyph_x_scale(second) == glyph_x_scale(first)
9786     && glyph_y_scale(second) == glyph_y_scale(first)
9787     && glyph_slant(second)   == glyph_slant(first)
9788     && glyph_weight(second)  == glyph_weight(first)
9789    ;
9790}
9791
9792# define issimilarglyph_usage common_usage
9793
9794static int nodelib_direct_issimilarglyph(lua_State *L)
9795{
9796    halfword first = nodelib_valid_direct_from_index(L, 1);
9797    halfword second = nodelib_valid_direct_from_index(L, 2);
9798    lua_pushboolean(L, nodelib_aux_similar_glyph(first, second));
9799    return 1;
9800}
9801
9802static int nodelib_direct_isnextchar(lua_State *L)
9803{
9804    halfword n = nodelib_valid_direct_from_index(L, 1);
9805    if (n) {
9806        /* beware, don't mix push and pop */
9807        halfword nxt = node_next(n);
9808        if (node_type(n) != glyph_node) {
9809            nodelib_push_direct_or_nil(L, nxt);
9810            lua_pushnil(L);
9811            lua_pushinteger(L, node_type(n));
9812            return 3;
9813        } else {
9814            halfword chr = nodelib_direct_check_char(L, n);
9815            nodelib_push_direct_or_nil(L, nxt);
9816            if (chr >= 0) {
9817                lua_pushinteger(L, chr);
9818                if (nxt && nodelib_aux_similar_glyph(n, nxt)) {
9819                    lua_pushinteger(L, glyph_character(nxt));
9820                    return 3;
9821                }
9822            } else {
9823                lua_pushboolean(L, 0);
9824            }
9825            return 2;
9826        }
9827    } else {
9828        return 0;
9829    }
9830}
9831
9832static int nodelib_direct_isprevchar(lua_State *L)
9833{
9834    halfword n = nodelib_valid_direct_from_index(L, 1);
9835    if (n) {
9836        /* beware, don't mix push and pop */
9837        halfword prv = node_prev(n);
9838        if (node_type(n) != glyph_node) {
9839            nodelib_push_direct_or_nil(L, prv);
9840            lua_pushnil(L);
9841            lua_pushinteger(L, node_type(n));
9842            return 3;
9843        } else {
9844            halfword chr = nodelib_direct_check_char(L, n);
9845            nodelib_push_direct_or_nil(L, prv);
9846            if (chr >= 0) {
9847                lua_pushinteger(L, chr);
9848                if (prv && nodelib_aux_similar_glyph(n, prv)) {
9849                    lua_pushinteger(L, glyph_character(prv));
9850                    return 3;
9851                }
9852            } else {
9853                lua_pushboolean(L, 0);
9854            }
9855            return 2;
9856        }
9857    } else {
9858        return 0;
9859    }
9860}
9861
9862# define isglyph_usage     common_usage
9863# define isnextglyph_usage common_usage
9864# define isprevglyph_usage common_usage
9865
9866static int nodelib_direct_isglyph(lua_State *L)
9867{
9868    halfword n = nodelib_valid_direct_from_index(L, 1);
9869    if (n) {
9870        if (node_type(n) != glyph_node) {
9871            lua_pushboolean(L, 0);
9872            lua_pushinteger(L, node_type(n));
9873        } else {
9874            /* protected as well as unprotected */
9875            lua_pushinteger(L, glyph_character(n));
9876            lua_pushinteger(L, glyph_font(n));
9877        }
9878    } else {
9879        lua_pushnil(L); /* no glyph at all */
9880        lua_pushnil(L); /* no glyph at all */
9881    }
9882    return 2;
9883}
9884
9885static int nodelib_direct_isnextglyph(lua_State *L)
9886{
9887    halfword n = nodelib_valid_direct_from_index(L, 1);
9888    if (n) {
9889        nodelib_push_direct_or_nil(L, node_next(n));
9890        if (node_type(n) != glyph_node) {
9891            lua_pushboolean(L, 0);
9892            lua_pushinteger(L, node_type(n));
9893        } else {
9894            /* protected as well as unprotected */
9895            lua_pushinteger(L, glyph_character(n));
9896            lua_pushinteger(L, glyph_font(n));
9897        }
9898        return 3;
9899    } else {
9900        return 0;
9901    }
9902}
9903
9904static int nodelib_direct_isprevglyph(lua_State *L)
9905{
9906    halfword n = nodelib_valid_direct_from_index(L, 1);
9907    if (n) {
9908        nodelib_push_direct_or_nil(L, node_prev(n));
9909        if (node_type(n) != glyph_node) {
9910            lua_pushboolean(L, 0);
9911            lua_pushinteger(L, node_type(n));
9912        } else {
9913            /* protected as well as unprotected */
9914            lua_pushinteger(L, glyph_character(n));
9915            lua_pushinteger(L, glyph_font(n));
9916        }
9917        return 3;
9918    } else {
9919        return 0;
9920    }
9921}
9922
9923/* direct.usesfont */
9924
9925static inline int nodelib_aux_uses_font_disc(lua_State *L, halfword n, halfword font)
9926{
9927    if (font) {
9928        while (n) {
9929            if ((node_type(n) == glyph_node) && (glyph_font(n) == font)) {
9930                lua_pushboolean(L, 1);
9931                return 1;
9932            }
9933            n = node_next(n);
9934        }
9935    } else {
9936        while (n) {
9937            if (node_type(n) == glyph_node) {
9938                lua_pushinteger(L, glyph_font(n));
9939                return 1;
9940            }
9941            n = node_next(n);
9942        }
9943    }
9944    return 0;
9945}
9946
9947# define usesfont_usage common_usage
9948
9949static int nodelib_direct_usesfont(lua_State *L)
9950{
9951    halfword n = nodelib_valid_direct_from_index(L, 1);
9952    if (n) {
9953        halfword f = lmt_opthalfword(L, 2, 0);
9954        switch (node_type(n)) {
9955            case glyph_node:
9956                if (f) {
9957                    lua_pushboolean(L, glyph_font(n) == f);
9958                } else {
9959                    lua_pushinteger(L, glyph_font(n));
9960                }
9961                return 1;
9962            case disc_node:
9963                if (nodelib_aux_uses_font_disc(L, disc_pre_break_head(n), f)) {
9964                    return 1;
9965                } else if (nodelib_aux_uses_font_disc(L, disc_post_break_head(n), f)) {
9966                    return 1;
9967                } else if (nodelib_aux_uses_font_disc(L, disc_no_break_head(n), f)) {
9968                    return 1;
9969                }
9970                /*
9971                {
9972                    halfword c = disc_pre_break_head(n);
9973                    while (c) {
9974                        if (type(c) == glyph_node && font(c) == f) {
9975                            lua_pushboolean(L, 1);
9976                            return 1;
9977                        }
9978                        c = node_next(c);
9979                    }
9980                    c = disc_post_break_head(n);
9981                    while (c) {
9982                        if (type(c) == glyph_node && font(c) == f) {
9983                            lua_pushboolean(L, 1);
9984                            return 1;
9985                        }
9986                        c = node_next(c);
9987                    }
9988                    c = disc_no_break_head(n);
9989                    while (c) {
9990                        if (type(c) == glyph_node && font(c) == f) {
9991                            lua_pushboolean(L, 1);
9992                            return 1;
9993                        }
9994                        c = node_next(c);
9995                    }
9996                }
9997                */
9998                break;
9999            /* todo: other node types */
10000        }
10001    }
10002    lua_pushboolean(L, 0);
10003    return 1;
10004}
10005
10006/* boxes */
10007
10008/* node.getbox = tex.getbox */
10009/* node.setbox = tex.setbox */
10010
10011/* node.direct.getbox */
10012/* node.direct.setbox */
10013
10014# define getbox_usage common_usage
10015# define setbox_usage common_usage
10016
10017static int nodelib_direct_getbox(lua_State *L)
10018{
10019    int id = lmt_get_box_id(L, 1, 1);
10020    if (id >= 0) {
10021        int t = tex_get_tex_box_register(id, 0);
10022        if (t) {
10023            lua_pushinteger(L, t);
10024            return 1;
10025        }
10026    }
10027    lua_pushnil(L);
10028    return 1;
10029}
10030
10031static int nodelib_direct_setbox(lua_State *L)
10032{
10033    int flags = 0;
10034    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
10035    int id = lmt_get_box_id(L, slot++, 1);
10036    if (id >= 0) {
10037        int n;
10038        switch (lua_type(L, slot)) {
10039            case LUA_TBOOLEAN:
10040                {
10041                    n = lua_toboolean(L, slot);
10042                    if (n == 0) {
10043                        n = null;
10044                    } else {
10045                        return 0;
10046                    }
10047                }
10048                break;
10049            case LUA_TNIL:
10050                n = null;
10051                break;
10052            default:
10053                {
10054                    n = nodelib_valid_direct_from_index(L, slot);
10055                    if (n) {
10056                        switch (node_type(n)) {
10057                            case hlist_node:
10058                            case vlist_node:
10059                                break;
10060                            default:
10061                                /*tex Alternatively we could |hpack|. */
10062                                return luaL_error(L, "setbox: incompatible node type (%s)\n",get_node_name(node_type(n)));
10063                        }
10064                    }
10065                }
10066                break;
10067        }
10068        tex_set_tex_box_register(id, n, flags, 0);
10069    }
10070    return 0;
10071}
10072
10073/* node.isnode(n) */
10074
10075static int nodelib_userdata_isnode(lua_State *L)
10076{
10077    halfword n = lmt_maybe_isnode(L, 1);
10078    if (n) {
10079        lua_pushinteger (L, n);
10080    } else {
10081        lua_pushboolean (L, 0);
10082    }
10083    return 1;
10084}
10085
10086/* node.direct.isdirect(n) (handy for mixed usage testing) */
10087
10088# define isdirect_usage common_usage
10089# define isnode_usage   common_usage
10090
10091static int nodelib_direct_isdirect(lua_State *L)
10092{
10093    if (lua_type(L, 1) != LUA_TNUMBER) {
10094        lua_pushboolean(L, 0); /* maybe valid test too */
10095    }
10096    /* else return direct */
10097    return 1;
10098}
10099
10100/* node.direct.isnode(n) (handy for mixed usage testing) */
10101
10102static int nodelib_direct_isnode(lua_State *L)
10103{
10104    if (! lmt_maybe_isnode(L, 1)) {
10105        lua_pushboolean(L, 0);
10106    } else {
10107        /*tex Assume and return node. */
10108    }
10109    return 1;
10110}
10111
10112/*tex Maybe we should allocate a proper index |0 .. var_mem_max| but not now. */
10113
10114static int nodelib_userdata_getproperty(lua_State *L)
10115{   /* <node> */
10116    halfword n = lmt_check_isnode(L, 1);
10117    if (n) {
10118        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
10119        lua_rawgeti(L, -1, n); /* actually it is a hash */
10120    } else {
10121        lua_pushnil(L);
10122    }
10123    return 1;
10124}
10125
10126# define getproperty_usage common_usage
10127# define setproperty_usage common_usage
10128
10129static int nodelib_direct_getproperty(lua_State *L)
10130{   /* <direct> */
10131    halfword n = nodelib_valid_direct_from_index(L, 1);
10132    if (n) {
10133        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
10134        lua_rawgeti(L, -1, n); /* actually it is a hash */
10135    } else {
10136        lua_pushnil(L);
10137    }
10138    return 1;
10139}
10140
10141static int nodelib_userdata_setproperty(lua_State *L)
10142{
10143    /* <node> <value> */
10144    halfword n = lmt_check_isnode(L, 1);
10145    if (n) {
10146        lua_settop(L, 2);
10147        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
10148        /* <node> <value> <propertytable> */
10149        lua_replace(L, -3);
10150        /* <propertytable> <value> */
10151        lua_rawseti(L, -2, n); /* actually it is a hash */
10152    }
10153    return 0;
10154}
10155
10156static int nodelib_direct_setproperty(lua_State *L)
10157{
10158    /* <direct> <value> */
10159    halfword n = nodelib_valid_direct_from_index(L, 1);
10160    if (n) {
10161        lua_settop(L, 2);
10162        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
10163        /* <node> <value> <propertytable> */
10164        lua_replace(L, 1);
10165        /* <propertytable> <value> */
10166        lua_rawseti(L, 1, n); /* actually it is a hash */
10167    }
10168    return 0;
10169}
10170
10171/*tex
10172
10173    These two getters are kind of tricky as they can mess up the otherwise hidden table. But
10174    normally these are under control of the macro package so we can control it somewhat.
10175
10176*/
10177
10178static int nodelib_direct_getpropertiestable(lua_State *L)
10179{   /* <node|direct> */
10180    if (lua_toboolean(L, lua_gettop(L))) {
10181        /*tex Beware: this can have side effects when used without care. */
10182        lmt_initialize_properties(1);
10183    }
10184    lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
10185    return 1;
10186}
10187
10188static int nodelib_userdata_getpropertiestable(lua_State *L)
10189{   /* <node|direct> */
10190    lua_get_metatablelua(node_properties_indirect);
10191    return 1;
10192}
10193
10194/* extra helpers */
10195
10196static void nodelib_direct_effect_done(lua_State *L, halfword amount, halfword stretch, halfword shrink, halfword stretch_order, halfword shrink_order)
10197{
10198    halfword parent = nodelib_valid_direct_from_index(L, 2);
10199    if (parent) {
10200        singleword sign = box_glue_sign(parent);
10201        if (sign != normal_glue_sign) {
10202            switch (node_type(parent)) {
10203                case hlist_node:
10204                case vlist_node:
10205                    {
10206                        double w = (double) amount;
10207                        switch (sign) {
10208                            case stretching_glue_sign:
10209                                if (stretch_order == box_glue_order(parent)) {
10210                                    w += stretch * (double) box_glue_set(parent);
10211                                }
10212                                break;
10213                            case shrinking_glue_sign:
10214                                if (shrink_order == box_glue_order(parent)) {
10215                                    w -= shrink * (double) box_glue_set(parent);
10216                                }
10217                                break;
10218                        }
10219                        if (lua_toboolean(L, 3)) {
10220                            lua_pushinteger(L, lmt_roundedfloat(w));
10221                        } else {
10222                            lua_pushnumber(L, w);
10223                        }
10224                        return;
10225                    }
10226            }
10227        }
10228    }
10229    lua_pushinteger(L, amount);
10230}
10231
10232# define effectiveglue_usage (glue_usage | math_usage)
10233
10234static int nodelib_direct_effectiveglue(lua_State *L)
10235{
10236    halfword n = nodelib_valid_direct_from_index(L, 1);
10237    if (n) {
10238        switch (node_type(n)) {
10239            case glue_node:
10240                nodelib_direct_effect_done(L, glue_amount(n), glue_stretch(n), glue_shrink(n), glue_stretch_order(n), glue_shrink_order(n));
10241                break;
10242            case math_node:
10243                if (math_surround(n)) {
10244                    lua_pushinteger(L, math_surround(n));
10245                } else {
10246                    nodelib_direct_effect_done(L, math_amount(n), math_stretch(n), math_shrink(n), math_stretch_order(n), math_shrink_order(n));
10247                }
10248                break;
10249            default:
10250                lua_pushinteger(L, 0);
10251                break;
10252        }
10253    } else {
10254        lua_pushinteger(L, 0);
10255    }
10256    return 1;
10257}
10258
10259/*tex
10260
10261    Disc nodes are kind of special in the sense that their head is not the head as we see it, but
10262    a special node that has status info of which head and tail are part. Normally when proper
10263    set/get functions are used this status node is all right but if a macro package permits
10264    arbitrary messing around, then it can at some point call the following cleaner, just before
10265    linebreaking kicks in. This one is not called automatically because if significantly slows down
10266    the line break routing.
10267
10268*/
10269
10270# define checkdiscretionaries_usage common_usage
10271# define checkdiscretionary_usage   disc_usage
10272
10273static int nodelib_direct_checkdiscretionaries(lua_State *L) {
10274    halfword n = nodelib_valid_direct_from_index(L, 1);
10275    if (n) {
10276        while (n) {
10277            if (node_type(n) == disc_node) {
10278                tex_check_disc_field(n);
10279            }
10280            n = node_next(n) ;
10281        }
10282    }
10283    return 0;
10284}
10285
10286static int nodelib_direct_checkdiscretionary(lua_State *L) {
10287    halfword n = nodelib_valid_direct_from_index(L, 1);
10288    if (n && node_type(n) == disc_node) {
10289        halfword p = disc_pre_break_head(n);
10290        disc_pre_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
10291        p = disc_post_break_head(n);
10292        disc_post_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
10293        p = disc_no_break_head(n);
10294        disc_no_break_tail(n) = p ? tex_tail_of_node_list(p) : null;
10295    }
10296    return 0;
10297}
10298
10299# define flattendiscretionaries_usage common_usage
10300
10301static int nodelib_direct_flattendiscretionaries(lua_State *L)
10302{
10303    int count = 0;
10304    halfword head = nodelib_valid_direct_from_index(L, 1);
10305    if (head) {
10306        head = tex_flatten_discretionaries(head, &count, lua_toboolean(L, 2)); /* nest */
10307    } else {
10308        head = null;
10309    }
10310    nodelib_push_direct_or_nil(L, head);
10311    lua_pushinteger(L, count);
10312    return 2;
10313}
10314
10315# define softenhyphens_usage common_usage
10316
10317static int nodelib_direct_softenhyphens(lua_State *L)
10318{
10319    int found = 0;
10320    int replaced = 0;
10321    halfword head = nodelib_valid_direct_from_index(L, 1);
10322    if (head) {
10323        tex_soften_hyphens(head, &found, &replaced);
10324    }
10325    nodelib_push_direct_or_nil(L, head);
10326    lua_pushinteger(L, found);
10327    lua_pushinteger(L, replaced);
10328    return 3;
10329}
10330
10331/*tex The fields related to input tracking: */
10332
10333# define getinputfields_usage (glyph_usage | hlist_usage | vlist_usage | unset_usage)
10334# define setinputfields_usage getinputfields_usage
10335
10336static int nodelib_direct_getinputfields(lua_State *L)
10337{
10338    halfword n = nodelib_valid_direct_from_index(L, 1);
10339    if (n) {
10340        switch (node_type(n)) {
10341            case glyph_node:
10342                lua_pushinteger(L, glyph_input_file(n));
10343                lua_pushinteger(L, glyph_input_line(n));
10344                break;
10345            case hlist_node:
10346            case vlist_node:
10347            case unset_node:
10348                lua_pushinteger(L, box_input_file(n));
10349                lua_pushinteger(L, box_input_line(n));
10350                break;
10351            default:
10352                return 0;
10353        }
10354        return 2;
10355    }
10356    return 0;
10357}
10358
10359static int nodelib_direct_setinputfields(lua_State *L)
10360{
10361    halfword n = nodelib_valid_direct_from_index(L, 1);
10362    if (n) {
10363        /* there is no need to test for tag and line as two arguments are mandate */
10364        halfword tag = lmt_tohalfword(L, 2);
10365        halfword line = lmt_tohalfword(L, 3);
10366        switch (node_type(n)) {
10367            case glyph_node:
10368                glyph_input_file(n)  = tag;
10369                glyph_input_line(n) = line;
10370                break;
10371            case hlist_node:
10372            case vlist_node:
10373            case unset_node:
10374                box_input_file(n)  = tag;
10375                box_input_line(n) = line;
10376                break;
10377        }
10378    }
10379    return 0;
10380}
10381
10382/*tex
10383    Likely we pas the wrong |chr| here as we're after the analysis phase. Buit we don't use this
10384    helper any longer (it seems).
10385*/
10386
10387static int nodelib_direct_makeextensible(lua_State *L)
10388{
10389    int top = lua_gettop(L);
10390    if (top >= 3) {
10391        halfword fnt = lmt_tohalfword(L, 1);
10392        halfword chr = lmt_tohalfword(L, 2);
10393        halfword target = lmt_tohalfword(L, 3);
10394        halfword size = lmt_opthalfword(L, 4, 0);
10395        halfword overlap = lmt_opthalfword(L, 5, 65536);
10396        halfword attlist = null;
10397        halfword b = null;
10398        int horizontal = 0;
10399        if (top >= 4) {
10400            overlap = lmt_tohalfword(L, 4);
10401            if (top >= 5) {
10402                horizontal = lua_toboolean(L, 5);
10403                if (top >= 6) {
10404                    attlist = nodelib_valid_direct_from_index(L, 6);
10405                }
10406            }
10407        }
10408        b = tex_make_extensible(fnt, chr, target, overlap, horizontal, attlist, size);
10409        lua_pushinteger(L, b);
10410    } else {
10411        lua_pushnil(L);
10412    }
10413    return 1;
10414}
10415
10416# define flattenleaders_usage common_usage
10417
10418static int nodelib_direct_flattenleaders(lua_State *L)
10419{
10420    halfword n = nodelib_valid_direct_from_index(L, 1);
10421    int count = 0;
10422    if (n) {
10423        switch (node_type(n)) {
10424            case hlist_node:
10425                count = tex_flatten_leaders(n, hbox_group, 0, uleader_lua, 0);
10426                break;
10427            case vlist_node:
10428                count = tex_flatten_leaders(n, vbox_group, 0, uleader_lua, 0);
10429                break;
10430        }
10431    }
10432    lua_pushinteger(L, count);
10433    return 1;
10434}
10435
10436/*tex test */
10437
10438# define isvalid_usage common_usage
10439
10440static int nodelib_direct_isvalid(lua_State *L)
10441{
10442    lua_pushboolean(L, nodelib_valid_direct_from_index(L, 1));
10443    return 1;
10444}
10445
10446/* getlinestuff : LS RS LH RH ID PF FIRST LAST */
10447
10448static inline scaled set_effective_width(halfword source, halfword sign, halfword order, double glue)
10449{
10450    scaled amount = glue_amount(source);
10451    switch (sign) {
10452        case stretching_glue_sign:
10453            if (glue_stretch_order(source) == order) {
10454                return amount + scaledround((double) glue_stretch(source) * glue);
10455            } else {
10456                break;
10457            }
10458        case shrinking_glue_sign:
10459            if (glue_shrink_order(source) == order) {
10460                return amount + scaledround((double) glue_shrink(source) * glue);
10461            } else {
10462                break;
10463            }
10464    }
10465    return amount;
10466}
10467
10468# define getnormalizedline_usage hlist_usage
10469
10470static int nodelib_direct_getnormalizedline(lua_State *L)
10471{
10472    halfword n = nodelib_valid_direct_from_index(L, 1);
10473    if (n && node_type(n) == hlist_node && node_subtype(n) == line_list) {
10474        halfword head = box_list(n);
10475        halfword tail = head;
10476        halfword first = head;    /* the first special glue before the content */
10477        halfword last = tail;     /* the first special glue after the content */
10478        halfword current = head;
10479        scaled ls = 0;
10480        scaled rs = 0;
10481        scaled is = 0;
10482        scaled pr = 0;
10483        scaled pl = 0;
10484        scaled ir = 0;
10485        scaled il = 0;
10486        scaled lh = 0;
10487        scaled rh = 0;
10488        scaled cs = 0;
10489        singleword sign = box_glue_sign(n);
10490        halfword order = box_glue_order(n);
10491        double glue = box_glue_set(n);
10492        int details = lua_toboolean(L, 2);
10493        while (current) {
10494            tail = current ;
10495            if (node_type(current) == glue_node) {
10496                switch (node_subtype(current)) {
10497                    case left_skip_glue           : ls = set_effective_width(current, sign, order, glue); break; // first = current; break;
10498                    case right_skip_glue          : rs = set_effective_width(current, sign, order, glue); break; // if (last == tail) { last = current; } break;
10499                    case par_fill_left_skip_glue  : pl = set_effective_width(current, sign, order, glue); break; // first = current; break;
10500                    case par_fill_right_skip_glue : pr = set_effective_width(current, sign, order, glue); break; // if (last == tail) { last = current; } break;
10501                    case par_init_left_skip_glue  : il = set_effective_width(current, sign, order, glue); break; // first = current; break;
10502                    case par_init_right_skip_glue : ir = set_effective_width(current, sign, order, glue); break; // if (last == tail) { last = current; } break;
10503                    case indent_skip_glue         : is = set_effective_width(current, sign, order, glue); break; // first = current; break;
10504                    case left_hang_skip_glue      : lh = set_effective_width(current, sign, order, glue); break; // first = current; break;
10505                    case right_hang_skip_glue     : rh = set_effective_width(current, sign, order, glue); break; // if (last == tail) { last = current; } break;
10506                    case correction_skip_glue     : cs = set_effective_width(current, sign, order, glue); break; // break;
10507                }
10508            }
10509            current = node_next(current);
10510        }
10511        tex_get_line_content_range(head, tail, &first, &last);
10512        lua_createtable(L, 0, 14); /* we could add some more */
10513        lua_push_integer_at_key(L, leftskip, ls);
10514        lua_push_integer_at_key(L, rightskip, rs);
10515        lua_push_integer_at_key(L, lefthangskip, lh);
10516        lua_push_integer_at_key(L, righthangskip, rh);
10517        lua_push_integer_at_key(L, indent, is);
10518        lua_push_integer_at_key(L, parfillleftskip, pl);
10519        lua_push_integer_at_key(L, parfillrightskip, pr);
10520        lua_push_integer_at_key(L, parinitleftskip, il);
10521        lua_push_integer_at_key(L, parinitrightskip, ir);
10522        lua_push_integer_at_key(L, correctionskip, cs);
10523        lua_push_integer_at_key(L, first, first); /* points to a skip */
10524        lua_push_integer_at_key(L, last, last);   /* points to a skip */
10525        lua_push_integer_at_key(L, head, head);
10526        lua_push_integer_at_key(L, tail, tail);
10527        if (details) {
10528            scaled ns = tex_natural_hsize(box_list(n), &cs); /* todo: check if cs is the same */
10529            lua_push_integer_at_key(L, width, box_width(n));
10530            lua_push_integer_at_key(L, height, box_height(n));
10531            lua_push_integer_at_key(L, depth, box_depth(n));
10532            lua_push_integer_at_key(L, left, ls + lh + pl + il);
10533            lua_push_integer_at_key(L, right, rs + rh + pr + ir);
10534            lua_push_integer_at_key(L, size, ns);
10535        }
10536        return 1;
10537    }
10538    return 0;
10539}
10540
10541/*tex new */
10542
10543# define ignoremathskip_usage math_usage
10544
10545static int nodelib_direct_ignoremathskip(lua_State *L)
10546{
10547    halfword n = nodelib_valid_direct_from_index(L, 1);
10548    if (n && node_type(n) == math_node) {
10549        lua_pushboolean(L, tex_ignore_math_skip(n));
10550    } else {
10551        lua_pushboolean(L, 0);
10552    }
10553    return 1;
10554}
10555
10556# define reverse_usage common_usage
10557
10558static int nodelib_direct_reverse(lua_State *L)
10559{
10560    halfword n = nodelib_valid_direct_from_index(L, 1);
10561    if (n) {
10562        n = tex_reversed_node_list(n);
10563    }
10564    nodelib_push_direct_or_nil(L, n);
10565    return 1;
10566}
10567
10568# define exchange_usage common_usage
10569
10570static int nodelib_direct_exchange(lua_State *L)
10571{
10572    halfword head = nodelib_valid_direct_from_index(L, 1);
10573    if (head) {
10574        halfword first = nodelib_valid_direct_from_index(L, 2);
10575        if (first) {
10576            halfword second = nodelib_valid_direct_from_index(L, 3);
10577            if (! second) {
10578                second = node_next(first);
10579            }
10580            if (second) {
10581                halfword pf = node_prev(first);
10582                halfword ns = node_next(second);
10583                if (first == head) {
10584                    head = second;
10585                } else if (second == head) {
10586                    head = first;
10587                }
10588                if (second == node_next(first)) {
10589                    node_prev(first) = second;
10590                    node_next(second) = first;
10591                } else {
10592                    halfword nf = node_next(first);
10593                    halfword ps = node_prev(second);
10594                    node_prev(first) = ps;
10595                    if (ps) {
10596                        node_next(ps) = first;
10597                    }
10598                    node_next(second) = nf;
10599                    if (nf) {
10600                        node_prev(nf) = second;
10601                    }
10602                }
10603                node_next(first) = ns;
10604                node_prev(second) = pf;
10605                if (pf) {
10606                    node_next(pf) = second;
10607                }
10608                if (ns) {
10609                    node_prev(ns) = first;
10610                }
10611            }
10612        }
10613    }
10614    nodelib_push_direct_or_nil(L, head);
10615    return 1;
10616}
10617
10618/*tex experiment */
10619
10620# define migrate_usage (hlist_usage | vlist_usage | insert_usage)
10621
10622static int nodelib_direct_migrate(lua_State *L)
10623{
10624    halfword head = nodelib_valid_direct_from_index(L, 1);
10625    if (head) {
10626        int inserts = lua_type(L, 3) == LUA_TBOOLEAN ? lua_toboolean(L, 2) : 1;
10627        int marks = lua_type(L, 2) == LUA_TBOOLEAN ? lua_toboolean(L, 3) : 1;
10628        halfword first = null;
10629        halfword last = null;
10630        tex_migrate(head, &first, &last, inserts, marks);
10631        nodelib_push_direct_or_nil(L, head);
10632        nodelib_push_direct_or_nil(L, first);
10633        nodelib_push_direct_or_nil(L, last);
10634        return 3;
10635    }
10636    return 0;
10637}
10638
10639/*tex experiment */
10640
10641static int nodelib_aux_no_left(halfword n, halfword l, halfword r)
10642{
10643    if (tex_has_glyph_option(n, (singleword) l)) {
10644        return 1;
10645    } else {
10646        n = node_prev(n);
10647        if (n) {
10648            if (node_type(n) == disc_node) {
10649                n = disc_no_break_tail(n);
10650            }
10651            if (n && node_type(n) == glyph_node && tex_has_glyph_option(n, (singleword) r)) {
10652                return 1;
10653            }
10654        }
10655    }
10656    return 0;
10657}
10658
10659static int nodelib_aux_no_right(halfword n, halfword r, halfword l)
10660{
10661    if (tex_has_glyph_option(n, (singleword) r)) {
10662        return 1;
10663    } else {
10664        n = node_next(n);
10665        if (node_type(n) == disc_node) {
10666            n = disc_no_break_head(n);
10667        }
10668        if (n && node_type(n) == glyph_node && tex_has_glyph_option(n, (singleword) l)) {
10669            return 1;
10670        }
10671    }
10672    return 0;
10673}
10674
10675/*tex This one also checks is we have a glyph. */
10676
10677# define hasglyphoption_usage glyph_usage
10678
10679static int nodelib_direct_hasglyphoption(lua_State *L)
10680{
10681    halfword current = nodelib_valid_direct_from_index(L, 1);
10682    int result = 0;
10683    if (current && node_type(current) == glyph_node) {
10684        int option = lmt_tointeger(L, 2);
10685        switch (option) {
10686            case glyph_option_normal_glyph:
10687                break;
10688            case glyph_option_no_left_ligature:
10689                result = nodelib_aux_no_left(current, glyph_option_no_left_ligature, glyph_option_no_right_ligature);
10690                break;
10691            case glyph_option_no_right_ligature:
10692                result = nodelib_aux_no_right(current, glyph_option_no_right_ligature, glyph_option_no_left_ligature);
10693                break;
10694            case glyph_option_no_left_kern:
10695                result = nodelib_aux_no_left(current, glyph_option_no_left_kern, glyph_option_no_right_kern);
10696                break;
10697            case glyph_option_no_right_kern:
10698                result = nodelib_aux_no_right(current, glyph_option_no_right_kern, glyph_option_no_left_kern);
10699                break;
10700            case glyph_option_no_expansion:
10701            case glyph_option_no_protrusion:
10702            case glyph_option_apply_x_offset:
10703            case glyph_option_apply_y_offset:
10704                /* todo */
10705                break;
10706            case glyph_option_no_italic_correction:
10707            case glyph_option_no_zero_italic_correction:
10708            case glyph_option_math_discretionary:
10709            case glyph_option_math_italics_too:
10710                result = tex_has_glyph_option(current, option);
10711                break;
10712            default:
10713                /* user options */
10714                result = tex_has_glyph_option(current, option);
10715        }
10716    }
10717    lua_pushboolean(L, result);
10718    return 1;
10719}
10720
10721# define hasdiscoption_usage disc_usage
10722
10723static int nodelib_direct_hasdiscoption(lua_State *L)
10724{
10725    halfword current = nodelib_valid_direct_from_index(L, 1);
10726    int result = 0;
10727    if (current && node_type(current) == disc_node) {
10728        /*tex No specific checking, maybe some day check of pre/post/replace. */
10729        result = tex_has_disc_option(current, lmt_tointeger(L, 2));
10730    }
10731    lua_pushboolean(L, result);
10732    return 1;
10733}
10734
10735# define isitalicglyph_usage glyph_usage
10736
10737static int nodelib_direct_isitalicglyph(lua_State *L)
10738{
10739    halfword n = nodelib_valid_direct_from_index(L, 1);
10740    lua_pushboolean(L,
10741        n && node_type(n) == glyph_node && ! tex_has_glyph_option(n, glyph_option_no_italic_correction) && (
10742            glyph_slant(n)
10743         || has_font_text_control(glyph_font(n), text_control_has_italics)
10744         || has_font_text_control(glyph_font(n), text_control_auto_italics)
10745        )
10746    );
10747    return 1;
10748}
10749
10750# define firstitalicglyph_usage common_usage
10751
10752static int nodelib_direct_firstitalicglyph(lua_State *L)
10753{
10754    halfword n = nodelib_valid_direct_from_index(L, 1);
10755    int kerntoo = lua_toboolean(L, 2);
10756    while (n) {
10757        switch (node_type(n)) {
10758            case glyph_node:
10759                if (! tex_has_glyph_option(n, glyph_option_no_italic_correction) && (glyph_slant(n)
10760                     || has_font_text_control(glyph_font(n), text_control_has_italics)
10761                     || has_font_text_control(glyph_font(n), text_control_auto_italics)
10762                   )) {
10763                    goto DONE;
10764                }
10765                break;
10766            case kern_node:
10767                if (kerntoo) {
10768                    switch (node_subtype(n)) {
10769                        case italic_kern_subtype:
10770                        case left_correction_kern_subtype:
10771                        case right_correction_kern_subtype:
10772                            goto DONE;
10773                    }
10774                }
10775                break;
10776        }
10777        n = node_next(n);
10778    }
10779  DONE:
10780    nodelib_push_direct_or_nil(L, n);
10781    return 1;
10782}
10783
10784/* not yet ok: */
10785
10786# define isspeciallist_usage  common_usage
10787# define getspeciallist_usage common_usage
10788# define setspeciallist_usage common_usage
10789
10790static int nodelib_direct_getspeciallist(lua_State *L)
10791{
10792    const char *s = lua_tostring(L, 1);
10793    halfword head = null;
10794    halfword tail = null;
10795    if (! s) {
10796        /* error */
10797    } else if (lua_key_eq(s, pageinserthead)) {
10798        head = tex_get_special_node_list(page_insert_list_type, &tail);
10799    } else if (lua_key_eq(s, contributehead)) {
10800        head = tex_get_special_node_list(contribute_list_type, &tail);
10801    } else if (lua_key_eq(s, pagehead)) {
10802        head = tex_get_special_node_list(page_list_type, &tail);
10803    } else if (lua_key_eq(s, temphead)) {
10804        head = tex_get_special_node_list(temp_list_type, &tail);
10805    } else if (lua_key_eq(s, holdhead)) {
10806        head = tex_get_special_node_list(hold_list_type, &tail);
10807    } else if (lua_key_eq(s, postadjusthead)) {
10808        head = tex_get_special_node_list(post_adjust_list_type, &tail);
10809    } else if (lua_key_eq(s, preadjusthead)) {
10810        head = tex_get_special_node_list(pre_adjust_list_type, &tail);
10811    } else if (lua_key_eq(s, postmigratehead)) {
10812        head = tex_get_special_node_list(post_migrate_list_type, &tail);
10813    } else if (lua_key_eq(s, premigratehead)) {
10814        head = tex_get_special_node_list(pre_migrate_list_type, &tail);
10815    } else if (lua_key_eq(s, alignhead)) {
10816        head = tex_get_special_node_list(align_list_type, &tail);
10817    } else if (lua_key_eq(s, pagediscardshead)) {
10818        head = tex_get_special_node_list(page_discards_list_type, &tail);
10819    } else if (lua_key_eq(s, splitdiscardshead)) {
10820        head = tex_get_special_node_list(split_discards_list_type, &tail);
10821    }
10822    nodelib_push_direct_or_nil(L, head);
10823    nodelib_push_direct_or_nil(L, tail);
10824    return 2;
10825}
10826
10827static int nodelib_direct_isspeciallist(lua_State *L)
10828{
10829    halfword head = nodelib_valid_direct_from_index(L, 1);
10830    int istail = 0;
10831    int checked = tex_is_special_node_list(head, &istail);
10832    if (checked >= 0) {
10833        lua_pushinteger(L, checked);
10834        if (istail) {
10835            lua_pushboolean(L, 1);
10836            return 2;
10837        }
10838    } else {
10839        lua_pushboolean(L, 0);
10840    }
10841    return 1;
10842}
10843
10844static int nodelib_direct_setspeciallist(lua_State *L)
10845{
10846    halfword head = nodelib_valid_direct_from_index(L, 2);
10847    const char *s = lua_tostring(L, 1);
10848    if (! s) {
10849        /* error */
10850    } else if (lua_key_eq(s, pageinserthead)) {
10851        tex_set_special_node_list(page_insert_list_type, head);
10852    } else if (lua_key_eq(s, contributehead)) {
10853        tex_set_special_node_list(contribute_list_type, head);
10854    } else if (lua_key_eq(s, pagehead)) {
10855        tex_set_special_node_list(page_list_type, head);
10856    } else if (lua_key_eq(s, temphead)) {
10857        tex_set_special_node_list(temp_list_type, head);
10858    } else if (lua_key_eq(s, holdhead)) {
10859        tex_set_special_node_list(hold_list_type, head);
10860    } else if (lua_key_eq(s, postadjusthead)) {
10861        tex_set_special_node_list(post_adjust_list_type, head);
10862    } else if (lua_key_eq(s, preadjusthead)) {
10863        tex_set_special_node_list(pre_adjust_list_type, head);
10864    } else if (lua_key_eq(s, postmigratehead)) {
10865        tex_set_special_node_list(post_migrate_list_type, head);
10866    } else if (lua_key_eq(s, premigratehead)) {
10867        tex_set_special_node_list(pre_migrate_list_type, head);
10868    } else if (lua_key_eq(s, alignhead)) {
10869        tex_set_special_node_list(align_list_type, head);
10870    } else if (lua_key_eq(s, pagediscardshead)) {
10871        tex_set_special_node_list(page_discards_list_type, head);
10872    } else if (lua_key_eq(s, splitdiscardshead)) {
10873        tex_set_special_node_list(split_discards_list_type, head);
10874    }
10875    return 0;
10876}
10877
10878/* not yet ok: */
10879
10880# define getmvllist_usage common_usage
10881# define setmvllist_usage common_usage
10882
10883static int nodelib_direct_getmvllist(lua_State *L)
10884{
10885    /* todo: optional mvl integer for direct access */
10886    halfword head = null;
10887    halfword tail = null;
10888    halfword mvl  = tex_current_mvl(&head, &tail);
10889    lua_pushinteger(L, head);
10890    lua_pushinteger(L, tail);
10891    lua_pushinteger(L, mvl);
10892    return 3;
10893}
10894
10895# define vbalance_usage common_usage
10896
10897static int nodelib_direct_vbalance(lua_State *L)
10898{
10899    halfword index = lmt_get_box_id(L, 1, 1);
10900    halfword mode = nodelib_aux_packing(L, 2);
10901    halfword trial = lua_toboolean(L, 3); 
10902    nodelib_push_direct_or_nil(L, tex_vbalance(index, mode, trial));
10903    return 1;
10904}
10905
10906# define updatetopmarks_usage        common_usage
10907# define updatefirstmarks_usage      common_usage
10908# define updatefirstandbotmark_usage no_usage
10909# define updatemarks_usage           no_usage
10910
10911/* before */
10912
10913static int nodelib_direct_updatetopmarks(lua_State *L)
10914{
10915    lua_pushboolean(L, tex_update_top_marks());
10916    return 1;
10917}
10918
10919/* after */
10920
10921static int nodelib_direct_updatefirstmarks(lua_State *L)
10922{
10923    lua_pushboolean(L, tex_update_first_marks());
10924    return 1;
10925}
10926
10927/* as we go */
10928
10929static int nodelib_direct_updatefirstandbotmark(lua_State *L)
10930{
10931    halfword n = nodelib_valid_direct_from_index(L, 1);
10932    if (n) {
10933        tex_update_first_and_bot_mark(n);
10934    }
10935    return 0;
10936}
10937
10938static int nodelib_direct_updatemarks(lua_State *L)
10939{
10940    halfword n = nodelib_valid_direct_from_index(L, 1);
10941    lua_pushboolean(L, n ? tex_update_marks(n) : 0);
10942    return 1;
10943}
10944
10945// static int nodelib_direct_resetsplitmarks(lua_State *L)
10946// {
10947//     tex_reset_split_marks();
10948//     return 0;
10949// }
10950// 
10951// static int nodelib_direct_updatesplitmark(lua_State *L)
10952// {
10953//     halfword n = nodelib_valid_direct_from_index(L, 1);
10954//     if (n) {
10955//         tex_update_split_mark(n);
10956//     }
10957//     return 0;
10958// }
10959
10960/*tex
10961    This is just an experiment, so it might go away. Using a list can be a bit faster that traverse
10962    (2-4 times) but you only see a difference on very last lists and even then one need some 10K
10963    loops to notice it. If that gain is needed, I bet that the document takes a while to process
10964    anyway.
10965*/
10966
10967# define getnodes_usage common_usage
10968
10969static int nodelib_direct_getnodes(lua_State *L)
10970{
10971    halfword n = nodelib_valid_direct_from_index(L, 1);
10972    if (n) {
10973        int i = 0;
10974        /* maybe count */
10975        lua_newtable(L);
10976        if (lua_type(L, 2) == LUA_TNUMBER) {
10977            int t = lmt_tointeger(L, 2);
10978            if (lua_type(L, 3) == LUA_TNUMBER) {
10979                int s = lmt_tointeger(L, 3);
10980                while (n) {
10981                    if (node_type(n) == t && node_subtype(n) == s) {
10982                        lua_pushinteger(L, n);
10983                        lua_rawseti(L, -2, ++i);
10984                    }
10985                    n = node_next(n);
10986                }
10987            } else {
10988                while (n) {
10989                    if (node_type(n) == t) {
10990                        lua_pushinteger(L, n);
10991                        lua_rawseti(L, -2, ++i);
10992                    }
10993                    n = node_next(n);
10994                }
10995            }
10996        } else {
10997            while (n) {
10998                lua_pushinteger(L, n);
10999                lua_rawseti(L, -2, ++i);
11000                n = node_next(n);
11001            }
11002        }
11003        if (i) {
11004            return 1;
11005        } else {
11006            lua_pop(L, 1);
11007        }
11008    }
11009    lua_pushnil(L);
11010    return 1;
11011}
11012
11013/*tex experiment */
11014
11015static int nodelib_direct_getusedattributes(lua_State* L)
11016{
11017    lua_newtable(L); /* todo: preallocate */
11018    for (int current = lmt_node_memory_state.nodes_data.top; current > lmt_node_memory_state.reserved; current--) {
11019        if (lmt_node_memory_state.nodesizes[current] > 0 && (node_type(current) == attribute_node && node_subtype(current) != attribute_list_subtype)) {
11020            if (lua_rawgeti(L, -1, attribute_index(current)) == LUA_TTABLE) {
11021                lua_pushboolean(L, 1);
11022                lua_rawseti(L, -2, attribute_value(current));
11023                lua_pop(L, 1);
11024                /* not faster: */
11025             // if (lua_rawgeti(L, -1, attribute_value(current)) != LUA_TBOOLEAN) {
11026             //     lua_pushboolean(L, 1);
11027             //     lua_rawseti(L, -3, attribute_value(current));
11028             // }
11029             // lua_pop(L, 2);
11030            } else {
11031                lua_pop(L, 1);
11032                lua_newtable(L);
11033                lua_pushboolean(L, 1);
11034                lua_rawseti(L, -2, attribute_value(current));
11035                lua_rawseti(L, -2, attribute_index(current));
11036            }
11037        }
11038    }
11039    return 1;
11040}
11041
11042static int nodelib_shared_getcachestate(lua_State *L)
11043{
11044    lua_pushboolean(L, attribute_cache_disabled);
11045    return 1;
11046}
11047
11048/*tex done */
11049
11050static int nodelib_get_property_t(lua_State *L)
11051{   /* <table> <node> */
11052    halfword n = lmt_check_isnode(L, 2);
11053    if (n) {
11054        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
11055        /* <table> <node> <properties> */
11056        lua_rawgeti(L, -1, n);
11057    } else {
11058        lua_pushnil(L);
11059    }
11060    return 1;
11061}
11062
11063static int nodelib_set_property_t(lua_State *L)
11064{
11065    /* <table> <node> <value> */
11066    halfword n = lmt_check_isnode(L, 2);
11067    if (n) {
11068        lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
11069        /* <table> <node> <value> <properties> */
11070        lua_insert(L, -2);
11071        /* <table> <node> <properties> <value> */
11072        lua_rawseti(L, -2, n);
11073    }
11074    return 0;
11075}
11076
11077/* */
11078
11079# define gluetostring_usage (glue_usage | glue_spec_usage)
11080
11081static int nodelib_hybrid_gluetostring(lua_State *L)
11082{
11083    halfword glue = lua_type(L, 1) == LUA_TNUMBER ? nodelib_valid_direct_from_index(L, 1): lmt_maybe_isnode(L, 1);
11084    if (glue) {
11085        switch (node_type(glue)) {
11086            case glue_node:
11087            case glue_spec_node:
11088                {
11089                    int saved_selector = lmt_print_state.selector;
11090                    char *str = NULL;
11091                    lmt_print_state.selector = new_string_selector_code;
11092                    tex_print_spec(glue, pt_unit);
11093                    str = tex_take_string(NULL);
11094                    lmt_print_state.selector = saved_selector;
11095                    lua_pushstring(L, str);
11096                    return 1;
11097                }
11098        }
11099    }
11100    return 0;
11101}
11102
11103static const struct luaL_Reg nodelib_p[] = {
11104    { "__index",    nodelib_get_property_t },
11105    { "__newindex", nodelib_set_property_t },
11106    { NULL,         NULL                   },
11107};
11108
11109void lmt_initialize_properties(int set_size)
11110{
11111    lua_State *L = lmt_lua_state.lua_instance;
11112    if (lmt_node_memory_state.node_properties_id) {
11113        /*tex
11114            We should clean up but for now we accept a leak because these tables are still empty,
11115            and when you do this once again you're probably messing up. This should actually be
11116            enough:
11117        */
11118        luaL_unref(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
11119        lmt_node_memory_state.node_properties_id = 0;
11120    }
11121    if (set_size) {
11122        tex_engine_get_config_number("propertiessize", &lmt_node_memory_state.node_properties_table_size);
11123        if (lmt_node_memory_state.node_properties_table_size < 0) {
11124            lmt_node_memory_state.node_properties_table_size = 0;
11125        }
11126        /*tex It's a hash, not an array because we jump by size. */
11127        lua_createtable(L, 0, lmt_node_memory_state.node_properties_table_size);
11128    } else {
11129        lua_newtable(L);
11130    }
11131    /* <properties table> */
11132    lmt_node_memory_state.node_properties_id = luaL_ref(L, LUA_REGISTRYINDEX);
11133    /* not needed, so unofficial */
11134    lua_pushstring(L, NODE_PROPERTIES_DIRECT);
11135    /* <direct identifier> */
11136    lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id);
11137    /* <direct identifier> <properties table> */
11138    lua_settable(L, LUA_REGISTRYINDEX);
11139    /* */
11140    lua_pushstring(L, NODE_PROPERTIES_INDIRECT);
11141    /* <indirect identifier> */
11142    lua_newtable(L);
11143    /* <indirect identifier> <stub table> */
11144    luaL_newmetatable(L, NODE_PROPERTIES_INSTANCE);
11145    /* <indirect identifier> <stub table> <metatable> */
11146    luaL_setfuncs(L, nodelib_p, 0);
11147    /* <indirect identifier> <stub table> <metatable> */
11148    lua_setmetatable(L, -2);
11149    /* <indirect identifier> <stub table> */
11150    lua_settable(L, LUA_REGISTRYINDEX);
11151    /* */
11152}
11153
11154/*tex This is an experiment: */
11155
11156typedef enum usage_targets {
11157    no_usage_target       = 0,
11158    direct_usage_target   = 1, /* only available for direct nodes */
11159    userdata_usage_target = 2, /* only available for userdata nodes */
11160    separate_usage_target = 3, /* dedicated versions for direct and userdata nodes */
11161    hybrid_usage_target   = 4, /* shared version for direct and userdata nodes */
11162    general_usage_target  = 5, /* no direct or userdata arguments */
11163} usage_targets;
11164
11165typedef struct usage_record {
11166    const char  *name;
11167    lua_Integer  usage;
11168    int          target;
11169} usage_record;
11170
11171static const usage_record usage_data[] = {
11172    { .name = "addmargins",             .target = direct_usage_target,   .usage = addmargins_usage             },
11173    { .name = "addxoffset",             .target = direct_usage_target,   .usage = addxoffset_usage             },
11174    { .name = "addxymargins",           .target = direct_usage_target,   .usage = addxymargins_usage           },
11175    { .name = "addyoffset",             .target = direct_usage_target,   .usage = addyoffset_usage             },
11176    { .name = "appendaftertail",        .target = direct_usage_target,   .usage = appendaftertail_usage        },
11177    { .name = "beginofmath",            .target = direct_usage_target,   .usage = beginofmath_usage            },
11178    { .name = "checkdiscretionaries",   .target = direct_usage_target,   .usage = checkdiscretionaries_usage   },
11179    { .name = "checkdiscretionary",     .target = direct_usage_target,   .usage = checkdiscretionary_usage     },
11180    { .name = "collapsing",             .target = direct_usage_target,   .usage = collapsing_usage             },
11181    { .name = "copy",                   .target = separate_usage_target, .usage = copy_usage                   },
11182    { .name = "copylist",               .target = separate_usage_target, .usage = copylist_usage               },
11183    { .name = "copyonly",               .target = direct_usage_target,   .usage = copyonly_usage               },
11184    { .name = "count",                  .target = direct_usage_target,   .usage = count_usage                  },
11185    { .name = "currentattributes",      .target = separate_usage_target, .usage = currentattributes_usage      },
11186    { .name = "dimensions",             .target = direct_usage_target,   .usage = dimensions_usage             },
11187    { .name = "effectiveglue",          .target = direct_usage_target,   .usage = effectiveglue_usage          },
11188    { .name = "endofmath",              .target = direct_usage_target,   .usage = endofmath_usage              },
11189    { .name = "exchange",               .target = direct_usage_target,   .usage = exchange_usage               },
11190    { .name = "fields",                 .target = general_usage_target,  .usage = fields_usage                 },
11191    { .name = "findattribute",          .target = direct_usage_target,   .usage = findattribute_usage          },
11192    { .name = "findattributerange",     .target = direct_usage_target,   .usage = findattributerange_usage     },
11193    { .name = "findnode",               .target = direct_usage_target,   .usage = findnode_usage               },
11194    { .name = "firstchar",              .target = direct_usage_target,   .usage = firstchar_usage              },
11195    { .name = "firstglyph",             .target = direct_usage_target,   .usage = firstglyph_usage             },
11196    { .name = "firstglyphnode",         .target = direct_usage_target,   .usage = firstglyphnode_usage         },
11197    { .name = "firstitalicglyph",       .target = direct_usage_target,   .usage = firstitalicglyph_usage       },
11198    { .name = "flattendiscretionaries", .target = direct_usage_target,   .usage = flattendiscretionaries_usage },
11199    { .name = "flattenleaders",         .target = direct_usage_target,   .usage = flattenleaders_usage         },
11200    { .name = "flushlist",              .target = separate_usage_target, .usage = flushlist_usage              },
11201    { .name = "flushnode",              .target = separate_usage_target, .usage = flushnode_usage              },
11202    { .name = "free",                   .target = separate_usage_target, .usage = free_usage                   },
11203    { .name = "freeze",                 .target = direct_usage_target,   .usage = freeze_usage                 },
11204    { .name = "getanchors",             .target = direct_usage_target,   .usage = getanchors_usage             },
11205    { .name = "getattribute",           .target = separate_usage_target, .usage = getattribute_usage           },
11206    { .name = "getattributelist",       .target = direct_usage_target,   .usage = getattributelist_usage       },
11207    { .name = "getattributes",          .target = direct_usage_target,   .usage = getattributes_usage          },
11208    { .name = "getboth",                .target = direct_usage_target,   .usage = getboth_usage                },
11209    { .name = "getbottom",              .target = direct_usage_target,   .usage = getbottom_usage              },
11210    { .name = "getbottomdelimiter",     .target = direct_usage_target,   .usage = getbottomdelimiter_usage     },
11211    { .name = "getbox",                 .target = direct_usage_target,   .usage = getbox_usage                 },
11212    { .name = "getcachestate",          .target = general_usage_target,  .usage = common_usage                 }, /* todo */
11213    { .name = "getchar",                .target = direct_usage_target,   .usage = getchar_usage                },
11214    { .name = "getchardict",            .target = direct_usage_target,   .usage = getchardict_usage            },
11215    { .name = "getcharspec",            .target = direct_usage_target,   .usage = getcharspec_usage            },
11216    { .name = "getchoice",              .target = direct_usage_target,   .usage = getchoice_usage              },
11217    { .name = "getclass",               .target = direct_usage_target,   .usage = getclass_usage               },
11218    { .name = "getcontrol",             .target = direct_usage_target,   .usage = getcontrol_usage             },
11219    { .name = "getcornerkerns",         .target = direct_usage_target,   .usage = getcornerkerns_usage         },
11220    { .name = "getdata",                .target = direct_usage_target,   .usage = getdata_usage                },
11221    { .name = "getdegree",              .target = direct_usage_target,   .usage = getdegree_usage              },
11222    { .name = "getdelimiter",           .target = direct_usage_target,   .usage = getdelimiter_usage           },
11223    { .name = "getdenominator",         .target = direct_usage_target,   .usage = getdenominator_usage         },
11224    { .name = "getdepth",               .target = direct_usage_target,   .usage = getdepth_usage               },
11225    { .name = "getdirection",           .target = direct_usage_target,   .usage = getdirection_usage           },
11226    { .name = "getdisc",                .target = direct_usage_target,   .usage = getdisc_usage                },
11227    { .name = "getdiscpart",            .target = direct_usage_target,   .usage = getdiscpart_usage            },
11228    { .name = "getexcept",              .target = direct_usage_target,   .usage = getexcept_usage              },
11229    { .name = "getexpansion",           .target = direct_usage_target,   .usage = getexpansion_usage           },
11230    { .name = "getfam",                 .target = direct_usage_target,   .usage = getfam_usage                 },
11231    { .name = "getfield",               .target = separate_usage_target, .usage = getfield_usage               },
11232    { .name = "getfont",                .target = direct_usage_target,   .usage = getfont_usage                },
11233    { .name = "getgeometry",            .target = direct_usage_target,   .usage = getgeometry_usage            },
11234    { .name = "getglue",                .target = direct_usage_target,   .usage = getglue_usage                },
11235    { .name = "getglyphdata",           .target = direct_usage_target,   .usage = getglyphdata_usage           },
11236    { .name = "getglyphdimensions",     .target = direct_usage_target,   .usage = getglyphdimensions_usage     },
11237    { .name = "getheight",              .target = direct_usage_target,   .usage = getheight_usage              },
11238    { .name = "getid",                  .target = direct_usage_target,   .usage = getid_usage                  },
11239    { .name = "getidsubtype",           .target = direct_usage_target,   .usage = getidsubtype_usage           },
11240    { .name = "getindex",               .target = direct_usage_target,   .usage = getindex_usage               },
11241    { .name = "getinputfields",         .target = direct_usage_target,   .usage = getinputfields_usage         },
11242    { .name = "getkern",                .target = direct_usage_target,   .usage = getkern_usage                },
11243    { .name = "getkerndimension",       .target = direct_usage_target,   .usage = getkerndimension_usage       },
11244    { .name = "getlanguage",            .target = direct_usage_target,   .usage = getlanguage_usage            },
11245    { .name = "getleader",              .target = direct_usage_target,   .usage = getleader_usage              },
11246    { .name = "getleftdelimiter",       .target = direct_usage_target,   .usage = getleftdelimiter_usage       },
11247    { .name = "getlist",                .target = direct_usage_target,   .usage = getlist_usage                },
11248    { .name = "getlistdimensions",      .target = direct_usage_target,   .usage = getlistdimensions_usage      },
11249    { .name = "getmvllist",             .target = direct_usage_target,   .usage = getmvllist_usage             },
11250    { .name = "getnext",                .target = direct_usage_target,   .usage = getnext_usage                },
11251    { .name = "getnodes",               .target = direct_usage_target,   .usage = getnodes_usage               },
11252    { .name = "getnormalizedline",      .target = direct_usage_target,   .usage = getnormalizedline_usage      },
11253    { .name = "getnucleus",             .target = direct_usage_target,   .usage = getnucleus_usage             },
11254    { .name = "getnumerator",           .target = direct_usage_target,   .usage = getnumerator_usage           },
11255    { .name = "getoffsets",             .target = direct_usage_target,   .usage = getoffsets_usage             },
11256    { .name = "getoptions",             .target = direct_usage_target,   .usage = getoptions_usage             },
11257    { .name = "getorientation",         .target = direct_usage_target,   .usage = getorientation_usage         },
11258    { .name = "getparstate",            .target = direct_usage_target,   .usage = getparstate_usage            },
11259    { .name = "getpenalty",             .target = direct_usage_target,   .usage = getpenalty_usage             },
11260    { .name = "getpost",                .target = direct_usage_target,   .usage = getpost_usage                },
11261    { .name = "getpre",                 .target = direct_usage_target,   .usage = getpre_usage                 },
11262    { .name = "getprev",                .target = direct_usage_target,   .usage = getprev_usage                },
11263    { .name = "getprime",               .target = direct_usage_target,   .usage = getprime_usage               },
11264    { .name = "getpropertiestable",     .target = separate_usage_target, .usage = common_usage                 }, /* todo */
11265    { .name = "getproperty",            .target = separate_usage_target, .usage = getproperty_usage            },
11266    { .name = "getreplace",             .target = direct_usage_target,   .usage = getreplace_usage             },
11267    { .name = "getrightdelimiter",      .target = direct_usage_target,   .usage = getrightdelimiter_usage      },
11268    { .name = "getruledimensions",      .target = direct_usage_target,   .usage = getruledimensions_usage      },
11269    { .name = "getscale",               .target = direct_usage_target,   .usage = getscale_usage               },
11270    { .name = "getscales",              .target = direct_usage_target,   .usage = getscales_usage              },
11271    { .name = "getscript",              .target = direct_usage_target,   .usage = getscript_usage              },
11272    { .name = "getscripts",             .target = direct_usage_target,   .usage = getscripts_usage             },
11273    { .name = "getshift",               .target = direct_usage_target,   .usage = getshift_usage               },
11274    { .name = "getslant",               .target = direct_usage_target,   .usage = getslant_usage               },
11275    { .name = "getspeciallist",         .target = direct_usage_target,   .usage = getspeciallist_usage         },
11276    { .name = "getstate",               .target = direct_usage_target,   .usage = getstate_usage               },
11277    { .name = "getsub",                 .target = direct_usage_target,   .usage = getsub_usage                 },
11278    { .name = "getsubpre",              .target = direct_usage_target,   .usage = getsubpre_usage              },
11279    { .name = "getsubtype",             .target = direct_usage_target,   .usage = getsubtype_usage             },
11280    { .name = "getsup",                 .target = direct_usage_target,   .usage = getsup_usage                 },
11281    { .name = "getsuppre",              .target = direct_usage_target,   .usage = getsuppre_usage              },
11282    { .name = "gettop",                 .target = direct_usage_target,   .usage = gettop_usage                 },
11283    { .name = "gettopdelimiter",        .target = direct_usage_target,   .usage = gettopdelimiter_usage        },
11284    { .name = "gettotal",               .target = direct_usage_target,   .usage = gettotal_usage               },
11285    { .name = "getweight",              .target = direct_usage_target,   .usage = getweight_usage              },
11286    { .name = "getwhd",                 .target = direct_usage_target,   .usage = getwhd_usage                 },
11287    { .name = "getwidth",               .target = direct_usage_target,   .usage = getwidth_usage               },
11288    { .name = "getwordrange",           .target = direct_usage_target,   .usage = getwordrange_usage           },
11289    { .name = "getxscale",              .target = direct_usage_target,   .usage = getxscale_usage              },
11290    { .name = "getxyscales",            .target = direct_usage_target,   .usage = getxyscales_usage            },
11291    { .name = "getyscale",              .target = direct_usage_target,   .usage = getyscale_usage              },
11292    { .name = "gluetostring" ,          .target = hybrid_usage_target,   .usage = gluetostring_usage           },
11293    { .name = "hasattribute",           .target = separate_usage_target, .usage = hasattribute_usage           },
11294    { .name = "hasdimensions",          .target = direct_usage_target,   .usage = hasdimensions_usage          },
11295    { .name = "hasdiscoption",          .target = direct_usage_target,   .usage = hasdiscoption_usage          },
11296    { .name = "hasfield",               .target = separate_usage_target, .usage = hasfield_usage               },
11297    { .name = "hasgeometry",            .target = direct_usage_target,   .usage = hasgeometry_usage            },
11298    { .name = "hasglyph",               .target = direct_usage_target,   .usage = hasglyph_usage               },
11299    { .name = "hasglyphoption",         .target = direct_usage_target,   .usage = hasglyphoption_usage         },
11300    { .name = "hpack",                  .target = direct_usage_target,   .usage = hpack_usage                  },
11301    { .name = "hyphenating",            .target = direct_usage_target,   .usage = hyphenating_usage            },
11302    { .name = "id",                     .target = general_usage_target,  .usage = id_usage                     },
11303    { .name = "ignoremathskip",         .target = direct_usage_target,   .usage = ignoremathskip_usage         },
11304    { .name = "insertafter",            .target = separate_usage_target, .usage = insertafter_usage            },
11305    { .name = "insertbefore",           .target = separate_usage_target, .usage = insertbefore_usage           },
11306    { .name = "instock",                .target = userdata_usage_target, .usage = common_usage                 }, /* todo */
11307    { .name = "inuse",                  .target = userdata_usage_target, .usage = common_usage                 }, /* todo */
11308    { .name = "isboth",                 .target = direct_usage_target,   .usage = isboth_usage                 },
11309    { .name = "ischar",                 .target = direct_usage_target,   .usage = ischar_usage                 },
11310    { .name = "isdirect",               .target = direct_usage_target,   .usage = isdirect_usage               },
11311    { .name = "isglyph",                .target = direct_usage_target,   .usage = isglyph_usage                },
11312    { .name = "isitalicglyph",          .target = direct_usage_target,   .usage = isitalicglyph_usage          },
11313    { .name = "isloop",                 .target = direct_usage_target,   .usage = isloop_usage                 },
11314    { .name = "isnext",                 .target = direct_usage_target,   .usage = isnext_usage                 },
11315    { .name = "isnextchar",             .target = direct_usage_target,   .usage = isnextchar_usage             },
11316    { .name = "isnextglyph",            .target = direct_usage_target,   .usage = isnextglyph_usage            },
11317    { .name = "isnode",                 .target = separate_usage_target, .usage = isnode_usage                 },
11318    { .name = "isprev",                 .target = direct_usage_target,   .usage = isprev_usage                 },
11319    { .name = "isprevchar",             .target = direct_usage_target,   .usage = isnextchar_usage             },
11320    { .name = "isprevglyph",            .target = direct_usage_target,   .usage = isprevglyph_usage            },
11321    { .name = "issimilarglyph",         .target = direct_usage_target,   .usage = issimilarglyph_usage         },
11322    { .name = "isspeciallist",          .target = direct_usage_target,   .usage = isspeciallist_usage          },
11323    { .name = "isvalid",                .target = direct_usage_target,   .usage = isvalid_usage                },
11324    { .name = "iszeroglue",             .target = direct_usage_target,   .usage = iszeroglue_usage             },
11325    { .name = "kerning",                .target = direct_usage_target,   .usage = kerning_usage                },
11326    { .name = "lastnode",               .target = direct_usage_target,   .usage = lastnode_usage               },
11327    { .name = "length",                 .target = direct_usage_target,   .usage = length_usage                 },
11328    { .name = "ligaturing",             .target = direct_usage_target,   .usage = ligaturing_usage             },
11329    { .name = "migrate",                .target = direct_usage_target,   .usage = migrate_usage                },
11330    { .name = "mlisttohlist",           .target = direct_usage_target,   .usage = mlisttohlist_usage           },
11331    { .name = "naturalhsize",           .target = direct_usage_target,   .usage = naturalhsize_usage           },
11332    { .name = "naturalwidth",           .target = direct_usage_target,   .usage = naturalwidth_usage           },
11333    { .name = "new",                    .target = separate_usage_target, .usage = new_usage                    },
11334    { .name = "newcontinuationatom",    .target = direct_usage_target,   .usage = newcontinuationatom_usage    },
11335    { .name = "newmathglyph",           .target = direct_usage_target,   .usage = newmathglyph_usage           },
11336    { .name = "newtextglyph",           .target = direct_usage_target,   .usage = newtextglyph_usage           },
11337    { .name = "patchattributes",        .target = direct_usage_target,   .usage = patchattributes_usage        },
11338    { .name = "patchparshape",          .target = direct_usage_target,   .usage = patchparshape_usage          },
11339    { .name = "prependbeforehead",      .target = direct_usage_target,   .usage = prependbeforehead_usage      },
11340    { .name = "protectglyph",           .target = direct_usage_target,   .usage = protectglyph_usage           },
11341    { .name = "protectglyphs",          .target = direct_usage_target,   .usage = protectglyphs_usage          },
11342    { .name = "protectglyphsnone",      .target = direct_usage_target,   .usage = protectglyphsnone_usage      },
11343    { .name = "protrusionskippable",    .target = direct_usage_target,   .usage = protrusionskippable_usage    },
11344    { .name = "rangedimensions",        .target = direct_usage_target,   .usage = rangedimensions_usage        },
11345    { .name = "remove",                 .target = separate_usage_target, .usage = remove_usage                 },
11346    { .name = "removefromlist",         .target = direct_usage_target,   .usage = removefromlist_usage         },
11347    { .name = "repack",                 .target = direct_usage_target,   .usage = repack_usage                 },
11348    { .name = "reverse",                .target = direct_usage_target,   .usage = reverse_usage                },
11349    { .name = "serialized",             .target = separate_usage_target, .usage = serialized_usage             },
11350    { .name = "setanchors",             .target = direct_usage_target,   .usage = setanchors_usage             },
11351    { .name = "setattribute",           .target = separate_usage_target, .usage = setattribute_usage           },
11352    { .name = "setattributelist",       .target = direct_usage_target,   .usage = setattributelist_usage       },
11353    { .name = "setattributes",          .target = direct_usage_target,   .usage = setattributes_usage          },
11354    { .name = "setboth",                .target = direct_usage_target,   .usage = setboth_usage                },
11355    { .name = "setbottom",              .target = direct_usage_target,   .usage = setbottom_usage              },
11356    { .name = "setbottomdelimiter",     .target = direct_usage_target,   .usage = setbottomdelimiter_usage     },
11357    { .name = "setbox",                 .target = direct_usage_target,   .usage = setbox_usage                 },
11358    { .name = "setchar",                .target = direct_usage_target,   .usage = setchar_usage                },
11359    { .name = "setchardict",            .target = direct_usage_target,   .usage = setchardict_usage            },
11360    { .name = "setcharspec",            .target = direct_usage_target,   .usage = setcharspec_usage            },
11361    { .name = "setchoice",              .target = direct_usage_target,   .usage = setchoice_usage              },
11362    { .name = "setclass",               .target = direct_usage_target,   .usage = setclass_usage               },
11363    { .name = "setcontrol",             .target = direct_usage_target,   .usage = setcontrol_usage             },
11364    { .name = "setdata",                .target = direct_usage_target,   .usage = setdata_usage                },
11365    { .name = "setdegree",              .target = direct_usage_target,   .usage = setdegree_usage              },
11366    { .name = "setdelimiter",           .target = direct_usage_target,   .usage = setdelimiter_usage           },
11367    { .name = "setdenominator",         .target = direct_usage_target,   .usage = setdenominator_usage         },
11368    { .name = "setdepth",               .target = direct_usage_target,   .usage = setdepth_usage               },
11369    { .name = "setdirection",           .target = direct_usage_target,   .usage = setdirection_usage           },
11370    { .name = "setdisc",                .target = direct_usage_target,   .usage = setdisc_usage                },
11371    { .name = "setdiscpart",            .target = direct_usage_target,   .usage = setdiscpart_usage            },
11372    { .name = "setexcept",              .target = direct_usage_target,   .usage = setexcept_usage              },
11373    { .name = "setexpansion",           .target = direct_usage_target,   .usage = setexpansion_usage           },
11374    { .name = "setfam",                 .target = direct_usage_target,   .usage = setfam_usage                 },
11375    { .name = "setfield",               .target = separate_usage_target, .usage = setfield_usage               },
11376    { .name = "setfont",                .target = direct_usage_target,   .usage = setfont_usage                },
11377    { .name = "setgeometry",            .target = direct_usage_target,   .usage = setgeometry_usage            },
11378    { .name = "setglue",                .target = direct_usage_target,   .usage = setglue_usage                },
11379    { .name = "setglyphdata",           .target = direct_usage_target,   .usage = setglyphdata_usage           },
11380    { .name = "setheight",              .target = direct_usage_target,   .usage = setheight_usage              },
11381    { .name = "setindex",               .target = direct_usage_target,   .usage = setindex_usage               },
11382    { .name = "setinputfields",         .target = direct_usage_target,   .usage = setinputfields_usage         },
11383    { .name = "setkern",                .target = direct_usage_target,   .usage = setkern_usage                },
11384    { .name = "setlanguage",            .target = direct_usage_target,   .usage = setlanguage_usage            },
11385    { .name = "setleader",              .target = direct_usage_target,   .usage = setleader_usage              },
11386    { .name = "setleftdelimiter",       .target = direct_usage_target,   .usage = setleftdelimiter_usage       },
11387    { .name = "setlink",                .target = direct_usage_target,   .usage = setlink_usage                },
11388    { .name = "setlist",                .target = direct_usage_target,   .usage = setlist_usage                },
11389    { .name = "setnext",                .target = direct_usage_target,   .usage = setnext_usage                },
11390    { .name = "setnucleus",             .target = direct_usage_target,   .usage = setnucleus_usage             },
11391    { .name = "setnumerator",           .target = direct_usage_target,   .usage = setnumerator_usage           },
11392    { .name = "setoffsets",             .target = direct_usage_target,   .usage = setoffsets_usage             },
11393    { .name = "setoptions",             .target = direct_usage_target,   .usage = setoptions_usage             },
11394    { .name = "setorientation",         .target = direct_usage_target,   .usage = setorientation_usage         },
11395    { .name = "setpenalty",             .target = direct_usage_target,   .usage = setpenalty_usage             },
11396    { .name = "setpost",                .target = direct_usage_target,   .usage = setpost_usage                },
11397    { .name = "setpre",                 .target = direct_usage_target,   .usage = setpre_usage                 },
11398    { .name = "setprev",                .target = direct_usage_target,   .usage = setprev_usage                },
11399    { .name = "setprime",               .target = direct_usage_target,   .usage = setprime_usage               },
11400    { .name = "setproperty",            .target = separate_usage_target, .usage = setproperty_usage            },
11401    { .name = "setreplace",             .target = direct_usage_target,   .usage = setreplace_usage             },
11402    { .name = "setrightdelimiter",      .target = direct_usage_target,   .usage = setrightdelimiter_usage      },
11403    { .name = "setruledimensions",      .target = direct_usage_target,   .usage = setruledimensions_usage      },
11404    { .name = "setruledimensions",      .target = direct_usage_target,   .usage = setruledimensions_usage      },
11405    { .name = "setscale",               .target = direct_usage_target,   .usage = setscale_usage               },
11406    { .name = "setscales",              .target = direct_usage_target,   .usage = setscales_usage              },
11407    { .name = "setscript",              .target = direct_usage_target,   .usage = setscript_usage              },
11408    { .name = "setscripts",             .target = direct_usage_target,   .usage = setscripts_usage             },
11409    { .name = "setshift",               .target = direct_usage_target,   .usage = setshift_usage               },
11410    { .name = "setslant",               .target = direct_usage_target,   .usage = setslant_usage               },
11411    { .name = "setspeciallist",         .target = direct_usage_target,   .usage = setspeciallist_usage         },
11412    { .name = "setsplit",               .target = direct_usage_target,   .usage = setsplit_usage               },
11413    { .name = "setstate",               .target = direct_usage_target,   .usage = setstate_usage               },
11414    { .name = "setsub",                 .target = direct_usage_target,   .usage = setsub_usage                 },
11415    { .name = "setsubpre",              .target = direct_usage_target,   .usage = setsubpre_usage              },
11416    { .name = "setsubtype",             .target = direct_usage_target,   .usage = setsubtype_usage             },
11417    { .name = "setsup",                 .target = direct_usage_target,   .usage = setsup_usage                 },
11418    { .name = "setsuppre",              .target = direct_usage_target,   .usage = setsuppre_usage              },
11419    { .name = "settop",                 .target = direct_usage_target,   .usage = settop_usage                 },
11420    { .name = "settopdelimiter",        .target = direct_usage_target,   .usage = settopdelimiter_usage        },
11421    { .name = "settotal",               .target = direct_usage_target,   .usage = settotal_usage               },
11422    { .name = "setweight",              .target = direct_usage_target,   .usage = setweight_usage              },
11423    { .name = "setwhd",                 .target = direct_usage_target,   .usage = setwhd_usage                 },
11424    { .name = "setwidth",               .target = direct_usage_target,   .usage = setwidth_usage               },
11425    { .name = "setxyscales",            .target = direct_usage_target,   .usage = setxyscales_usage            },
11426    { .name = "show",                   .target = separate_usage_target, .usage = show_usage                   },
11427    { .name = "showlist",               .target = direct_usage_target,   .usage = showlist_usage               },
11428    { .name = "size",                   .target = direct_usage_target,   .usage = size_usage                   },
11429    { .name = "size",                   .target = general_usage_target,  .usage = common_usage                 }, /* todo */
11430    { .name = "slide",                  .target = direct_usage_target,   .usage = slide_usage                  },
11431    { .name = "softenhyphens",          .target = direct_usage_target,   .usage = softenhyphens_usage          },
11432    { .name = "startofpar",             .target = direct_usage_target,   .usage = startofpar_usage             },
11433    { .name = "subtypes",               .target = general_usage_target,  .usage = common_usage                 }, /* todo */
11434    { .name = "tail",                   .target = separate_usage_target, .usage = tail_usage                   },
11435    { .name = "todirect",               .target = userdata_usage_target, .usage = todirect_usage               },
11436    { .name = "tonode",                 .target = direct_usage_target,   .usage = tonode_usage                 },
11437    { .name = "tostring",               .target = separate_usage_target, .usage = common_usage                 }, /* todo */
11438    { .name = "tovaliddirect",          .target = direct_usage_target,   .usage = tovaliddirect_usage          },
11439    { .name = "traverse",               .target = separate_usage_target, .usage = traverse_usage               },
11440    { .name = "traversechar",           .target = direct_usage_target,   .usage = traversechar_usage           },
11441    { .name = "traversecontent",        .target = direct_usage_target,   .usage = traversecontent_usage        },
11442    { .name = "traverseglyph",          .target = direct_usage_target,   .usage = traverseglyph_usage          },
11443    { .name = "traverseid",             .target = separate_usage_target, .usage = traverseid_usage             },
11444    { .name = "traverseitalic",         .target = direct_usage_target,   .usage = traverseitalic_usage         },
11445    { .name = "traverseleader",         .target = direct_usage_target,   .usage = traverseleader_usage         },
11446    { .name = "traverselist",           .target = direct_usage_target,   .usage = traverselist_usage           },
11447    { .name = "type",                   .target = hybrid_usage_target,   .usage = type_usage                   },
11448    { .name = "types",                  .target = general_usage_target,  .usage = common_usage                 }, /* todo */
11449    { .name = "unprotectglyph",         .target = direct_usage_target,   .usage = unprotectglyph_usage         },
11450    { .name = "unprotectglyphs",        .target = direct_usage_target,   .usage = unprotectglyphs_usage        },
11451    { .name = "unprotectglyphsnone",    .target = direct_usage_target,   .usage = unprotectglyphsnone_usage    },
11452    { .name = "unsetattribute",         .target = separate_usage_target, .usage = unsetattribute_usage         },
11453    { .name = "unsetattributes",        .target = direct_usage_target,   .usage = unsetattributes_usage        },
11454    { .name = "usedlist",               .target = separate_usage_target, .usage = usedlist_usage               },
11455    { .name = "usesfont",               .target = direct_usage_target,   .usage = usesfont_usage               },
11456    { .name = "validpar",               .target = direct_usage_target,   .usage = validpar_usage               },
11457    { .name = "verticalbreak",          .target = direct_usage_target,   .usage = verticalbreak_usage          },
11458    { .name = "vpack",                  .target = direct_usage_target,   .usage = vpack_usage                  },
11459    { .name = "write",                  .target = separate_usage_target, .usage = write_usage                  },
11460    { .name = "xscaled",                .target = direct_usage_target,   .usage = xscaled_usage                },
11461    { .name = "yscaled",                .target = direct_usage_target,   .usage = yscaled_usage                },
11462
11463    { .name = "vbalance",               .target = direct_usage_target,   .usage = vbalance_usage               },
11464
11465    { .name = "updatetopmarks",         .target = no_usage_target,       .usage = updatetopmarks_usage         },
11466    { .name = "updatemarks",            .target = direct_usage_target,   .usage = updatemarks_usage            },
11467    { .name = "updatefirstmarks",       .target = no_usage_target,       .usage = updatefirstmarks_usage       },
11468    { .name = "updatefirstandbotmark",  .target = direct_usage_target,   .usage = updatefirstandbotmark_usage  },
11469
11470    { .name = NULL,                     .target = no_usage_target,       .usage = 0                            },
11471} ;
11472
11473static int nodelib_direct_hasusage(lua_State *L)
11474{
11475    const char *str = lua_tostring(L, 1);
11476    if (str) {
11477        int target = lmt_optinteger(L, 2, 0);
11478        for (int i = 0; usage_data[i].name != NULL; i++) {
11479            if ((! target || usage_data[i].target == target) && ! strcmp(usage_data[i].name, str)) {
11480                lua_pushboolean(L, 1);
11481                return 1;
11482            }
11483        }
11484    }
11485    lua_pushboolean(L, 0);
11486    return 1;
11487}
11488
11489static int nodelib_direct_getusage(lua_State *L)
11490{
11491    switch (lua_type(L, 1)) {
11492        case LUA_TSTRING:
11493            {
11494                const char *str = lua_tostring(L, 1);
11495                if (str) {
11496                    int target = lmt_optinteger(L, 2, 0);
11497                    for (int i = 0; usage_data[i].name != NULL; i++) {
11498                        if ((! target || usage_data[i].target == target) && ! strcmp(usage_data[i].name, str)) {
11499                            if (usage_data[i].usage & common_usage) {
11500                                lua_pushboolean(L, 1);
11501                            } else {
11502                                int n = 0;
11503                                lua_newtable(L);
11504                                for (int id = 0; id <= passive_node; id++) {
11505                                    lua_Integer bit = (lua_Integer) 1 << (id + 1);
11506                                    if (usage_data[i].usage & bit) {
11507                                        lua_pushinteger(L, id);
11508                                        lua_rawseti(L, -2, ++n);
11509                                    }
11510                                }
11511                            }
11512                            return 1;
11513                        }
11514                    }
11515                }
11516            }
11517            break;
11518        case LUA_TNUMBER:
11519            {
11520                int id = (int) lua_tointeger(L, 1);
11521                if (id >= 0 && id <= passive_node) {
11522                    int target = lmt_optinteger(L, 2, 0);
11523                    lua_Integer bit = (lua_Integer) 1 << (id + 1);
11524                    int n = 0;
11525                    lua_newtable(L);
11526                    for (int i = 0; usage_data[i].name != NULL; i++) {
11527                        if ((! target || usage_data[i].target == target) && ((usage_data[i].usage & bit) || (usage_data[i].usage & common_usage))) {
11528                            lua_pushstring(L, usage_data[i].name);
11529                            lua_rawseti(L, -2, ++n);
11530                        }
11531                    }
11532                    return 1;
11533                }
11534            }
11535            break;
11536    }
11537    return 0;
11538}
11539
11540/* node.direct.* */
11541
11542static const struct luaL_Reg nodelib_direct_function_list[] = {
11543
11544    { "addmargins",              nodelib_direct_addmargins             },
11545    { "addxoffset",              nodelib_direct_addxoffset             },
11546    { "addxymargins",            nodelib_direct_addxymargins           },
11547    { "addyoffset",              nodelib_direct_addyoffset             },
11548    { "appendaftertail",         nodelib_direct_appendaftertail        },
11549    { "beginofmath",             nodelib_direct_beginofmath            },
11550    { "checkdiscretionaries",    nodelib_direct_checkdiscretionaries   },
11551    { "checkdiscretionary",      nodelib_direct_checkdiscretionary     },
11552    { "collapsing",              nodelib_direct_collapsing             }, /*tex A funny name but like |ligaturing| and |hyphenating|. */
11553    { "copy",                    nodelib_direct_copy                   },
11554    { "copylist",                nodelib_direct_copylist               },
11555    { "copyonly",                nodelib_direct_copyonly               },
11556    { "count",                   nodelib_direct_count                  },
11557    { "currentattributes",       nodelib_direct_currentattributes      },
11558    { "dimensions",              nodelib_direct_dimensions             },
11559    { "effectiveglue",           nodelib_direct_effectiveglue          },
11560    { "endofmath",               nodelib_direct_endofmath              },
11561    { "exchange",                nodelib_direct_exchange               },
11562    { "findattribute",           nodelib_direct_findattribute          },
11563    { "findattributerange",      nodelib_direct_findattributerange     },
11564    { "findnode",                nodelib_direct_findnode               },
11565    { "firstchar",               nodelib_direct_firstchar              },
11566    { "firstglyph",              nodelib_direct_firstglyph             },
11567    { "firstglyphnode",          nodelib_direct_firstglyphnode         },
11568    { "firstitalicglyph",        nodelib_direct_firstitalicglyph       },
11569    { "flattendiscretionaries",  nodelib_direct_flattendiscretionaries },
11570    { "flattenleaders",          nodelib_direct_flattenleaders         },
11571    { "flushlist",               nodelib_direct_flushlist              },
11572    { "flushnode",               nodelib_direct_flushnode              },
11573    { "free",                    nodelib_direct_free                   },
11574    { "freeze",                  nodelib_direct_freeze                 },
11575    { "getanchors",              nodelib_direct_getanchors             },
11576    { "getattribute",            nodelib_direct_getattribute           },
11577    { "getattributelist",        nodelib_direct_getattributelist       },
11578    { "getattributes",           nodelib_direct_getattributes          },
11579    { "getboth",                 nodelib_direct_getboth                },
11580    { "getbottom",               nodelib_direct_getbottom              },
11581    { "getbottomdelimiter",      nodelib_direct_getbottomdelimiter     },
11582    { "getbox",                  nodelib_direct_getbox                 },
11583    { "getchar",                 nodelib_direct_getchar                },
11584    { "getchardict",             nodelib_direct_getchardict            },
11585    { "getcharspec",             nodelib_direct_getcharspec            },
11586    { "getchoice",               nodelib_direct_getchoice              },
11587    { "getclass",                nodelib_direct_getclass               },
11588    { "getcontrol",              nodelib_direct_getcontrol             },
11589    { "getcornerkerns",          nodelib_direct_getcornerkerns         },
11590    { "getdata",                 nodelib_direct_getdata                },
11591    { "getdegree",               nodelib_direct_getdegree              },
11592    { "getdelimiter",            nodelib_direct_getdelimiter           },
11593    { "getdenominator",          nodelib_direct_getdenominator         },
11594    { "getdepth",                nodelib_direct_getdepth               },
11595    { "getdirection",            nodelib_direct_getdirection           },
11596    { "getdisc",                 nodelib_direct_getdisc                },
11597    { "getdiscpart",             nodelib_direct_getdiscpart            },
11598    { "getexcept",               nodelib_direct_getexcept              },
11599    { "getexpansion",            nodelib_direct_getexpansion           },
11600    { "getfam",                  nodelib_direct_getfam                 },
11601    { "getfield",                nodelib_direct_getfield               },
11602    { "getfont",                 nodelib_direct_getfont                },
11603    { "getgeometry",             nodelib_direct_getgeometry            },
11604    { "getglue",                 nodelib_direct_getglue                },
11605    { "getglyphdata",            nodelib_direct_getglyphdata           },
11606    { "getglyphdimensions",      nodelib_direct_getglyphdimensions     },
11607    { "getheight",               nodelib_direct_getheight              },
11608    { "getid",                   nodelib_direct_getid                  },
11609    { "getidsubtype",            nodelib_direct_getidsubtype           },
11610    { "getindex",                nodelib_direct_getindex               },
11611    { "getinputfields",          nodelib_direct_getinputfields         },
11612    { "getkern",                 nodelib_direct_getkern                },
11613    { "getkerndimension",        nodelib_direct_getkerndimension       },
11614    { "getlanguage",             nodelib_direct_getlanguage            },
11615    { "getleader",               nodelib_direct_getleader              },
11616    { "getleftdelimiter",        nodelib_direct_getleftdelimiter       },
11617    { "getlist",                 nodelib_direct_getlist                },
11618    { "getlistdimensions",       nodelib_direct_getlistdimensions      },
11619    { "getnext",                 nodelib_direct_getnext                },
11620    { "getmvllist",              nodelib_direct_getmvllist             },
11621    { "getnodes",                nodelib_direct_getnodes               },
11622    { "getnormalizedline",       nodelib_direct_getnormalizedline      },
11623    { "getnucleus",              nodelib_direct_getnucleus             },
11624    { "getnumerator",            nodelib_direct_getnumerator           },
11625    { "getoffsets",              nodelib_direct_getoffsets             },
11626    { "getoptions",              nodelib_direct_getoptions             },
11627    { "getorientation",          nodelib_direct_getorientation         },
11628    { "getparstate",             nodelib_direct_getparstate            },
11629    { "getpenalty",              nodelib_direct_getpenalty             },
11630    { "getpost",                 nodelib_direct_getpost                },
11631    { "getpre",                  nodelib_direct_getpre                 },
11632    { "getprev",                 nodelib_direct_getprev                },
11633    { "getprime",                nodelib_direct_getprime               },
11634    { "getpropertiestable",      nodelib_direct_getpropertiestable     },
11635    { "getproperty",             nodelib_direct_getproperty            },
11636    { "getreplace",              nodelib_direct_getreplace             },
11637    { "getrightdelimiter",       nodelib_direct_getrightdelimiter      },
11638    { "getruledimensions",       nodelib_direct_getruledimensions      },
11639    { "getscale",                nodelib_direct_getscale               },
11640    { "getscales",               nodelib_direct_getscales              },
11641    { "getscript",               nodelib_direct_getscript              },
11642    { "getscripts",              nodelib_direct_getscripts             },
11643    { "getshift",                nodelib_direct_getshift               },
11644    { "getslant",                nodelib_direct_getslant               },
11645    { "getspeciallist",          nodelib_direct_getspeciallist         },
11646    { "getstate",                nodelib_direct_getstate               },
11647    { "getsub",                  nodelib_direct_getsub                 },
11648    { "getsubpre",               nodelib_direct_getsubpre              },
11649    { "getsubtype",              nodelib_direct_getsubtype             },
11650    { "getsup",                  nodelib_direct_getsup                 },
11651    { "getsuppre",               nodelib_direct_getsuppre              },
11652    { "gettop",                  nodelib_direct_gettop                 },
11653    { "gettopdelimiter",         nodelib_direct_gettopdelimiter        },
11654    { "gettotal" ,               nodelib_direct_gettotal               },
11655    { "getusage",                nodelib_direct_getusage               },
11656    { "getusedattributes",       nodelib_direct_getusedattributes      },
11657    { "getweight",               nodelib_direct_getweight              },
11658    { "getwhd",                  nodelib_direct_getwhd                 },
11659    { "getwidth",                nodelib_direct_getwidth               },
11660    { "getwordrange",            nodelib_direct_getwordrange           },
11661    { "getxscale",               nodelib_direct_getxscale              },
11662    { "getxyscales",             nodelib_direct_getxyscales            },
11663    { "getyscale",               nodelib_direct_getyscale              },
11664    { "hasattribute",            nodelib_direct_hasattribute           },
11665    { "hasdimensions",           nodelib_direct_hasdimensions          },
11666    { "hasdiscoption",           nodelib_direct_hasdiscoption          },
11667    { "hasfield",                nodelib_direct_hasfield               },
11668    { "hasgeometry",             nodelib_direct_hasgeometry            },
11669    { "hasglyph",                nodelib_direct_hasglyph               },
11670    { "hasglyphoption",          nodelib_direct_hasglyphoption         },
11671    { "hasusage",                nodelib_direct_hasusage               },
11672    { "hpack",                   nodelib_direct_hpack                  },
11673    { "hyphenating",             nodelib_direct_hyphenating            },
11674    { "ignoremathskip",          nodelib_direct_ignoremathskip         },
11675    { "insertafter",             nodelib_direct_insertafter            },
11676    { "insertbefore",            nodelib_direct_insertbefore           },
11677    { "isboth",                  nodelib_direct_isboth                 },
11678    { "ischar",                  nodelib_direct_ischar                 },
11679    { "isdirect",                nodelib_direct_isdirect               },
11680    { "isglyph",                 nodelib_direct_isglyph                },
11681    { "isitalicglyph",           nodelib_direct_isitalicglyph          },
11682    { "isloop",                  nodelib_direct_isloop                 },
11683    { "isnext",                  nodelib_direct_isnext                 },
11684    { "isnextchar",              nodelib_direct_isnextchar             },
11685    { "isnextglyph",             nodelib_direct_isnextglyph            },
11686    { "isnode",                  nodelib_direct_isnode                 },
11687    { "isprev",                  nodelib_direct_isprev                 },
11688    { "isprevchar",              nodelib_direct_isprevchar             },
11689    { "isprevglyph",             nodelib_direct_isprevglyph            },
11690    { "issimilarglyph",          nodelib_direct_issimilarglyph         },
11691    { "isspeciallist",           nodelib_direct_isspeciallist          },
11692    { "isvalid",                 nodelib_direct_isvalid                },
11693    { "iszeroglue",              nodelib_direct_iszeroglue             },
11694    { "kerning",                 nodelib_direct_kerning                },
11695    { "lastnode",                nodelib_direct_lastnode               },
11696    { "length",                  nodelib_direct_length                 },
11697    { "ligaturing",              nodelib_direct_ligaturing             },
11698    { "makeextensible",          nodelib_direct_makeextensible         },
11699    { "migrate",                 nodelib_direct_migrate                },
11700    { "mlisttohlist",            nodelib_direct_mlisttohlist           },
11701    { "naturalhsize",            nodelib_direct_naturalhsize           },
11702    { "naturalwidth",            nodelib_direct_naturalwidth           },
11703    { "new",                     nodelib_direct_new                    },
11704    { "newcontinuationatom",     nodelib_direct_newcontinuationatom    },
11705    { "newmathglyph",            nodelib_direct_newmathglyph           },
11706    { "newtextglyph",            nodelib_direct_newtextglyph           },
11707    { "patchattributes",         nodelib_direct_patchattributes        },
11708    { "patchparshape",           nodelib_direct_patchparshape          },
11709    { "prependbeforehead",       nodelib_direct_prependbeforehead      },
11710    { "protectglyph",            nodelib_direct_protectglyph           },
11711    { "protectglyphs",           nodelib_direct_protectglyphs          },
11712    { "protectglyphsnone",       nodelib_direct_protectglyphsnone      },
11713    { "protrusionskippable",     nodelib_direct_protrusionskipable     },
11714    { "rangedimensions",         nodelib_direct_rangedimensions        }, /* maybe get... */
11715    { "remove",                  nodelib_direct_remove                 },
11716    { "removefromlist",          nodelib_direct_removefromlist         },
11717    { "repack",                  nodelib_direct_repack                 },
11718    { "reverse",                 nodelib_direct_reverse                },
11719    { "serialized",              nodelib_direct_serialized             },
11720    { "setanchors",              nodelib_direct_setanchors             },
11721    { "setattribute",            nodelib_direct_setattribute           },
11722    { "setattributelist",        nodelib_direct_setattributelist       },
11723    { "setattributes",           nodelib_direct_setattributes          },
11724    { "setboth",                 nodelib_direct_setboth                },
11725    { "setbottom",               nodelib_direct_setbottom              },
11726    { "setbottomdelimiter",      nodelib_direct_setbottomdelimiter     },
11727    { "setbox",                  nodelib_direct_setbox                 },
11728    { "setchar",                 nodelib_direct_setchar                },
11729    { "setchardict",             nodelib_direct_setchardict            },
11730    { "setchoice",               nodelib_direct_setchoice              },
11731    { "setclass",                nodelib_direct_setclass               },
11732    { "setcontrol",              nodelib_direct_setcontrol             },
11733    { "setdata",                 nodelib_direct_setdata                },
11734    { "setdegree",               nodelib_direct_setdegree              },
11735    { "setdelimiter",            nodelib_direct_setdelimiter           },
11736    { "setdenominator",          nodelib_direct_setdenominator         },
11737    { "setdepth",                nodelib_direct_setdepth               },
11738    { "setdirection",            nodelib_direct_setdirection           },
11739    { "setdisc",                 nodelib_direct_setdisc                },
11740    { "setdiscpart",             nodelib_direct_setdiscpart            },
11741    { "setexcept",               nodelib_direct_setexcept              },
11742    { "setexpansion",            nodelib_direct_setexpansion           },
11743    { "setfam",                  nodelib_direct_setfam                 },
11744    { "setfield",                nodelib_direct_setfield               },
11745    { "setfont",                 nodelib_direct_setfont                },
11746    { "setgeometry",             nodelib_direct_setgeometry            },
11747    { "setglue",                 nodelib_direct_setglue                },
11748    { "setglyphdata",            nodelib_direct_setglyphdata           },
11749    { "setheight",               nodelib_direct_setheight              },
11750    { "setindex",                nodelib_direct_setindex               },
11751    { "setinputfields",          nodelib_direct_setinputfields         },
11752    { "setkern",                 nodelib_direct_setkern                },
11753    { "setlanguage",             nodelib_direct_setlanguage            },
11754    { "setleader",               nodelib_direct_setleader              },
11755    { "setleftdelimiter",        nodelib_direct_setleftdelimiter       },
11756    { "setlink",                 nodelib_direct_setlink                },
11757    { "setlist",                 nodelib_direct_setlist                },
11758    { "setnext",                 nodelib_direct_setnext                },
11759    { "setnucleus",              nodelib_direct_setnucleus             },
11760    { "setnumerator",            nodelib_direct_setnumerator           },
11761    { "setoffsets",              nodelib_direct_setoffsets             },
11762    { "setoptions",              nodelib_direct_setoptions             },
11763    { "setorientation",          nodelib_direct_setorientation         },
11764    { "setpenalty",              nodelib_direct_setpenalty             },
11765    { "setpost",                 nodelib_direct_setpost                },
11766    { "setpre",                  nodelib_direct_setpre                 },
11767    { "setprev",                 nodelib_direct_setprev                },
11768    { "setprime" ,               nodelib_direct_setprime               },
11769    { "setproperty",             nodelib_direct_setproperty            },
11770    { "setreplace",              nodelib_direct_setreplace             },
11771    { "setrightdelimiter",       nodelib_direct_setrightdelimiter      },
11772    { "setruledimensions",       nodelib_direct_setruledimensions      },
11773    { "setscale",                nodelib_direct_setscale               },
11774    { "setscales",               nodelib_direct_setscales              },
11775    { "setscript",               nodelib_direct_setscript              },
11776    { "setscripts",              nodelib_direct_setscripts             },
11777    { "setshift",                nodelib_direct_setshift               },
11778    { "setslant",                nodelib_direct_setslant               },
11779    { "setspeciallist",          nodelib_direct_setspeciallist         },
11780    { "setsplit",                nodelib_direct_setsplit               },
11781    { "setstate",                nodelib_direct_setstate               },
11782    { "setsub",                  nodelib_direct_setsub                 },
11783    { "setsubpre",               nodelib_direct_setsubpre              },
11784    { "setsubtype",              nodelib_direct_setsubtype             },
11785    { "setsup",                  nodelib_direct_setsup                 },
11786    { "setsuppre",               nodelib_direct_setsuppre              },
11787    { "settop" ,                 nodelib_direct_settop                 },
11788    { "settopdelimiter",         nodelib_direct_settopdelimiter        },
11789    { "settotal" ,               nodelib_direct_settotal               },
11790    { "setweight",               nodelib_direct_setweight              },
11791    { "setwhd",                  nodelib_direct_setwhd                 },
11792    { "setwidth",                nodelib_direct_setwidth               },
11793    { "show",                    nodelib_direct_show                   },
11794    { "slide",                   nodelib_direct_slide                  },
11795    { "softenhyphens",           nodelib_direct_softenhyphens          },
11796    { "startofpar",              nodelib_direct_startofpar             },
11797    { "tail",                    nodelib_direct_tail                   },
11798    { "tostring",                nodelib_direct_tostring               },
11799    { "tovaliddirect",           nodelib_direct_tovaliddirect          },
11800    { "traverse",                nodelib_direct_traverse               },
11801    { "traversechar",            nodelib_direct_traversechar           },
11802    { "traversecontent",         nodelib_direct_traversecontent        },
11803    { "traverseglyph",           nodelib_direct_traverseglyph          },
11804    { "traverseid",              nodelib_direct_traverseid             },
11805    { "traverseitalic",          nodelib_direct_traverseitalic         },
11806    { "traverseleader",          nodelib_direct_traverseleader         },
11807    { "traverselist",            nodelib_direct_traverselist           },
11808    { "unprotectglyph",          nodelib_direct_unprotectglyph         },
11809    { "unprotectglyphs",         nodelib_direct_unprotectglyphs        },
11810    { "unsetattribute",          nodelib_direct_unsetattribute         },
11811    { "unsetattributes",         nodelib_direct_unsetattributes        },
11812    { "usedlist",                nodelib_direct_usedlist               },
11813    { "usesfont",                nodelib_direct_usesfont               },
11814    { "verticalbreak",           nodelib_direct_verticalbreak          },
11815    { "vpack",                   nodelib_direct_vpack                  },
11816    { "write",                   nodelib_direct_write                  },
11817    { "xscaled",                 nodelib_direct_xscaled                },
11818    { "yscaled",                 nodelib_direct_yscaled                },
11819
11820    { "vbalance",                nodelib_direct_vbalance               },
11821
11822    { "updatetopmarks",          nodelib_direct_updatetopmarks         },
11823    { "updatefirstmarks",        nodelib_direct_updatefirstmarks       },
11824    { "updatefirstandbotmark",   nodelib_direct_updatefirstandbotmark  },
11825    { "updatemarks",             nodelib_direct_updatemarks            },
11826
11827 /* { "appendtocurrentlist",     nodelib_direct_appendtocurrentlist    }, */ /* beware, we conflict in ctx */
11828
11829    { "gluetostring",            nodelib_hybrid_gluetostring           },
11830    { "type",                    nodelib_hybrid_type                   },
11831
11832    { "fields",                  nodelib_shared_fields                 },
11833    { "getcachestate",           nodelib_shared_getcachestate          },
11834    { "id",                      nodelib_shared_id                     },
11835    { "size",                    nodelib_shared_size                   },
11836    { "subtypes",                nodelib_shared_subtypes               },
11837    { "todirect",                nodelib_shared_todirect               },
11838    { "tonode",                  nodelib_shared_tonode                 },
11839    { "types",                   nodelib_shared_types                  },
11840 /* { "values",                  nodelib_shared_values                 }, */ /* finally all are now in tex. */
11841
11842    { "setfielderror",            nodelib_shared_setfielderror         }, /* private */
11843    { "getfielderror",            nodelib_shared_getfielderror         }, /* private */
11844
11845    { NULL,                      NULL                                  },
11846
11847};
11848
11849/* node.* */
11850
11851static const struct luaL_Reg nodelib_function_list[] = {
11852
11853    { "copy",                     nodelib_userdata_copy                 },
11854    { "copylist",                 nodelib_userdata_copylist             },
11855    { "currentattributes",        nodelib_userdata_currentattributes    },
11856    { "flushlist",                nodelib_userdata_flushlist            },
11857    { "flushnode",                nodelib_userdata_flushnode            },
11858    { "free",                     nodelib_userdata_free                 },
11859    { "getattribute",             nodelib_userdata_getattribute         },
11860    { "getfield",                 nodelib_userdata_getfield             },
11861    { "getpropertiestable",       nodelib_userdata_getpropertiestable   },
11862    { "getproperty",              nodelib_userdata_getproperty          },
11863    { "hasattribute",             nodelib_userdata_hasattribute         },
11864    { "hasfield",                 nodelib_userdata_hasfield             },
11865    { "insertafter",              nodelib_userdata_insertafter          },
11866    { "insertbefore",             nodelib_userdata_insertbefore         },
11867    { "instock",                  nodelib_userdata_instock              },
11868    { "inuse",                    nodelib_userdata_inuse                },
11869    { "isnode",                   nodelib_userdata_isnode               },
11870    { "new",                      nodelib_userdata_new                  },
11871    { "remove",                   nodelib_userdata_remove               },
11872    { "serialized",               nodelib_userdata_serialized           },
11873    { "setattribute",             nodelib_userdata_setattribute         },
11874    { "setfield",                 nodelib_userdata_setfield             },
11875    { "setproperty",              nodelib_userdata_setproperty          },
11876    { "show",                     nodelib_userdata_show                 },
11877    { "tail",                     nodelib_userdata_tail                 },
11878    { "tostring",                 nodelib_userdata_tostring             },
11879    { "traverse",                 nodelib_userdata_traverse             },
11880    { "traverseid",               nodelib_userdata_traverse_id          },
11881    { "unsetattribute",           nodelib_userdata_unsetattribute       },
11882    { "usedlist",                 nodelib_userdata_usedlist             },
11883    { "write",                    nodelib_userdata_write                },
11884 /* { "appendtocurrentlist",      nodelib_userdata_append               }, */ /* beware, we conflict in ctx */
11885
11886    { "gluetostring",             nodelib_hybrid_gluetostring           },
11887    { "type",                     nodelib_hybrid_type                   },
11888
11889    { "fields",                   nodelib_shared_fields                 },
11890    { "getcachestate",            nodelib_shared_getcachestate          },
11891    { "id",                       nodelib_shared_id                     },
11892    { "size",                     nodelib_shared_size                   },
11893    { "subtypes",                 nodelib_shared_subtypes               },
11894    { "todirect",                 nodelib_shared_todirect               },
11895    { "tonode",                   nodelib_shared_tonode                 },
11896    { "types",                    nodelib_shared_types                  },
11897 /* { "values",                   nodelib_shared_values                 }, */ /* finally all are now in tex. */
11898
11899    { "setfielderror",            nodelib_shared_setfielderror          }, /* private */
11900    { "getfielderror",            nodelib_shared_getfielderror          }, /* private */
11901
11902    { NULL,                       NULL                                  },
11903
11904};
11905
11906static const struct luaL_Reg nodelib_metatable[] = {
11907    { "__index",    nodelib_userdata_index    },
11908    { "__newindex", nodelib_userdata_newindex },
11909    { "__tostring", nodelib_userdata_tostring },
11910    { "__eq",       nodelib_userdata_equal    },
11911    { NULL,         NULL                      },
11912};
11913
11914int luaopen_node(lua_State *L)
11915{
11916    /*tex the main metatable of node userdata */
11917    luaL_newmetatable(L, NODE_METATABLE_INSTANCE);
11918    /* node.* */
11919    luaL_setfuncs(L, nodelib_metatable, 0);
11920    lua_newtable(L);
11921    luaL_setfuncs(L, nodelib_function_list, 0);
11922    /* node.direct */
11923    lua_pushstring(L, lua_key(direct));
11924    lua_newtable(L);
11925    luaL_setfuncs(L, nodelib_direct_function_list, 0);
11926    lua_rawset(L, -3);
11927    return 1;
11928}
11929
11930void lmt_node_list_to_lua(lua_State *L, halfword n)
11931{
11932    lmt_push_node_fast(L, n);
11933}
11934
11935halfword lmt_node_list_from_lua(lua_State *L, int n)
11936{
11937    if (lua_isnil(L, n)) {
11938        return null;
11939    } else {
11940        halfword list = lmt_check_isnode(L, n);
11941        return list ? list : null;
11942    }
11943}
11944
11945/*tex
11946    Here come the callbacks that deal with node lists. Some are called in multiple locations and
11947    then get additional information passed concerning the whereabouts.
11948
11949    The begin paragraph callback first got |cmd| and |chr| but in the end it made more sense to
11950    do it like the rest and pass a string. There is no need for more granularity.
11951 */
11952
11953void lmt_begin_paragraph_callback(
11954    int  invmode,
11955    int *indented,
11956    int  context
11957)
11958{
11959    int callback_id = lmt_callback_defined(begin_paragraph_callback);
11960    if (callback_id > 0) {
11961        lua_State *L = lmt_lua_state.lua_instance;
11962        int top = 0;
11963        if (lmt_callback_okay(L, callback_id, &top)) {
11964            int i;
11965            lua_pushboolean(L, invmode);
11966            lua_pushboolean(L, *indented);
11967            lmt_push_par_trigger(L, context);
11968            i = lmt_callback_call(L, 3, 1, top);
11969            /* done */
11970            if (i) {
11971                lmt_callback_error(L, top, i);
11972            }
11973            else {
11974                *indented = lua_toboolean(L, -1);
11975                lmt_callback_wrapup(L, top);
11976            }
11977        }
11978    }
11979}
11980
11981void lmt_paragraph_context_callback(
11982    int  context,
11983    int *ignore
11984)
11985{
11986    int callback_id = lmt_callback_defined(paragraph_context_callback);
11987    if (callback_id > 0) {
11988        lua_State *L = lmt_lua_state.lua_instance;
11989        int top = 0;
11990        if (lmt_callback_okay(L, callback_id, &top)) {
11991            int i;
11992            lmt_push_par_context(L, context);
11993            i = lmt_callback_call(L, 1, 1, top);
11994            if (i) {
11995                lmt_callback_error(L, top, i);
11996            }
11997            else {
11998                *ignore = lua_toboolean(L, -1);
11999                lmt_callback_wrapup(L, top);
12000            }
12001        }
12002    }
12003}
12004
12005void lmt_page_filter_callback(
12006    int      context,
12007    halfword boundary
12008)
12009{
12010    int callback_id = lmt_callback_defined(buildpage_filter_callback);
12011    if (callback_id > 0) {
12012        lua_State *L = lmt_lua_state.lua_instance;
12013        int top = 0;
12014        if (lmt_callback_okay(L, callback_id, &top)) {
12015            int i;
12016            lmt_push_page_context(L, context);
12017            lua_push_halfword(L, boundary);
12018            i = lmt_callback_call(L, 2, 0, top);
12019            if (i) {
12020                lmt_callback_error(L, top, i);
12021            } else {
12022                lmt_callback_wrapup(L, top);
12023            }
12024        }
12025    }
12026}
12027
12028/*tex This one gets |tail| and optionally gets back |head|. */
12029
12030void lmt_append_line_filter_callback(
12031    halfword context,
12032    halfword index /* class */
12033)
12034{
12035    if (cur_list.tail) {
12036        int callback_id = lmt_callback_defined(append_line_filter_callback);
12037        if (callback_id > 0) {
12038            lua_State *L = lmt_lua_state.lua_instance;
12039            int top = 0;
12040            if (lmt_callback_okay(L, callback_id, &top)) {
12041                int i;
12042                lmt_node_list_to_lua(L, node_next(cur_list.head));
12043                lmt_node_list_to_lua(L, cur_list.tail);
12044                lmt_push_append_line_context(L, context);
12045                lua_push_halfword(L, index);
12046                i = lmt_callback_call(L, 4, 1, top);
12047                if (i) {
12048                    lmt_callback_error(L, top, i);
12049                } else {
12050                    if (lua_type(L, -1) == LUA_TUSERDATA) {
12051                        int a = lmt_node_list_from_lua(L, -1);
12052                        node_next(cur_list.head) = a;
12053                        cur_list.tail = tex_tail_of_node_list(a);
12054                    }
12055                    lmt_callback_wrapup(L, top);
12056                }
12057            }
12058        }
12059    }
12060}
12061
12062/*tex
12063
12064    Eventually the optional fixing of lists will go away because we assume that proper double linked
12065    lists get returned. Keep in mind that \TEX\ itself never looks back (we didn't change that bit,
12066    at least not until now) so it's only callbacks that suffer from bad |prev| fields.
12067
12068*/
12069
12070void lmt_node_filter_callback(
12071    int       filterid,
12072    int       extrainfo,
12073    halfword  head,
12074    halfword *tail
12075)
12076{
12077    if (head) {
12078        /*tex We start after head (temp). */
12079        halfword start = node_next(head);
12080        if (start) {
12081            int callback_id = lmt_callback_defined(filterid);
12082            if (callback_id > 0) {
12083                lua_State *L = lmt_lua_state.lua_instance;
12084                int top = 0;
12085                if (lmt_callback_okay(L, callback_id, &top)) {
12086                    int i;
12087                    /*tex We make sure we have no prev */
12088                    node_prev(start) = null;
12089                    /*tex the action */
12090                    lmt_node_list_to_lua(L, start);
12091                    lmt_push_group_code(L, extrainfo);
12092                    i = lmt_callback_call(L, 2, 1, top);
12093                    if (i) {
12094                        lmt_callback_error(L, top, i);
12095                    } else {
12096                        /*tex append to old head */
12097                        halfword list = lmt_node_list_from_lua(L, -1);
12098                        tex_try_couple_nodes(head, list);
12099                        /*tex redundant as we set top anyway */
12100                        lua_pop(L, 2);
12101                        /*tex find tail in order to update tail */
12102                        *tail = tex_tail_of_node_list(head);
12103                        lmt_callback_wrapup(L, top);
12104                    }
12105                }
12106            }
12107        }
12108    }
12109    return;
12110}
12111
12112/*tex
12113    Maybe this one will get extended a bit in due time.
12114*/
12115
12116int lmt_linebreak_callback(
12117    halfword  head,
12118    int       isbroken, /* display_math */
12119    halfword *newhead
12120)
12121{
12122    if (head) {
12123        halfword start = node_next(head);
12124        if (start) {
12125            int callback_id = lmt_callback_defined(linebreak_filter_callback);
12126            if (callback_id > 0) {
12127                lua_State *L = lmt_lua_state.lua_instance;
12128                int top = 0;
12129                if (callback_id > 0 && lmt_callback_okay(L, callback_id, &top)) {
12130                    int i;
12131                    int ret = 0;
12132                    node_prev(start) = null;
12133                    lmt_node_list_to_lua(L, start);
12134                    lua_pushboolean(L, isbroken);
12135                    i = lmt_callback_call(L, 2, 1, top);
12136                    if (i) {
12137                        lmt_callback_error(L, top, i);
12138                    } else {
12139                        halfword *result = lua_touserdata(L, -1);
12140                        if (result) {
12141                            halfword list = lmt_node_list_from_lua(L, -1);
12142                            tex_try_couple_nodes(*newhead, list);
12143                            ret = 1;
12144                        }
12145                        lmt_callback_wrapup(L, top);
12146                    }
12147                    return ret;
12148                }
12149            }
12150        }
12151    }
12152    return 0;
12153}
12154
12155void lmt_alignment_callback(
12156    halfword head,
12157    halfword context,
12158    halfword callback,
12159    halfword attrlist,
12160    halfword preamble
12161)
12162{
12163    if (head || preamble) {
12164        int callback_id = lmt_callback_defined(alignment_filter_callback);
12165        if (callback_id > 0) {
12166            lua_State *L = lmt_lua_state.lua_instance;
12167            int top = 0;
12168            if (lmt_callback_okay(L, callback_id, &top)) {
12169                int i;
12170                lmt_node_list_to_lua(L, head);
12171                lmt_push_alignment_context(L, context);
12172                lua_pushinteger(L, callback);
12173                lmt_node_list_to_lua(L, attrlist);
12174                lmt_node_list_to_lua(L, preamble);
12175                i = lmt_callback_call(L, 5, 0, top);
12176                if (i) {
12177                    lmt_callback_error(L, top, i);
12178                } else {
12179                    lmt_callback_wrapup(L, top);
12180                }
12181            }
12182        }
12183    }
12184    return;
12185}
12186
12187void lmt_local_box_callback(
12188    halfword linebox,
12189    halfword leftbox,
12190    halfword rightbox,
12191    halfword middlebox,
12192    halfword linenumber,
12193    scaled   leftskip,
12194    scaled   rightskip,
12195    scaled   lefthang,
12196    scaled   righthang,
12197    scaled   indentation,
12198    scaled   parinitleftskip,
12199    scaled   parinitrightskip,
12200    scaled   parfillleftskip,
12201    scaled   parfillrightskip,
12202    scaled   overshoot
12203)
12204{
12205    if (linebox) {
12206        int callback_id = lmt_callback_defined(local_box_filter_callback);
12207        if (callback_id > 0) {
12208            lua_State *L = lmt_lua_state.lua_instance;
12209            int top = 0;
12210            if (lmt_callback_okay(L, callback_id, &top)) {
12211                int i;
12212                lmt_node_list_to_lua(L, linebox);
12213                lmt_node_list_to_lua(L, leftbox);
12214                lmt_node_list_to_lua(L, rightbox);
12215                lmt_node_list_to_lua(L, middlebox);
12216                lua_pushinteger(L, linenumber);
12217                lua_pushinteger(L, leftskip);
12218                lua_pushinteger(L, rightskip);
12219                lua_pushinteger(L, lefthang);
12220                lua_pushinteger(L, righthang);
12221                lua_pushinteger(L, indentation);
12222                lua_pushinteger(L, parinitleftskip);
12223                lua_pushinteger(L, parinitrightskip);
12224                lua_pushinteger(L, parfillleftskip);
12225                lua_pushinteger(L, parfillrightskip);
12226                lua_pushinteger(L, overshoot);
12227                i = lmt_callback_call(L, 15, 0, top);
12228                if (i) {
12229                    lmt_callback_error(L, top, i);
12230                } else {
12231                    /* todo: check if these boxes are still okay (defined) */
12232                    lmt_callback_wrapup(L, top);
12233                }
12234            }
12235        }
12236    }
12237}
12238
12239/*tex
12240    This one is a bit different from the \LUATEX\ variant. The direction parameter has been dropped
12241    and prevdepth correction can be controlled.
12242*/
12243
12244int lmt_append_to_vlist_callback(
12245    halfword  box,
12246    int       location,
12247    halfword  prevdepth,
12248    halfword *result,
12249    int      *nextdepth,
12250    int      *prevset,
12251    int      *checkdepth
12252)
12253{
12254    if (box) {
12255        int callback_id = lmt_callback_defined(append_to_vlist_filter_callback);
12256        if (callback_id > 0) {
12257            lua_State *L = lmt_lua_state.lua_instance;
12258            int top = 0;
12259            if (lmt_callback_okay(L, callback_id, &top)) {
12260                int i;
12261                lmt_node_list_to_lua(L, box);
12262                lua_push_key_by_index(location);
12263                lua_pushinteger(L, (int) prevdepth);
12264                i = lmt_callback_call(L, 3, 3, top);
12265                if (i) {
12266                    lmt_callback_error(L, top, i);
12267                } else {
12268                    switch (lua_type(L, -3)) {
12269                        case LUA_TUSERDATA:
12270                            *result = lmt_check_isnode(L, -3);
12271                            break;
12272                        case LUA_TNIL:
12273                            *result = null;
12274                            break;
12275                        default:
12276                            tex_normal_warning("append to vlist callback", "node or nil expected");
12277                            break;
12278                    }
12279                    if (lua_type(L, -2) == LUA_TNUMBER) {
12280                        *nextdepth = lmt_roundnumber(L, -2);
12281                        *prevset = 1;
12282                    }
12283                    if (*result && lua_type(L, -1) == LUA_TBOOLEAN) {
12284                        *checkdepth = lua_toboolean(L, -1);
12285                    }
12286                    lmt_callback_wrapup(L, top);
12287                    return 1;
12288                }
12289            }
12290        }
12291    }
12292    return 0;
12293}
12294
12295/*tex
12296    Here we keep the directions although they play no real role in the
12297    packing process.
12298 */
12299
12300halfword lmt_hpack_filter_callback(
12301    halfword head,
12302    scaled   size,
12303    int      packtype,
12304    int      extrainfo,
12305    int      direction,
12306    halfword attr
12307)
12308{
12309    if (head) {
12310        int callback_id = lmt_callback_defined(hpack_filter_callback);
12311        if (callback_id > 0) {
12312            lua_State *L = lmt_lua_state.lua_instance;
12313            int top = 0;
12314            if (lmt_callback_okay(L, callback_id, &top)) {
12315                int i;
12316                node_prev(head) = null;
12317                lmt_node_list_to_lua(L, head);
12318                lmt_push_group_code(L, extrainfo);
12319                lua_pushinteger(L, size);
12320                lmt_push_pack_type(L, packtype);
12321                if (direction >= 0) {
12322                    lua_pushinteger(L, direction);
12323                } else {
12324                    lua_pushnil(L);
12325                }
12326                /* maybe: (attr && attr != cache_disabled) */
12327                lmt_node_list_to_lua(L, attr);
12328                i = lmt_callback_call(L, 6, 1, top);
12329                if (i) {
12330                    lmt_callback_error(L, top, i);
12331                } else {
12332                    head = lmt_node_list_from_lua(L, -1);
12333                    lmt_callback_wrapup(L, top);
12334                }
12335            }
12336        }
12337    }
12338    return head;
12339}
12340
12341extern halfword lmt_packed_vbox_filter_callback(
12342    halfword box,
12343    int      extrainfo
12344)
12345{
12346    if (box) {
12347        int callback_id = lmt_callback_defined(packed_vbox_filter_callback);
12348        if (callback_id > 0) {
12349            lua_State *L = lmt_lua_state.lua_instance;
12350            int top = 0;
12351            if (lmt_callback_okay(L, callback_id, &top)) {
12352                int i;
12353                lmt_node_list_to_lua(L, box);
12354                lmt_push_group_code(L, extrainfo);
12355                i = lmt_callback_call(L, 2, 1, top);
12356                if (i) {
12357                    lmt_callback_error(L, top, i);
12358                } else {
12359                    box = lmt_node_list_from_lua(L, -1);
12360                    lmt_callback_wrapup(L, top);
12361                }
12362            }
12363        }
12364    }
12365    return box;
12366}
12367
12368halfword lmt_vpack_filter_callback(
12369    halfword head,
12370    scaled   size,
12371    int      packtype,
12372    scaled   maxdepth,
12373    int      extrainfo,
12374    int      direction,
12375    halfword attr
12376)
12377{
12378    if (head) {
12379        int callback_id = lmt_callback_defined(extrainfo == output_group ? pre_output_filter_callback : vpack_filter_callback);
12380        if (callback_id > 0) {
12381            lua_State *L = lmt_lua_state.lua_instance;
12382            int top = 0;
12383            if (lmt_callback_okay(L, callback_id, &top)) {
12384                int i;
12385                node_prev(head) = null;
12386                lmt_node_list_to_lua(L, head);
12387                lmt_push_group_code(L, extrainfo);
12388                lua_pushinteger(L, size);
12389                lmt_push_pack_type(L, packtype);
12390                lua_pushinteger(L, maxdepth);
12391                if (direction >= 0) {
12392                    lua_pushinteger(L, direction);
12393                } else {
12394                    lua_pushnil(L);
12395                }
12396                lmt_node_list_to_lua(L, attr);
12397                i = lmt_callback_call(L, 7, 1, top);
12398                if (i) {
12399                    lmt_callback_error(L, top, i);
12400                } else {
12401                    head = lmt_node_list_from_lua(L, -1);
12402                    lmt_callback_wrapup(L, top);
12403                }
12404            }
12405        }
12406    }
12407    return head;
12408}
12409
12410/* watch the -3 here, we skip over the repeat boolean  */
12411
12412# define get_dimension_par(P,A,B) \
12413    lua_push_key(A); \
12414    P = (lua_rawget(L, -3) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : B; \
12415    lua_pop(L, 1);
12416
12417# define get_integer_par(P,A,B) \
12418    lua_push_key(A); \
12419    P = (lua_rawget(L, -3) == LUA_TNUMBER) ? lmt_tohalfword(L, -1) : B; \
12420    lua_pop(L, 1);
12421
12422int lmt_par_pass_callback(
12423    halfword               head,
12424    line_break_properties *properties,
12425    halfword               identifier,
12426    halfword               subpass,
12427    halfword               callback,
12428    halfword               features,
12429    scaled                 overfull,
12430    scaled                 underfull,
12431    halfword               verdict,
12432    halfword               classified,
12433    scaled                 threshold,
12434    halfword               demerits,
12435    halfword               classes,
12436    int                   *repeat
12437)
12438{
12439 // if (head) {
12440        int callback_id = lmt_callback_defined(paragraph_pass_callback);
12441        (void) features;
12442        if (callback_id > 0) {
12443            lua_State *L = lmt_lua_state.lua_instance;
12444            int top = 0;
12445            int result = 0;
12446            if (lmt_callback_okay(L, callback_id, &top)) {
12447                int i;
12448                lmt_node_list_to_lua(L, head);
12449                lua_push_integer(L, identifier);
12450                lua_push_integer(L, subpass);
12451                lua_push_integer(L, callback);
12452                lua_push_integer(L, overfull);
12453                lua_push_integer(L, underfull);
12454                lua_push_integer(L, verdict);
12455                lua_push_integer(L, classified);
12456                lua_push_integer(L, threshold);
12457                lua_push_integer(L, demerits);
12458                lua_push_integer(L, classes);
12459                i = lmt_callback_call(L, 10, 2, top);
12460                if (i) {
12461                    lmt_callback_error(L, top, i);
12462                } else {
12463                    switch (lua_type(L, -2)) {
12464                        case LUA_TBOOLEAN:
12465                            {
12466                                result = lua_toboolean(L, -2);
12467                                break;
12468                            }
12469                        case LUA_TTABLE:
12470                            {
12471                                /*tex
12472                                    This is untested and might go away at some point.
12473                                */
12474                                get_integer_par(properties->tolerance, tolerance, properties->tolerance);
12475                                lmt_linebreak_state.threshold = properties->tolerance;
12476                                lmt_linebreak_state.global_threshold = lmt_linebreak_state.threshold;
12477                                get_integer_par(properties->line_penalty, linepenalty, properties->line_penalty);
12478                                get_integer_par(properties->left_twin_demerits, lefttwindemerits, properties->left_twin_demerits);
12479                                get_integer_par(properties->right_twin_demerits, righttwindemerits, properties->right_twin_demerits);
12480                                get_integer_par(properties->extra_hyphen_penalty, extrahyphenpenalty, properties->extra_hyphen_penalty);
12481                                get_integer_par(properties->double_hyphen_demerits, doublehyphendemerits, properties->double_hyphen_demerits);
12482                                get_integer_par(properties->final_hyphen_demerits, finalhyphendemerits, properties->final_hyphen_demerits);
12483                                get_integer_par(properties->adj_demerits, adjdemerits, properties->adj_demerits);
12484                                get_dimension_par(properties->emergency_stretch, emergencystretch, properties->emergency_stretch);
12485                                get_integer_par(properties->adjust_spacing_step, adjustspacingstep, properties->adjust_spacing_step);
12486                                get_integer_par(properties->adjust_spacing_shrink, adjustspacingshrink, properties->adjust_spacing_shrink);
12487                                get_integer_par(properties->adjust_spacing_stretch, adjustspacingstretch, properties->adjust_spacing_stretch);
12488                                get_integer_par(properties->adjust_spacing, adjustspacing, properties->adjust_spacing);
12489                                get_integer_par(properties->line_break_optional, optional, properties->line_break_optional);
12490                                get_integer_par(properties->line_break_checks, linebreakchecks, properties->line_break_checks);
12491                                get_integer_par(properties->math_penalty_factor, mathpenaltyfactor, properties->math_penalty_factor);
12492                                get_integer_par(properties->sf_factor, sffactor, properties->sf_factor);
12493                                get_integer_par(properties->sf_stretch_factor, sfstretchfactor, properties->sf_stretch_factor);
12494                                /*tex
12495                                    These are not properties (yet, but we could just add these as hidden fields):
12496
12497                                    emergencyfactor
12498                                    emergencypercentage
12499                                    emergencyleftextra
12500                                    emergencyrightextra
12501                                    emergencywidthextra
12502
12503                                    These make no sense here:
12504
12505                                    fitnessdemerits
12506                                    hyphenation
12507
12508                                */
12509                                result = 1;
12510                                break;
12511                            }
12512                        default:
12513                            {
12514                                /* just quit */
12515                                break;
12516                            }
12517                    }
12518                    if (lua_type(L, -1) == LUA_TBOOLEAN) {
12519                        *repeat = lua_toboolean(L, -1);
12520                    }
12521                }
12522            }
12523            lmt_callback_wrapup(L, top);
12524            return result;
12525        }
12526 // }
12527    return 0;
12528}
12529
12530halfword lmt_uleader_callback(
12531    halfword    head,
12532    halfword    index,
12533    int         context,
12534    halfword    box,
12535    int         location
12536)
12537{
12538    if (head) {
12539        int callback_id = lmt_callback_defined(handle_uleader_callback);
12540        if (callback_id > 0) {
12541            lua_State *L = lmt_lua_state.lua_instance;
12542            int top = 0;
12543            if (lmt_callback_okay(L, callback_id, &top)) {
12544                int i;
12545                lmt_node_list_to_lua(L, head);
12546             // lua_pushinteger(L, context);
12547                lmt_push_group_code(L, context);
12548                lua_pushinteger(L, index);
12549                lmt_node_list_to_lua(L, box);
12550                lua_pushinteger(L, location); /* maybe also string */
12551                i = lmt_callback_call(L, 5, 1, top);
12552                if (i) {
12553                    lmt_callback_error(L, top, i);
12554                } else {
12555                    head = lmt_node_list_from_lua(L, -1);
12556                    lmt_callback_wrapup(L, top);
12557                }
12558            }
12559        }
12560    }
12561    return head;
12562}
12563
12564halfword lmt_uinsert_callback(
12565    halfword callback, 
12566    halfword index,
12567    halfword order, 
12568    halfword packed,
12569    scaled   height, 
12570    scaled   amount
12571)
12572{
12573    if (packed) {
12574        int callback_id = lmt_callback_defined(handle_uinsert_callback);
12575        if (callback_id > 0) {
12576            lua_State *L = lmt_lua_state.lua_instance;
12577            int top = 0;
12578            if (lmt_callback_okay(L, callback_id, &top)) {
12579                int i;
12580                lua_pushinteger(L, callback);
12581                lua_pushinteger(L, index);
12582                lua_pushinteger(L, order);
12583                lmt_node_list_to_lua(L, packed);
12584                lua_pushinteger(L, height);
12585                lua_pushinteger(L, amount);
12586                i = lmt_callback_call(L, 6, 1, top);
12587                if (i) {
12588                    lmt_callback_error(L, top, i);
12589                } else {
12590                    halfword result = lmt_node_list_from_lua(L, -1); 
12591                    if (result) { 
12592                        packed = result; 
12593                    }
12594                    lmt_callback_wrapup(L, top);
12595                }
12596            }
12597        }
12598    }
12599    return packed;
12600}
12601
12602void lmt_insert_par_callback(
12603    halfword node,
12604    halfword mode
12605)
12606{
12607    int callback_id = lmt_callback_defined(insert_par_callback);
12608    if (callback_id > 0) {
12609        lua_State *L = lmt_lua_state.lua_instance;
12610        int top = 0;
12611        if (lmt_callback_okay(L, callback_id, &top)) {
12612            int i;
12613            lmt_node_list_to_lua(L, node);
12614            lmt_push_par_mode(L, mode);
12615            i = lmt_callback_call(L, 2, 0, top);
12616            if (i) {
12617                lmt_callback_error(L, top, i);
12618            } else {
12619                lmt_callback_wrapup(L, top);
12620            }
12621        }
12622    }
12623}
12624
12625scaled lmt_italic_correction_callback(
12626    halfword glyph,
12627    scaled   kern,
12628    halfword subtype
12629)
12630{
12631    int callback_id = lmt_callback_defined(italic_correction_callback);
12632    if (callback_id > 0) {
12633        lua_State *L = lmt_lua_state.lua_instance;
12634        int top = 0;
12635        if (lmt_callback_okay(L, callback_id, &top)) {
12636            int i;
12637            lmt_node_list_to_lua(L, glyph);
12638            lua_pushinteger(L, kern);
12639            lua_pushinteger(L, subtype);
12640            i = lmt_callback_call(L, 3, 1, top);
12641            if (i) {
12642                lmt_callback_error(L, top, i);
12643            } else {
12644                kern = lmt_tohalfword(L, -1);
12645                lmt_callback_wrapup(L, top);
12646            }
12647        }
12648    }
12649    return kern;
12650}
12651