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