lmttexlib.c /size: 286 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5/*
6
7    This module deals with access to some if the internal quanities of \TEX, like registers,
8    internal variables and all kind of lists. Because we provide access by name and/or number
9    (index) there is quite a bit of code here, and sometimes if can look a bit confusing.
10
11    The code here differs from \LUATEX\ in the sense that there are some more checks, a bit
12    more abstraction, more access and better performance. What we see here is the result of
13    years of experimenting and usage in \CONTEXT.
14
15    A remark about some of the special node lists that one can query: because some start with
16    a so called |temp| node, we have to set the |prev| link to |nil| because otherwise at the
17    \LUA\ end we expose that |temp| node and users are not suposed to touch them! In the setter
18    no |prev| link is set so we can presume that it's not used later on anyway; this is because
19    original \TEX\ has no |prev| links.
20
21    There is still room for improvement but I'll deal with that when I have a reason (read: when
22    I need something).
23
24    We don't reallocate (and share) key references for strings that are only used in functions
25    that report values used as there's little gain. This will be cleaned up as we go.
26
27*/
28
29# include "luametatex.h"
30
31/*tex
32    Due to the nature of the accessors, this is the module with most metatables. However, we
33    provide getters and setters too. Users can choose what they like most. If needed we can use
34    fast metatable resolvers but there is no real need.
35*/
36
37# define TEX_METATABLE_ATTRIBUTE "tex.attribute"
38# define TEX_METATABLE_SKIP      "tex.skip"
39# define TEX_METATABLE_GLUE      "tex.glue"
40# define TEX_METATABLE_MUSKIP    "tex.muskip"
41# define TEX_METATABLE_MUGLUE    "tex.muglue"
42# define TEX_METATABLE_DIMEN     "tex.dimen"
43# define TEX_METATABLE_FLOAT     "tex.float"
44# define TEX_METATABLE_COUNT     "tex.count"
45# define TEX_METATABLE_TOKS      "tex.toks"
46# define TEX_METATABLE_BOX       "tex.box"
47# define TEX_METATABLE_SFCODE    "tex.sfcode"
48# define TEX_METATABLE_LCCODE    "tex.lccode"
49# define TEX_METATABLE_UCCODE    "tex.uccode"
50# define TEX_METATABLE_HCCODE    "tex.hccode"
51# define TEX_METATABLE_HMCODE    "tex.hmcode"
52# define TEX_METATABLE_AMCODE    "tex.amcode"
53# define TEX_METATABLE_CCCODE    "tex.cccode"
54# define TEX_METATABLE_CATCODE   "tex.catcode"
55# define TEX_METATABLE_MATHCODE  "tex.mathcode"
56# define TEX_METATABLE_DELCODE   "tex.delcode"
57# define TEX_METATABLE_LISTS     "tex.lists"
58# define TEX_METATABLE_NEST      "tex.nest"
59
60# define TEX_METATABLE_TEX       "tex.tex"
61
62# define TEX_NEST_INSTANCE       "tex.nest.instance"
63
64/*tex Let's share these. */
65
66static void texlib_aux_show_box_index_error(lua_State *L)
67{
68    luaL_error(L, "invalid index passed, range 0.." LMT_TOSTRING(max_box_register_index) " or name expected");
69}
70
71static void texlib_aux_show_character_error(lua_State *L, int i)
72{
73    luaL_error(L, "invalid character value %d passed, range 0.." LMT_TOSTRING(max_character_code), i);
74}
75
76static void texlib_aux_show_catcode_error(lua_State *L, int i)
77{
78    luaL_error(L, "invalid catcode %d passed, range 0.." LMT_TOSTRING(max_category_code), i);
79}
80
81static void texlib_aux_show_family_error(lua_State *L, int i)
82{
83    luaL_error(L, "invalid family %d passed, range 0.." LMT_TOSTRING(max_math_family_index), i);
84}
85
86static void texlib_aux_show_class_error(lua_State *L, int i)
87{
88    luaL_error(L, "invalid class %d passed, range 0.." LMT_TOSTRING(max_math_class_code), i);
89}
90
91static void texlib_aux_show_half_error(lua_State *L, int i)
92{
93    luaL_error(L, "invalid value %d passed, range 0.." LMT_TOSTRING(max_half_value), i);
94}
95
96/*tex
97
98    The rope model dates from the time that we had multiple \LUA\ instances so probably we can
99    simplify it a bit. On the other hand, it works, and also is upgraded for nodes, tokens and some
100    caching, so there is no real need to change anything now. A complication is anyway that input
101    states can nest and any change can mess it up.
102
103    We use a flag to indicate the kind of data that we are dealing with:
104
105    \starttabulate
106    \NC 0 \NC string \NC \NR
107    \NC 1 \NC char   \NC \NR
108    \NC 2 \NC token  \NC \NR
109    \NC 3 \NC node   \NC \NR
110    \stoptabulate
111
112    By treating simple \ASCII\ characters special we prevent mallocs. We also pack small strings if
113    only because we have the room available anyway (due to padding).
114
115    For quite a while we used to have this:
116
117    \starttyping
118    typedef struct spindle_char {
119        unsigned char c1, c2, c3, c4;
120    } spindle_char;
121
122    typedef union spindle_data {
123        spindle_char c;
124        halfword      h;
125    } spindle_data;
126    \stopttyping
127
128    The spindle and rope terminology was introduced by Taco early in the development of \LUATEX,
129    but in the meantime the datastructures have been adapted to deal with tokens and nodes. There
130    are also quite some optimizations in performance and memort usage (e.g. for small strings and
131    single characters).
132
133*/
134
135typedef enum line_modes {
136    full_line_mode    = 0,
137    partial_line_mode = 1,
138} line_modes;
139
140# define PACKED_SIZE       8
141# define INITIAL_SIZE     32
142# define MAX_ROPE_CACHE 5000
143
144typedef union spindle_data {
145    unsigned char  c[PACKED_SIZE];
146    halfword       h;
147    char          *t;
148} spindle_data;
149
150typedef struct spindle_rope {
151//  char          *text;
152    void          *next;
153    union {
154        unsigned int tsize;
155        halfword     tail;
156    };
157    unsigned char  kind;
158    unsigned char  partial;
159    short          cattable;
160    spindle_data   data;
161    /* alignment, not needed when we have c[PACKED_SIZE] */
162 /* int            padding; */
163} spindle_rope;
164
165typedef struct spindle {
166    spindle_rope *head;
167    spindle_rope *tail;
168    int           complete;
169    /* alignment */
170    int           padding;
171} spindle;
172
173typedef struct spindle_state_info {
174    int           spindle_size;
175    int           spindle_index;
176    spindle      *spindles;
177    spindle_rope *rope_cache;
178    int           rope_count;
179    /* alignment */
180    int           padding;
181} spindle_state_info ;
182
183static spindle_state_info lmt_spindle_state = {
184    .spindle_size  = 0,
185    .spindle_index = 0,
186    .spindles      = NULL,
187    .rope_cache    = NULL,
188    .rope_count    = 0,
189    .padding       = 0
190};
191
192# define write_spindle lmt_spindle_state.spindles[lmt_spindle_state.spindle_index]
193# define read_spindle  lmt_spindle_state.spindles[lmt_spindle_state.spindle_index - 1]
194
195static inline void texlib_aux_reset_spindle(int i)
196{
197    lmt_spindle_state.spindles[i].head = NULL;
198    lmt_spindle_state.spindles[i].tail = NULL;
199    lmt_spindle_state.spindles[i].complete = 0;
200}
201
202/*
203
204    Each rope takes 48 bytes. So, caching some 100 K ropes is not really a problem. In practice we
205    seldom reach that number anyway.
206
207*/
208
209static inline spindle_rope *texlib_aux_new_rope(void)
210{
211    spindle_rope *rope;
212    if (lmt_spindle_state.rope_cache) {
213        rope = lmt_spindle_state.rope_cache;
214        lmt_spindle_state.rope_cache = rope->next;
215    } else {
216        rope = (spindle_rope *) lmt_memory_malloc(sizeof(spindle_rope));
217        ++lmt_spindle_state.rope_count;
218        if (rope) {
219            rope->next = NULL;
220        } else {
221            tex_overflow_error("spindle", sizeof(spindle_rope));
222        }
223    }
224    return rope;
225}
226
227static inline void texlib_aux_dispose_rope(spindle_rope *rope)
228{
229    if (rope) {
230        if (lmt_spindle_state.rope_count > MAX_ROPE_CACHE) {
231            lmt_memory_free(rope);
232            --lmt_spindle_state.rope_count;
233        } else {
234            rope->next = lmt_spindle_state.rope_cache;
235            lmt_spindle_state.rope_cache = rope;
236        }
237    }
238}
239
240static void texlib_aux_initialize(void)
241{
242    lmt_spindle_state.spindles = lmt_memory_malloc(INITIAL_SIZE * sizeof(spindle));
243    if (lmt_spindle_state.spindles) {
244        for (int i = 0; i < INITIAL_SIZE; i++) {
245            texlib_aux_reset_spindle(i);
246        }
247        lmt_spindle_state.spindle_index = 0;
248        lmt_spindle_state.spindle_size = INITIAL_SIZE;
249    } else {
250        tex_overflow_error("spindle", sizeof(spindle));
251    }
252}
253
254/*tex
255    We could convert strings into tokenlists here but conceptually the split is cleaner.
256*/
257
258static int texlib_aux_store(lua_State *L, int i, int partial, int cattable, int append)
259{
260    size_t tsize = 0;
261    spindle_rope *rope = NULL;
262    unsigned char kind = unset_lua_input;
263    spindle_data data = { .h = 0 };
264    switch (lua_type(L, i)) {
265        case LUA_TNUMBER:
266        case LUA_TSTRING:
267            {
268                const char *sttemp = lua_tolstring(L, i, &tsize);
269                if (! partial) {
270                    while (tsize > 0 && sttemp[tsize-1] == ' ') {
271                        tsize--;
272                    }
273                }
274                if (tsize > PACKED_SIZE) {
275                    data.t = lmt_memory_malloc(tsize + 1);
276                    kind = string_lua_input;
277                    if (data.t) {
278                        memcpy(data.t, sttemp, (tsize + 1));
279                        break;
280                    } else {
281                        return 0;
282                    }
283                } else {
284                    /*tex
285                        We could append to a previous but partial interferes and in practice it then
286                        never can be done. How often do we print char by char?
287                    */
288                 // (void) append;
289                    if (append && write_spindle.tail && partial && partial == write_spindle.tail->partial) {
290                        if (write_spindle.tail->kind == packed_lua_input && write_spindle.tail->cattable == cattable) {
291                            size_t s = write_spindle.tail->tsize;
292                            if (tsize + s <= PACKED_SIZE) {
293                                for (unsigned i = 0; i < tsize; i++) {
294                                      write_spindle.tail->data.c[s++] = (unsigned char) sttemp[i];
295                                }
296                                write_spindle.tail->tsize += (unsigned int) tsize;
297                             /* lmt_token_state.luacstrings++; */ /* already set */
298                             /* write_spindle.complete = 0;    */ /* already set */
299                                return 1;
300                            }
301                        }
302                    }
303                    for (unsigned int i = 0; i < tsize; i++) {
304                        /*tex When we end up here we often don't have that many bytes. */
305                        data.c[i] = (unsigned char) sttemp[i];
306                    }
307                    kind = packed_lua_input;
308                }
309            }
310            break;
311        case LUA_TBOOLEAN:
312            {
313                /*tex We know that we are below |PACKED_SIZE|. */
314                char *sttemp;
315                if (lua_toboolean(L, i)) {
316                    sttemp = "true";
317                    tsize = 4;
318                } else {
319                    sttemp = "false";
320                    tsize = 5;
321                }
322                if (append && write_spindle.tail && partial && partial == write_spindle.tail->partial) {
323                    if (write_spindle.tail->kind == packed_lua_input && write_spindle.tail->cattable == cattable) {
324                        size_t s = write_spindle.tail->tsize;
325                        if (tsize + s <= PACKED_SIZE) {
326                            for (unsigned i = 0; i < tsize; i++) {
327                                    write_spindle.tail->data.c[s++] = (unsigned char) sttemp[i];
328                            }
329                            write_spindle.tail->tsize += (unsigned int) tsize;
330                            return 1;
331                        }
332                    }
333                }
334                for (unsigned int i = 0; i < tsize; i++) {
335                    /*tex When we end up here we often don't have that many bytes. */
336                    data.c[i] = (unsigned char) sttemp[i];
337                }
338                kind = packed_lua_input;
339            }
340            break;
341        case LUA_TUSERDATA:
342            {
343                void *p = lua_touserdata(L, i);
344                if (p && lua_getmetatable(L, i)) {
345                    lua_get_metatablelua(token_instance);
346                    if (lua_rawequal(L, -1, -2)) {
347                        halfword token = (*((lua_token *) p)).token;
348                        if (token_link(token)) {
349                            /*tex
350                                We cannnot pass a list unless we copy it, alternatively we can bump the ref count
351                                but a quick test didn't work out that well.
352                            */
353                            token = token_link(token);
354                            if (token) {
355                                /*tex
356                                    We're now past the ref count head. Like below we could actually append to a
357                                    current rope but so far we seldom end up in here. Maybe I'll do add that later.
358                                */
359                                halfword t = null;
360                                kind = token_list_lua_input;
361                                data.h = tex_copy_token_list(token, &t);
362                                tsize = t;
363                            } else {
364                                lua_pop(L, 2);
365                                return 0;
366                            }
367                        } else {
368                            /*tex
369                                This is a little (mostly memory) optimization. We use token list instead of adding
370                                single token ropes. That way we also need to push less back into the input. We
371                                check for partial. This optimization is experimental and might go.
372                            */
373                            if (write_spindle.tail && write_spindle.tail->partial && partial == write_spindle.tail->partial) {
374                                switch (write_spindle.tail->kind) {
375                                    case token_lua_input:
376                                        /*tex
377                                            Okay, we now allocate a token but still pushing into the input later has
378                                            less (nesting) overhead then because we process a sequence.
379                                        */
380                                        write_spindle.tail->kind = token_list_lua_input;
381                                        write_spindle.tail->data.h = tex_store_new_token(null, write_spindle.tail->data.h);
382                                        write_spindle.tail->tail = tex_store_new_token(write_spindle.tail->data.h, token_info(token));
383                                        break;
384                                    case token_list_lua_input:
385                                        /*tex
386                                            Normally we have short lists but it still pays off to store the tail
387                                            in |tsize| instead of locating the tail each time.
388                                        */
389                                        write_spindle.tail->tail = tex_store_new_token(write_spindle.tail->tail, token_info(token));
390                                        break;
391                                    default:
392                                        goto SINGLE;
393                                }
394                                lmt_token_state.luacstrings++; /* already set */
395                                write_spindle.complete = 0; /* already set */
396                                lua_pop(L, 2);
397                                return 1;
398                            }
399                            /*tex The non-optimized case: */
400                          SINGLE:
401                            kind = token_lua_input;
402                            data.h = token_info(token);
403                        }
404                        lua_pop(L, 2);
405                    } else {
406                        lua_get_metatablelua(node_instance);
407                        if (lua_rawequal(L, -1, -3)) {
408                            kind = node_lua_input;
409                            data.h = *((halfword *) p);
410                            lua_pop(L, 3);
411                        } else {
412                            lua_pop(L, 3);
413                            return 0;
414                        }
415                    }
416                } else {
417                    return 0;
418                }
419            }
420            break;
421        default:
422            return 0;
423    }
424    lmt_token_state.luacstrings++;
425    rope = texlib_aux_new_rope();
426    /* set */
427    rope->tsize = (unsigned) tsize;
428    rope->next = NULL;
429    rope->kind = kind;
430    rope->partial = (unsigned char) partial;
431    rope->cattable = (short) cattable;
432    rope->data = data;
433    /* add */
434    if (write_spindle.head) {
435        write_spindle.tail->next = rope;
436    } else {
437        write_spindle.head = rope;
438    }
439    write_spindle.tail = rope;
440    write_spindle.complete = 0;
441    return 1;
442}
443
444static void texlib_aux_store_token(halfword token, int partial, int cattable)
445{
446    spindle_rope *rope = texlib_aux_new_rope();
447    /* set */
448    rope->tsize = 0;
449    rope->next = NULL;
450    rope->kind = token_lua_input;
451    rope->partial = (unsigned char) partial;
452    rope->cattable = (short) cattable;
453    rope->data.h = token;
454    /* add */
455    if (write_spindle.head) {
456        write_spindle.tail->next = rope;
457    } else {
458        write_spindle.head = rope;
459    }
460    write_spindle.tail = rope;
461    write_spindle.complete = 0;
462    lmt_token_state.luacstrings++;
463}
464
465static void lmx_aux_store_string(char *str, int len, int cattable)
466{
467    spindle_rope *rope = texlib_aux_new_rope();
468    rope->data.h = 0; /* wipes */
469    if (len > PACKED_SIZE) {
470        rope->data.t = lmt_memory_malloc((size_t) len + 1);
471        if (rope->data.t) {
472            memcpy(rope->data.t, str, (size_t) len + 1);
473        } else {
474            len = 0;
475        }
476        rope->kind = string_lua_input;
477    } else {
478        for (int i = 0; i < len; i++) {
479            /* when we end up here we often don't have that many bytes */
480            rope->data.c[i] = (unsigned char) str[i];
481        }
482        rope->kind = packed_lua_input;
483    }
484    /* set */
485    rope->tsize = (unsigned) len;
486    rope->next = NULL;
487    rope->partial = full_line_mode,
488    rope->cattable = (unsigned char) cattable;
489    /* add */
490    if (write_spindle.head) {
491        write_spindle.tail->next = rope;
492    } else {
493        write_spindle.head = rope;
494    }
495    write_spindle.tail = rope;
496    write_spindle.complete = 0;
497    lmt_token_state.luacstrings++;
498}
499
500static int texlib_aux_cprint(lua_State *L, int partial, int cattable, int startstrings)
501{
502    int top = lua_gettop(L);
503    int type = lua_type(L, startstrings);
504    if (top > startstrings && cattable != no_catcode_table_preset && type == LUA_TNUMBER) {
505        cattable = lmt_tointeger(L, startstrings);
506        ++startstrings;
507        if (cattable != default_catcode_table_preset && cattable != no_catcode_table_preset && ! tex_valid_catcode_table(cattable)) {
508            cattable = default_catcode_table_preset;
509        }
510        type = lua_type(L, startstrings);
511    }
512    if (type == LUA_TTABLE) {
513        for (int i = 1;; i++) {
514            lua_rawgeti(L, startstrings, i);
515            if (texlib_aux_store(L, -1, partial, cattable, i > 1)) {
516                lua_pop(L, 1);
517            } else {
518                lua_pop(L, 1);
519                break;
520            }
521        }
522    } else {
523        for (int i = startstrings; i <= top; i++) {
524            texlib_aux_store(L, i, partial, cattable, i > startstrings);
525        }
526    }
527    return 0;
528}
529
530/*tex
531    We now feed back to be tokenized input from the \TEX\ end into the same handler as we use for
532    \LUA. It saves code but more important is that we no longer need the pseudo files and lines
533    that are kind of inefficient and depend on variable nodes.
534*/
535
536void lmt_cstring_store(char *str, int len, int cattable)
537{
538    lmx_aux_store_string(str, len, cattable);
539}
540
541void lmt_tstring_store(strnumber s, int cattable)
542{
543    lmx_aux_store_string((char *) str_string(s), (int) str_length(s), cattable);
544}
545
546/*tex
547    This is a bit of a dirty trick, needed for an experiment and it's fast enough for our purpose.
548*/
549
550void lmt_cstring_print(int cattable, const char *s, int ispartial)
551{
552    lua_State *L = lmt_lua_state.lua_instance;
553    int top = lua_gettop(L);
554    lua_settop(L, 0);
555    lua_pushinteger(L, cattable);
556    lua_pushstring(L, s);
557    texlib_aux_cprint(L, ispartial ? partial_line_mode : full_line_mode, default_catcode_table_preset, 1);
558    lua_settop(L, top);
559}
560
561/* lua.write */
562
563static int texlib_write(lua_State *L)
564{
565    return texlib_aux_cprint(L, full_line_mode, no_catcode_table_preset, 1);
566}
567
568/* lua.print */
569
570static int texlib_print(lua_State *L)
571{
572    return texlib_aux_cprint(L, full_line_mode, default_catcode_table_preset, 1);
573}
574
575/* lua.sprint */
576
577static int texlib_sprint(lua_State *L)
578{
579    return texlib_aux_cprint(L, partial_line_mode, default_catcode_table_preset, 1);
580}
581
582static int texlib_mprint(lua_State *L)
583{
584    int ini = 1;
585    if (tracing_nesting_par > 2) {
586        tex_local_control_message("entering local control via (run) macro");
587    }
588    texlib_aux_store_token(token_val(end_local_cmd, 0), partial_line_mode, default_catcode_table_preset);
589    if (lmt_token_state.luacstrings > 0) {
590        tex_lua_string_start();
591    }
592    if (lua_type(L, 1) == LUA_TSTRING) {
593        size_t lname = 0;
594        const char *name = lua_tolstring(L, 1, &lname);
595        int cs = tex_string_locate_only(name, lname);
596        int cmd = eq_type(cs);
597        if (is_call_cmd(cmd)) {
598            texlib_aux_store_token(cs_token_flag + cs, partial_line_mode, default_catcode_table_preset);
599            ++ini;
600        } else {
601            tex_local_control_message("invalid (mprint) macro");
602        }
603    }
604    if (lua_gettop(L) >= ini) {
605        texlib_aux_cprint(L, partial_line_mode, default_catcode_table_preset, ini);
606    }
607    if (tracing_nesting_par > 2) {
608        tex_local_control_message("entering local control via mprint");
609    }
610    tex_local_control(1);
611// tex_cleanup_input_state();
612    return 0;
613}
614
615/* we default to obeymode */
616
617static int texlib_pushlocal(lua_State *L)
618{
619    (void) L;
620    if (tracing_nesting_par > 2) {
621        tex_local_control_message("pushing local control");
622    }
623    texlib_aux_store_token(token_val(end_local_cmd, 0), partial_line_mode, default_catcode_table_preset);
624    if (lmt_token_state.luacstrings > 0) {
625        tex_lua_string_start();
626    }
627    return 0;
628}
629
630static int texlib_poplocal(lua_State *L)
631{
632    (void) L;
633    if (tracing_nesting_par > 2) {
634        tex_local_control_message("entering local control via pop");
635    }
636    tex_local_control(1);
637// tex_cleanup_input_state();
638    return 0;
639}
640
641/* lua.cprint */
642
643static int texlib_cprint(lua_State *L)
644{
645    /*tex
646        We map a catcode to a pseudo cattable. So a negative value is a specific catcode with offset 1.
647    */
648    int cattable = lmt_tointeger(L, 1);
649    if (cattable < 0 || cattable > 15) {
650        cattable = - 12 - 0xFF ;
651    } else {
652        cattable = - cattable - 0xFF;
653    }
654    if (lua_type(L, 2) == LUA_TTABLE) {
655        for (int i = 1; ; i++) {
656            lua_rawgeti(L, 2, i);
657            if (texlib_aux_store(L, -1, partial_line_mode, cattable, i > 1)) {
658                lua_pop(L, 1);
659            } else {
660                lua_pop(L, 1);
661                break;
662            }
663        }
664    } else {
665        int n = lua_gettop(L);
666        for (int i = 2; i <= n; i++) {
667            texlib_aux_store(L, i, partial_line_mode, cattable, i > 2);
668        }
669    }
670    return 0;
671}
672
673/* lua.tprint */
674
675static int texlib_tprint(lua_State *L)
676{
677    int n = lua_gettop(L);
678    for (int i = 1; i <= n; i++) {
679        int cattable = default_catcode_table_preset;
680        int startstrings = 1;
681        if (lua_type(L, i) == LUA_TTABLE) {
682            lua_pushvalue(L, i); /* the table */
683            lua_rawgeti(L, -2, 1);
684            if (lua_type(L, -1) == LUA_TNUMBER) {
685                cattable = lmt_tointeger(L, -1);
686                startstrings = 2;
687                if (cattable != default_catcode_table_preset && cattable != no_catcode_table_preset && ! tex_valid_catcode_table(cattable)) {
688                    cattable = default_catcode_table_preset;
689                }
690            }
691            lua_pop(L, 1);
692            for (int j = startstrings; ; j++) {
693                lua_rawgeti(L, -2, j);
694                if (texlib_aux_store(L, -1, partial_line_mode, cattable, j > startstrings)) {
695                    lua_pop(L, 1);
696                } else {
697                    lua_pop(L, 1);
698                    break;
699                }
700            }
701            lua_pop(L, 1); /* the table */
702        } else {
703            /*tex We silently ignore other types. */
704        }
705    }
706    return 0;
707}
708
709static int texlib_isprintable(lua_State* L)
710{
711    halfword okay = 0;
712    switch (lua_type(L, 1)) {
713        case LUA_TSTRING:
714     // case LUA_TNUMBER:
715            okay = 1;
716            break;
717        case LUA_TUSERDATA:
718            if (lua_getmetatable(L, 1)) {
719                lua_get_metatablelua(token_instance);
720                if (lua_rawequal(L, -1, -2)) {
721                    okay = 1;
722                    // lua_pop(L, 2);
723                } else {
724                    lua_get_metatablelua(node_instance);
725                    if (lua_rawequal(L, -1, -3)) {
726                        okay = 1;
727                    }
728                    // lua_pop(L, 3);
729                }
730            }
731            break;
732    }
733    lua_pushboolean(L, okay);
734    return 1;
735}
736
737/*tex We actually don't need to copy and could read from the string. */
738
739int lmt_cstring_input(halfword *result, int *cattable, int *partial, int *finalline)
740{
741    spindle_rope *rope = read_spindle.head;
742    int type = eof_tex_input ;
743    if (! read_spindle.complete) {
744        read_spindle.complete = 1;
745        read_spindle.tail = NULL;
746    }
747    if (rope) {
748        switch (rope->kind) {
749            case string_lua_input:
750                {
751                    if (rope->data.t) {
752                        /*tex put that thing in the buffer */
753                        int strsize = (int) rope->tsize;
754                        int newlast = lmt_fileio_state.io_first + strsize;
755                        lmt_fileio_state.io_last = lmt_fileio_state.io_first;
756                        if (tex_room_in_buffer(newlast)) {
757                            memcpy(&lmt_fileio_state.io_buffer[lmt_fileio_state.io_last], &rope->data.t[0], sizeof(unsigned char) * strsize);
758                            lmt_fileio_state.io_last = newlast;
759                            lmt_memory_free(rope->data.t);
760                            rope->data.t = NULL;
761                        } else {
762                            return type;
763                        }
764                    }
765                    *cattable = rope->cattable;
766                    *partial = rope->partial;
767                    *finalline = (rope->next == NULL);
768                    type = string_tex_input;
769                    break;
770                }
771            case packed_lua_input:
772                {
773                    unsigned strsize = rope->tsize;
774                    int newlast = lmt_fileio_state.io_first + strsize;
775                    lmt_fileio_state.io_last = lmt_fileio_state.io_first;
776                    if (tex_room_in_buffer(newlast)) {
777                        memcpy(&lmt_fileio_state.io_buffer[lmt_fileio_state.io_last], &rope->data.c[0], sizeof(unsigned char) * strsize);
778                     // for (unsigned i = 0; i < strsize; i++) {
779                     //     /* when we end up here we often don't have that many bytes */
780                     //     lmt_fileio_state.io_buffer[lmt_fileio_state.io_last + i] = rope->data.c[i];
781                     // }
782                        lmt_fileio_state.io_last = newlast;
783                        *cattable = rope->cattable;
784                        *partial = rope->partial;
785                        *finalline = (rope->next == NULL);
786                        type = string_tex_input;
787                    } else {
788                        return type;
789                    }
790                    break;
791                }
792            case token_lua_input:
793                {
794                    *result = rope->data.h;
795                    type = token_tex_input;
796                    break;
797                }
798            case token_list_lua_input:
799                {
800                    *result = rope->data.h;
801                    type = token_list_tex_input;
802                    break;
803                }
804            case node_lua_input:
805                {
806                    *result = rope->data.h;
807                    type = node_tex_input;
808                    break;
809                }
810        }
811        texlib_aux_dispose_rope(read_spindle.tail);
812        read_spindle.tail = rope;
813        read_spindle.head = rope->next;
814    } else {
815        texlib_aux_dispose_rope(read_spindle.tail);
816        read_spindle.tail = NULL;
817    }
818    return type;
819}
820
821/*tex Open for reading, and make a new one for writing. */
822
823void lmt_cstring_start(void)
824{
825    lmt_spindle_state.spindle_index++;
826    if (lmt_spindle_state.spindle_size == lmt_spindle_state.spindle_index) {
827        int size = (lmt_spindle_state.spindle_size + 1) * sizeof(spindle);
828        spindle *spindles = lmt_memory_realloc(lmt_spindle_state.spindles, (size_t) size);
829        if (spindles) {
830            lmt_spindle_state.spindles = spindles;
831            texlib_aux_reset_spindle(lmt_spindle_state.spindle_index);
832            lmt_spindle_state.spindle_size++;
833        } else {
834            tex_overflow_error("spindle", size);
835        }
836    }
837}
838
839/*tex Close for reading. */
840
841void lmt_cstring_close(void)
842{
843    spindle_rope *rope;
844    spindle_rope *next = read_spindle.head;
845    while (next) {
846        if (next->kind == string_tex_input && next->data.t) {
847            lmt_memory_free(next->data.t);
848            next->data.t = NULL;
849        }
850        rope = next;
851        next = next->next;
852        if (rope == read_spindle.tail) {
853            read_spindle.tail = NULL;
854        }
855        texlib_aux_dispose_rope(rope);
856    }
857    read_spindle.head = NULL;
858    texlib_aux_dispose_rope(read_spindle.tail);
859    read_spindle.tail = NULL;
860    read_spindle.complete = 0;
861    lmt_spindle_state.spindle_index--;
862}
863
864/*tex
865    The original was close to the \TEX\ original (even using |cur_val|) but there is no need to have
866    that all-in-one loop with radix magic.
867*/
868
869static const char *texlib_aux_scan_integer_part(lua_State *L, const char *ss, int *ret, int *radix_ret)
870{
871    int negative = 0;     /*tex should the answer be negated? */
872    int vacuous = 1;      /*tex have no digits appeared? */
873    int overflow = 0;
874    int chr = 0;          /*tex the current character */
875    const char *str = ss; /*tex where we stopped in the string |ss| */
876    long long result = 0; /*tex return value */
877    while (1) {
878        chr = *str++;
879        switch (chr) {
880            case ' ':
881            case '+':
882                break;
883            case '-':
884                negative = ! negative;
885                break;
886            case '\'':
887                {
888                    int digit;
889                    *radix_ret = 8;
890                    chr = *str++;
891                    while (chr) {
892                        if ((chr >= '0') && (chr <= '0' + 7)) {
893                            digit = chr - '0';
894                        } else {
895                            break;
896                        }
897                        if (! overflow) {
898                            vacuous = 0;
899                            result = result * 8 + digit;
900                            if (result > max_integer) {
901                                overflow = 1;
902                            }
903                        }
904                        chr = *str++;
905                    }
906                    goto DONE;
907                }
908            case '"':
909                {
910                    int digit;
911                    *radix_ret = 16;
912                    chr = *str++;
913                    while (chr) {
914                        if ((chr >= '0') && (chr <= '0' + 9)) {
915                            digit = chr - '0';
916                        } else if ((chr <= 'A' + 5) && (chr >= 'A')) {
917                            digit = chr - 'A' + 10;
918                        } else if ((chr <= 'a' + 5) && (chr >= 'a')) {
919                            /*tex Actually \TEX\ only handles uppercase. */
920                            digit = chr - 'a' + 10;
921                        } else {
922                            goto DONE;
923                        }
924                        if (! overflow) {
925                            vacuous = 0;
926                            result = result * 16 + digit;
927                            if (result > max_integer) {
928                                overflow = 1;
929                            }
930                        }
931                        chr = *str++;
932                    }
933                    goto DONE;
934                }
935            default:
936                {
937                    int digit;
938                    *radix_ret = 10;
939                    while (chr) {
940                        if ((chr >= '0') && (chr <= '0' + 9)) {
941                            digit = chr - '0';
942                        } else {
943                            goto DONE;
944                        }
945                        if (! overflow) {
946                            vacuous = 0;
947                            result = result * 10 + digit;
948                            if (result > max_integer) {
949                                overflow = 1;
950                            }
951                        }
952                        chr = *str++;
953                    }
954                    goto DONE;
955                }
956        }
957    }
958  DONE:
959    if (overflow) {
960        luaL_error(L, "number too big");
961        result = max_integer;
962    } else if (vacuous) {
963        luaL_error(L, "missing number, treated as zero") ;
964    }
965    if (negative) {
966        result = -result;
967    }
968    *ret = (int) result;
969    if (chr != ' ' && str > ss) {
970        str--;
971    }
972    return str;
973}
974
975/*tex
976    This sets |cur_val| to a dimension. We can clean this up a bit like the normal dimen scanner,
977    but it's seldom called. Scanning is like in \TEX, with gobbling spaces and such. When no unit
978    is given we assume points. When nothing is given we assume zero. Trailing crap is just ignored.
979*/
980
981# define unit_hashes(a,b,c,d) \
982         unit_parameter_hash(a,c): \
983    case unit_parameter_hash(b,d): \
984    case unit_parameter_hash(a,d): \
985    case unit_parameter_hash(b,c)
986
987static const char *texlib_aux_scan_dimension_part(lua_State * L, const char *ss, int *ret)
988{
989    int negative = 0;
990    int fraction = 0;
991    int numerator;
992    int denominator;
993    scaled special;
994    int result = 0;
995    int radix = 0;
996    int remainder = 0;
997    int saved_error = lmt_scanner_state.arithmic_error;
998    const char *str = NULL;
999    if (ss && (*ss == '.' || *ss == ',')) {
1000        str = ss;
1001        goto FRACTION;
1002    } else {
1003        str = texlib_aux_scan_integer_part(L, ss, &result, &radix);
1004    }
1005    if (! (char) *str) {
1006        /*tex
1007            It is an error to have no unit, but instead of messaging that we just assume that
1008            scaled points are wanted.
1009        */
1010        goto ATTACH_FRACTION;
1011    }
1012    if (result < 0) {
1013        negative = ! negative;
1014        result = -result;
1015    }
1016  FRACTION:
1017    if ((radix == 0 || radix == 10) && (*str == '.' || *str == ',')) {
1018        unsigned k = 0;
1019        unsigned char digits[18];
1020        str++;
1021        while (1) {
1022            unsigned char chr = *str++;
1023            if ((chr > '0' + 9) || (chr < '0')) {
1024                break;
1025            } else if (k < 17) {
1026                digits[k++] = (unsigned char) chr - '0';
1027            }
1028        }
1029        fraction = tex_round_decimals_digits(digits, k);
1030        if (*str != ' ') {
1031            --str;
1032        }
1033    }
1034    /*tex
1035        The unit can have spaces in front that we skip.
1036    */
1037    while ((char) *str == ' ') {
1038        str++;
1039    }
1040    /*tex
1041        We dropped the |nd| and |nc| units as well as the |true| prefix. We could use a similar
1042        switch as in the normal scsanner but this one is not really used, so ...
1043    */
1044    if (str[0] && str[1]) {
1045        int index = unit_parameter_index(str[0], str[1]);
1046        if (index >= 0) {
1047            switch (index) {
1048                case unit_hashes('p','P','t','T'):
1049                    str += 2;
1050                    goto ATTACH_FRACTION;
1051                case unit_hashes('c','C','m','M'):
1052                    str += 2;
1053                    numerator = 7227;
1054                    denominator = 254;
1055                    goto CONVERSION;
1056                case unit_hashes('m','M','m','M'):
1057                    str += 2;
1058                    numerator = 7227;
1059                    denominator = 2540;
1060                    goto CONVERSION;
1061                case unit_hashes('e','E','m','M'):
1062                    str += 2;
1063                    special = tex_get_font_em_width(cur_font_par);
1064                    goto SPECIAL;
1065                case unit_hashes('e','E','x','X'):
1066                    str += 2;
1067                    special = tex_get_font_ex_height(cur_font_par);
1068                    goto SPECIAL;
1069                case unit_hashes('s','S','p','P'):
1070                    str += 2;
1071                    goto DONE;
1072                case unit_hashes('b','B','p','P'):
1073                    str += 2;
1074                    numerator = 7227;
1075                    denominator = 7200;
1076                    goto CONVERSION;
1077                case unit_hashes('t','T','s','S'):
1078                    str += 2;
1079                    numerator = 4588;
1080                    denominator = 645;
1081                    goto CONVERSION;
1082                case unit_hashes('e','E','s','S'):
1083                    str += 2;
1084                    numerator = 9176;
1085                    denominator = 129;
1086                    goto CONVERSION;
1087                case unit_hashes('e','E','u','U'):
1088                    str += 2;
1089                    numerator = 9176 * eu_factor_par;
1090                    denominator = 129 * 10;
1091                    goto CONVERSION;
1092                case unit_hashes('d','D','k','K'): /* number: 422042 */
1093                    str += 2;
1094                    numerator = 49838;
1095                    denominator = 7739;
1096                    goto CONVERSION;
1097                case unit_hashes('m','M','u','U'):
1098                    str += 2;
1099                    goto ATTACH_FRACTION;
1100                case unit_hashes('d','D','d','D'):
1101                    str += 2;
1102                    numerator = 1238;
1103                    denominator = 1157;
1104                    goto CONVERSION;
1105                case unit_hashes('c','C','c','C'):
1106                    str += 2;
1107                    numerator = 14856;
1108                    denominator = 1157;
1109                    goto CONVERSION;
1110                case unit_hashes('p','P','c','C'):
1111                    str += 2;
1112                    numerator = 12;
1113                    denominator = 1;
1114                    goto CONVERSION;
1115                case unit_hashes('p','P','x','X'):
1116                    str += 2;
1117                    special = px_dimension_par;
1118                    goto SPECIAL;
1119                case unit_hashes('i','I','n','N'):
1120                    str += 2;
1121                    numerator = 7227;
1122                    denominator = 100;
1123                    goto CONVERSION;
1124                default:
1125                    if (tex_get_userunit(index, &special)) {
1126                        str += 2;
1127                        goto SPECIAL;
1128                    }
1129                    {
1130                        halfword cs = unit_parameter(index);
1131                        if (cs > 0) {
1132                            halfword cmd = eq_type(cs);
1133                            halfword chr = eq_value(cs);
1134                            switch (cmd) {
1135                                case internal_dimension_cmd:
1136                                case register_dimension_cmd:
1137                                    str += 2;
1138                                    special = eq_value(chr);
1139                                    goto SPECIAL;
1140                                case dimension_cmd:
1141                                    str += 2;
1142                                    special = chr;
1143                                    goto SPECIAL;
1144                            }
1145                        }
1146                    }
1147            }
1148        }
1149    }
1150    goto ATTACH_FRACTION;
1151  SPECIAL:
1152    result = tex_nx_plus_y(result, special, tex_xn_over_d_r(special, fraction, 0x10000, &remainder));
1153    goto DONE;
1154  CONVERSION:
1155    result = tex_xn_over_d_r(result, numerator, denominator, &remainder);
1156    fraction = (numerator * fraction + 0x10000 * remainder) / denominator;
1157    result = result + (fraction / 0x10000);
1158    fraction = fraction % 0x10000;
1159  ATTACH_FRACTION:
1160    if (result >= 0x4000) {
1161        lmt_scanner_state.arithmic_error = 1;
1162    } else {
1163        result = result * 0x10000 + fraction;
1164    }
1165  DONE:
1166    if (lmt_scanner_state.arithmic_error || (abs(result) >= 0x40000000)) {
1167        result = max_dimension;
1168        luaL_error(L, "dimension too large");
1169    }
1170    *ret = negative ? - result : result;
1171    lmt_scanner_state.arithmic_error = saved_error;
1172    /* only when we want to report junk */
1173    while ((char) *str == ' ') {
1174        str++;
1175    }
1176    return str;
1177}
1178
1179static int texlib_aux_dimension_to_number(lua_State *L, const char *s)
1180{
1181    int result = 0;
1182    const char *d = texlib_aux_scan_dimension_part(L, s, &result);
1183    if (*d) {
1184        return luaL_error(L, "conversion failed (trailing junk?)");
1185    } else {
1186        return result;
1187    }
1188}
1189
1190static int texlib_aux_integer_to_number(lua_State *L, const char *s)
1191{
1192    int result = 0;
1193    int radix = 10;
1194    const char *d = texlib_aux_scan_integer_part(L, s, &result, &radix);
1195    if (*d) {
1196        return luaL_error(L, "conversion failed (trailing junk?)");
1197    } else {
1198        return result;
1199    }
1200}
1201
1202static int texlib_toscaled(lua_State *L)
1203{
1204    int sp;
1205    switch (lua_type(L, 1)) {
1206        case LUA_TNUMBER:
1207            sp = lmt_toroundnumber(L, 1);
1208            break;
1209        case LUA_TSTRING:
1210            sp = texlib_aux_dimension_to_number(L, lua_tostring(L, 1));
1211            break;
1212        default:
1213            return luaL_error(L, "string or a number expected");
1214    }
1215    lua_pushinteger(L, sp);
1216    return 1;
1217}
1218
1219static int texlib_tonumber(lua_State *L)
1220{
1221    int i;
1222    switch (lua_type(L, 1)) {
1223        case LUA_TNUMBER:
1224            i = lmt_toroundnumber(L, 1);
1225            break;
1226        case LUA_TSTRING:
1227            i = texlib_aux_integer_to_number(L, lua_tostring(L, 1));
1228            break;
1229        default:
1230            return luaL_error(L, "string or a number expected");
1231    }
1232    lua_pushinteger(L, i);
1233    return 1;
1234}
1235
1236static int texlib_error(lua_State *L)
1237{
1238    const char *error = luaL_checkstring(L, 1);
1239    const char *help = lua_type(L, 2) == LUA_TSTRING ? luaL_checkstring(L, 2) : NULL;
1240    tex_handle_error(normal_error_type, error, help);
1241    return 0;
1242}
1243
1244/*tex
1245
1246    The lua interface needs some extra functions. The functions themselves are quite boring, but they
1247    are handy because otherwise this internal stuff has to be accessed from \CCODE\ directly, where
1248    lots of the defines are not available.
1249
1250*/
1251
1252static inline int texlib_aux_valid_register_index(lua_State *L, int slot, int cmd, int base, int max, int constant_cmd)
1253{
1254    int index = -1;
1255    switch (lua_type(L, slot)) {
1256        case LUA_TSTRING:
1257            {
1258                size_t len;
1259                const char *str = lua_tolstring(L, 1, &len);
1260                int cs = tex_string_locate_only(str, len);
1261                if (eq_type(cs) == cmd) {
1262                    index = eq_value(cs) - base;
1263                } else if (eq_type(cs) == constant_cmd) {
1264                    return 0xFFFF + cs; // way above max
1265                }
1266            }
1267            break;
1268        case LUA_TNUMBER:
1269            index = lmt_tointeger(L, slot);
1270            break;
1271        default:
1272            luaL_error(L, "string or a number expected");
1273            break;
1274    }
1275    if (index >= 0 && index <= max) {
1276        return index;
1277    } else if (index < (eqtb_size + lmt_hash_state.hash_data.ptr + 1) && eq_type(index) == constant_cmd) {
1278        return index;
1279    } else {
1280        return -1;
1281    }
1282}
1283
1284static int texlib_getregisterindex(lua_State *L)
1285{
1286    size_t len;
1287    const char *str = lua_tolstring(L, 1, &len);
1288    int cs = tex_string_locate_only(str, len);
1289    int index = -1;
1290    switch (eq_type(cs)) {
1291        case register_toks_cmd      : index = eq_value(cs) - register_toks_base;      break;
1292        case register_integer_cmd   : index = eq_value(cs) - register_integer_base;   break;
1293        case register_attribute_cmd : index = eq_value(cs) - register_attribute_base; break;
1294        case register_dimension_cmd : index = eq_value(cs) - register_dimension_base; break;
1295        case register_glue_cmd      : index = eq_value(cs) - register_glue_base;      break;
1296        case register_muglue_cmd    : index = eq_value(cs) - register_muglue_base;    break;
1297    }
1298    if (index >= 0) {
1299        lua_pushinteger(L, index);
1300    } else {
1301        lua_pushnil(L);
1302    }
1303    return 1;
1304}
1305
1306static inline int texlib_aux_checked_register(lua_State *L, int cmd, int base, int max, int constant_cmd)
1307{
1308    int index = texlib_aux_valid_register_index(L, 1, cmd, base, max, constant_cmd);
1309    if (index >= 0) {
1310        lua_pushinteger(L, index);
1311    } else {
1312        lua_pushboolean(L, 0);
1313    }
1314    return 1;
1315}
1316
1317typedef void     (*setfunc) (int, halfword, int, int);
1318typedef halfword (*getfunc) (int, int);
1319
1320/*tex We no longer listen to |\globaldefs| here. */
1321
1322int lmt_check_for_flags(lua_State *L, int slot, int *flags, int prefixes, int numeric)
1323{
1324 // if (global_defs_par) {
1325 //     *flags = add_global_flag(*flags);
1326 // }
1327    if (prefixes) {
1328        while (1) {
1329            switch (lua_type(L, slot)) {
1330                case LUA_TSTRING:
1331                    {
1332                        const char *str = lua_tostring(L, slot);
1333                        if (! str || lua_key_eq(str, macro)) {
1334                            /*tex  For practical reasons we skip empty strings. */
1335                            slot += 1;
1336                        } else if (lua_key_eq(str, global)) {
1337                            slot += 1;
1338                            *flags = add_global_flag(*flags);
1339                        } else if (lua_key_eq(str, frozen)) {
1340                            slot += 1;
1341                            *flags = add_frozen_flag(*flags);
1342                        } else if (lua_key_eq(str, permanent)) {
1343                            slot += 1;
1344                            *flags = add_permanent_flag(*flags);
1345                        } else if (lua_key_eq(str, protected)) {
1346                            slot += 1;
1347                            *flags = add_protected_flag(*flags);
1348                        } else if (lua_key_eq(str, semiprotected)) {
1349                            slot += 1;
1350                            *flags = add_semiprotected_flag(*flags);
1351                        } else if (lua_key_eq(str, untraced)) {
1352                            slot += 1;
1353                            *flags = add_untraced_flag(*flags);
1354                        } else if (lua_key_eq(str, immutable)) {
1355                            slot += 1;
1356                            *flags = add_immutable_flag(*flags);
1357                        } else if (lua_key_eq(str, overloaded)) {
1358                            slot += 1;
1359                            *flags = add_overloaded_flag(*flags);
1360                        } else if (lua_key_eq(str, value)) {
1361                            slot += 1;
1362                            *flags = add_value_flag(*flags);
1363                        } else if (lua_key_eq(str, constant)) {
1364                            slot += 1;
1365                            *flags = add_constant_flag(*flags);
1366                        } else if (lua_key_eq(str, conditional) || lua_key_eq(str, condition)) {
1367                            /* condition will go, conditional stays */
1368                            slot += 1;
1369                            *flags = add_conditional_flag(*flags);
1370                        } else {
1371                            /*tex When we have this at the start we now can have a csname. */
1372                            return slot;
1373                        }
1374                        break;
1375                    }
1376                case LUA_TNUMBER:
1377                    if (numeric) {
1378                        *flags |= lua_tointeger(L, slot);
1379                        slot += 1;
1380                        break;
1381                    } else {
1382                        return slot;
1383                    }
1384                case LUA_TNIL:
1385                    /*tex This is quite convenient if we use some composer. */
1386                    slot += 1;
1387                    break;
1388                default:
1389                    return slot;
1390            }
1391        }
1392    }
1393    return slot;
1394}
1395
1396int lmt_check_for_level(lua_State *L, int slot, quarterword *level, quarterword defaultlevel)
1397{
1398    if (lua_type(L, slot) == LUA_TSTRING) {
1399        const char *str = lua_tostring(L, slot);
1400        *level = lua_key_eq(str, global) ? level_one : defaultlevel;
1401        ++slot;
1402    } else {
1403        *level = defaultlevel;
1404    }
1405    return slot;
1406}
1407
1408/* -1=noindex, 0=register 1=internal */
1409
1410static int texlib_aux_check_for_index(
1411    lua_State  *L,
1412    int         slot,
1413    const char *what,
1414    int        *index,
1415    int         internal_cmd,
1416    int         register_cmd,
1417    int         internal_base,
1418    int         register_base,
1419    int         max_index,
1420    int         constant_cmd
1421) {
1422    *index = -1;
1423    switch (lua_type(L, slot)) {
1424        case LUA_TSTRING:
1425            {
1426                size_t len;
1427                const char *str = lua_tolstring(L, slot, &len);
1428                int cs = tex_string_locate_only(str, len);
1429                if (eq_type(cs) == internal_cmd) {
1430                    *index = eq_value(cs) - internal_base;
1431                    return 1;
1432                } else if (eq_type(cs) == register_cmd) {
1433                    *index = eq_value(cs) - register_base;
1434                    return 0;
1435                } else if (eq_type(cs) == constant_cmd) {
1436                    *index = cs;
1437                    return 2;
1438                } else {
1439                    luaL_error(L, "incorrect %s name", what);
1440                    return -1;
1441                }
1442            }
1443        case LUA_TNUMBER:
1444            *index = lmt_tointeger(L, slot);
1445            if (*index >= 0 && *index <= max_index) {
1446                return 0;
1447            } else {
1448                halfword i = *index - 0xFFFF; /* hm, this is too hard coded */
1449                if (i < (eqtb_size + lmt_hash_state.hash_data.ptr + 1) && eq_type(i) == constant_cmd) {
1450                    *index = i;
1451                    return 2;
1452                }
1453            }
1454            return -1;
1455        case LUA_TUSERDATA:
1456            {
1457                halfword cs = lmt_get_lua_token_cs(L, 1);
1458                if (cs) {
1459                    if (eq_type(cs) == internal_cmd) {
1460                        *index = eq_value(cs) - internal_base;
1461                        return 1;
1462                    } else if (eq_type(cs) == register_cmd) {
1463                        *index = eq_value(cs) - register_base;
1464                        return 0;
1465                    } else if (eq_type(cs) == constant_cmd) {
1466                        *index = cs;
1467                        return 2;
1468                    }
1469                }
1470                luaL_error(L, "incorrect token");
1471                return -1;
1472            }
1473        default:
1474            luaL_error(L, "%s name or valid index expected", what);
1475            return -1;
1476    }
1477}
1478
1479static int texlib_get(lua_State *L);
1480
1481/*tex
1482
1483    We intercept the first string and when it is |global| then we check the second one which can
1484    also be a string. It is unlikely that one will use |\global| as register name so we don't need
1485    to check for the number of further arguments. This permits to treat lack of them as a reset.
1486
1487*/
1488
1489static int texlib_isdimen(lua_State *L)
1490{
1491    return texlib_aux_checked_register(L, register_dimension_cmd, register_dimension_base, max_dimension_register_index, dimension_cmd);
1492}
1493
1494/* [global] name|index integer|dimension|false|nil */
1495
1496static int texlib_setdimen(lua_State *L)
1497{
1498    int flags = 0;
1499    int index = 0;
1500    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1501    int state = texlib_aux_check_for_index(L, slot++, "dimen", &index, internal_dimension_cmd, register_dimension_cmd, internal_dimension_base, register_dimension_base, max_dimension_register_index, dimension_cmd);
1502    if (state >= 0) {
1503        halfword value = 0;
1504        switch (lua_type(L, slot)) {
1505            case LUA_TNUMBER:
1506                value = lmt_toroundnumber(L, slot++);
1507                break;
1508            case LUA_TSTRING:
1509                value = texlib_aux_dimension_to_number(L, lua_tostring(L, slot++));
1510                break;
1511            case LUA_TBOOLEAN:
1512                if (lua_toboolean(L, slot++)) {
1513                    /*tex The value |true| makes no sense. */
1514                    return 0;
1515                }
1516                break;
1517            case LUA_TNONE:
1518            case LUA_TNIL:
1519                break;
1520            default:
1521                luaL_error(L, "unsupported dimen value type");
1522                break;
1523        }
1524        if (state == 2) {
1525            tex_define(flags, index, dimension_cmd, value);
1526        } else {
1527            tex_set_tex_dimension_register(index, value, flags, state);
1528            if (state == 1 && lua_toboolean(L, slot)) {
1529                tex_update_par_par(internal_dimension_cmd, index);
1530            }
1531        }
1532    }
1533    return 0;
1534}
1535
1536static int texlib_getdimen(lua_State *L)
1537{
1538    int index;
1539    int state = texlib_aux_check_for_index(L, 1, "dimen", &index, internal_dimension_cmd, register_dimension_cmd, internal_dimension_base, register_dimension_base, max_dimension_register_index, dimension_cmd);
1540    lua_pushinteger(L, state >= 0 ? (state == 2 ? eq_value(index) : tex_get_tex_dimension_register(index, state)) : 0);
1541 // halfword value;
1542 // switch (state) {
1543 //     case 0 :
1544 //     case 1 :
1545 //         value = dimen_parameter(index);
1546 //         break;
1547 //     case 2 :
1548 //         value = eq_value(index);
1549 //         break;
1550 //     default:
1551 //         value = 0;
1552 //         break;
1553 // }
1554 // lua_pushinteger(L, value);
1555    return 1;
1556}
1557
1558static halfword texlib_aux_make_glue(lua_State *L, int top, int slot)
1559{
1560    halfword value = tex_copy_node(zero_glue);
1561    if (slot <= top) {
1562        glue_amount(value) = lmt_toroundnumber(L, slot++);
1563        if (slot <= top) {
1564            glue_stretch(value) = lmt_toroundnumber(L, slot++);
1565            if (slot <= top) {
1566                glue_shrink(value) = lmt_toroundnumber(L, slot++);
1567                if (slot <= top) {
1568                    glue_stretch_order(value) = tex_checked_glue_order(lmt_tohalfword(L, slot++));
1569                    if (slot <= top) {
1570                        glue_shrink_order(value) = tex_checked_glue_order(lmt_tohalfword(L, slot++));
1571                    }
1572                }
1573            }
1574        }
1575    }
1576    return value;
1577}
1578
1579static inline int texlib_aux_push_glue(lua_State* L, halfword g)
1580{
1581    if (g) {
1582        lua_pushinteger(L, glue_amount(g));
1583        lua_pushinteger(L, glue_stretch(g));
1584        lua_pushinteger(L, glue_shrink(g));
1585        lua_pushinteger(L, glue_stretch_order(g));
1586        lua_pushinteger(L, glue_shrink_order(g));
1587    } else {
1588        lua_pushinteger(L, 0);
1589        lua_pushinteger(L, 0);
1590        lua_pushinteger(L, 0);
1591        lua_pushinteger(L, 0);
1592        lua_pushinteger(L, 0);
1593    }
1594    return 5;
1595}
1596
1597static halfword texlib_aux_get_glue_spec(lua_State *L, int slot)
1598{
1599    halfword value = null;
1600    switch (lua_type(L, slot + 1)) {
1601        case LUA_TBOOLEAN:
1602         // if (lua_toboolean(L, slot + 1)) {
1603         //     /*tex The value |true| makes no sense. */
1604         // }
1605            break;
1606        case LUA_TNIL:
1607        case LUA_TNONE:
1608            break;
1609        default:
1610            value = lmt_check_isnode(L, slot + 1);
1611            if (node_type(value) != glue_spec_node) {
1612                value = null;
1613                luaL_error(L, "glue_spec expected");
1614            }
1615    }
1616    return value;
1617}
1618
1619static int texlib_isskip(lua_State *L)
1620{
1621    return texlib_aux_checked_register(L, register_glue_cmd, register_glue_base, max_glue_register_index, gluespec_cmd);
1622}
1623
1624/* [global] name|index gluespec|false|nil */
1625
1626static int texlib_setskip(lua_State *L)
1627{
1628    int flags = 0;
1629    int index = 0;
1630    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1631    int state = texlib_aux_check_for_index(L, slot++, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index, gluespec_cmd);
1632    if (state >= 0) {
1633        halfword value = texlib_aux_get_glue_spec(L, slot++);
1634        if (state == 2) {
1635            tex_define(flags, index, gluespec_cmd, value);
1636        } else {
1637            tex_set_tex_skip_register(index, value, flags, state);
1638            if (state == 1 && lua_toboolean(L, slot)) {
1639                tex_update_par_par(internal_glue_cmd, index);
1640            }
1641        }
1642    }
1643    return 0;
1644}
1645
1646static int texlib_getskip(lua_State *L)
1647{
1648    int index;
1649    int state = texlib_aux_check_for_index(L, 1, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index, gluespec_cmd);
1650    halfword value = state >= 0 ? (state == 2 ? eq_value(index) : tex_get_tex_skip_register(index, state)) : null;
1651    lmt_push_node_fast(L, tex_copy_node(value ? value : zero_glue));
1652    return 1;
1653}
1654
1655static int texlib_isglue(lua_State *L)
1656{
1657    return texlib_aux_checked_register(L, register_glue_cmd, register_glue_base, max_glue_register_index, gluespec_cmd);
1658}
1659
1660/* [global] slot [width] [stretch] [shrink] [stretch_order] [shrink_order] */
1661
1662static int texlib_setglue(lua_State *L)
1663{
1664    int flags = 0;
1665    int index = 0;
1666    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1667    int state = texlib_aux_check_for_index(L, slot++, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index, gluespec_cmd);
1668    if (state >= 0) {
1669        halfword value = texlib_aux_make_glue(L, lua_gettop(L), slot);
1670        if (state == 2) {
1671            tex_define(flags, index, gluespec_cmd, value);
1672        } else {
1673            tex_set_tex_skip_register(index, value, flags, state);
1674        }
1675    }
1676    return 0;
1677}
1678
1679static int texlib_getglue(lua_State *L)
1680{
1681    int index;
1682    int all = (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : 1;
1683    int state = texlib_aux_check_for_index(L, 1, "skip", &index, internal_glue_cmd, register_glue_cmd, internal_glue_base, register_glue_base, max_glue_register_index, gluespec_cmd);
1684    halfword value = state >= 0 ? (state == 2 ? eq_value(index) : tex_get_tex_skip_register(index, state)) : null;
1685    if (! value) {
1686        lua_pushinteger(L, 0);
1687        if (all) {
1688            /* save the trouble of testing p/m */
1689            lua_pushinteger(L, 0);
1690            lua_pushinteger(L, 0);
1691            return 3;
1692        } else {
1693            return 1;
1694        }
1695    } else if (all) {
1696        return texlib_aux_push_glue(L, value);
1697    } else {
1698        /* false */
1699        lua_pushinteger(L, value ? glue_amount(value) : 0);
1700        return 1;
1701    }
1702}
1703
1704static int texlib_ismuskip(lua_State *L)
1705{
1706    return texlib_aux_checked_register(L, register_muglue_cmd, register_muglue_base, max_muglue_register_index, mugluespec_cmd);
1707}
1708
1709static int texlib_setmuskip(lua_State *L)
1710{
1711    int flags = 0;
1712    int index = 0;
1713    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1714    int state = texlib_aux_check_for_index(L, slot++, "muskip", &index, internal_muglue_cmd, register_muglue_cmd, internal_muglue_base, register_muglue_base, max_muglue_register_index, mugluespec_cmd);
1715    if (state >= 0) {
1716        halfword value =texlib_aux_get_glue_spec(L, slot);
1717        if (state == 2) {
1718            tex_define(flags, index, mugluespec_cmd, value);
1719        } else {
1720            tex_set_tex_muskip_register(index, value, flags, state);
1721        }
1722    }
1723    return 0;
1724}
1725
1726static int texlib_getmuskip(lua_State *L)
1727{
1728    int index;
1729    int state = texlib_aux_check_for_index(L, 1, "muskip", &index, internal_muglue_cmd, register_muglue_cmd, internal_muglue_base, register_muglue_base, max_muglue_register_index, mugluespec_cmd);
1730    halfword value = state >= 0 ? (state == 2 ? eq_value(index) : tex_get_tex_muskip_register(index, state)) : null;
1731    lmt_push_node_fast(L, tex_copy_node(value ? value : zero_glue));
1732    return 1;
1733}
1734
1735static int texlib_ismuglue(lua_State *L)
1736{
1737    return texlib_aux_checked_register(L, register_muglue_cmd, register_muglue_base, max_muglue_register_index, mugluespec_cmd);
1738}
1739
1740static int texlib_setmuglue(lua_State *L)
1741{
1742    int flags = 0;
1743    int index = 0;
1744    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1745    int state = texlib_aux_check_for_index(L, slot++, "muskip", &index, internal_muglue_cmd, register_muglue_cmd, internal_muglue_base, register_muglue_base, max_muglue_register_index, mugluespec_cmd);
1746    if (state >= 0) {
1747        halfword value = texlib_aux_make_glue(L, lua_gettop(L), slot);
1748        if (state == 2) {
1749            tex_define(flags, index, mugluespec_cmd, value);
1750        } else {
1751            tex_set_tex_muskip_register(index, value, flags, state);
1752        }
1753    }
1754    return 0;
1755}
1756
1757static int texlib_getmuglue(lua_State *L)
1758{
1759    int index;
1760    int all = (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : 1;
1761    int state = texlib_aux_check_for_index(L, 1, "muskip", &index, internal_muglue_cmd, register_muglue_cmd, internal_muglue_base, register_muglue_base, max_muglue_register_index, mugluespec_cmd);
1762    halfword value = state >= 0 ? (state == 2 ? eq_value(index) : tex_get_tex_muskip_register(index, state)) : null;
1763    if (! value) {
1764        lua_pushinteger(L, 0);
1765        return 1;
1766    } else if (all) {
1767        return texlib_aux_push_glue(L, value);
1768    } else {
1769        /* false */
1770        lua_pushinteger(L, value ? glue_amount(value) : 0);
1771        return 1;
1772    }
1773}
1774
1775static int texlib_iscount(lua_State *L)
1776{
1777    return texlib_aux_checked_register(L, register_integer_cmd, register_integer_base, max_integer_register_index, integer_cmd);
1778}
1779
1780static int texlib_setcount(lua_State *L)
1781{
1782    int flags = 0;
1783    int index = 0;
1784    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1785    int state = texlib_aux_check_for_index(L, slot++, "count", &index, internal_integer_cmd, register_integer_cmd, internal_integer_base, register_integer_base, max_integer_register_index, integer_cmd);
1786    if (state >= 0) {
1787        halfword value = lmt_optinteger(L, slot++, 0);
1788        if (state == 2) {
1789            tex_define(flags, index, integer_cmd, value);
1790        } else {
1791            tex_set_tex_count_register(index, value, flags, state);
1792            if (state == 1 && lua_toboolean(L, slot)) {
1793                tex_update_par_par(internal_integer_cmd, index);
1794            }
1795        }
1796    }
1797    return 0;
1798}
1799
1800static int texlib_getcount(lua_State *L)
1801{
1802    int index;
1803    int state = texlib_aux_check_for_index(L, 1, "count", &index, internal_integer_cmd, register_integer_cmd, internal_integer_base, register_integer_base, max_integer_register_index, integer_cmd);
1804    lua_pushinteger(L, state >= 0 ? (state == 2 ? eq_value(index) : tex_get_tex_count_register(index, state)) : 0);
1805    return 1;
1806}
1807
1808static int texlib_isfloat(lua_State *L)
1809{
1810    return texlib_aux_checked_register(L, register_posit_cmd, register_posit_base, max_posit_register_index, posit_cmd);
1811}
1812
1813static int texlib_setfloat(lua_State *L)
1814{
1815    int flags = 0;
1816    int index = 0;
1817    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1818    int state = texlib_aux_check_for_index(L, slot++, "float", &index, internal_posit_cmd, register_posit_cmd, internal_posit_base, register_posit_base, max_posit_register_index, posit_cmd);
1819    if (state >= 0) {
1820        halfword value = tex_double_to_posit(luaL_optnumber(L, slot++, 0)).v;
1821        if (state == 2) {
1822            tex_define(flags, index, posit_cmd, value);
1823        } else {
1824            tex_set_tex_count_register(index, value, flags, state);
1825            if (state == 1 && lua_toboolean(L, slot)) {
1826                tex_update_par_par(internal_posit_cmd, index);
1827            }
1828        }
1829    }
1830    return 0;
1831}
1832
1833static int texlib_getfloat(lua_State *L)
1834{
1835    int index;
1836    int state = texlib_aux_check_for_index(L, 1, "float", &index, internal_posit_cmd, register_posit_cmd, internal_posit_base, register_posit_base, max_posit_register_index, posit_cmd);
1837    lua_pushnumber(L, tex_posit_to_double((state >= 0) ? (state == 2 ? eq_value(index) : tex_get_tex_posit_register(index, state)) : 0));
1838    return 1;
1839}
1840
1841static int texlib_isattribute(lua_State *L)
1842{
1843    return texlib_aux_checked_register(L, register_attribute_cmd, register_attribute_base, max_attribute_register_index, -1);
1844}
1845
1846/*tex there are no system set attributes so this is a bit overkill */
1847
1848static int texlib_setattribute(lua_State *L)
1849{
1850    int flags = 0;
1851    int index = 0;
1852    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1853    int state = texlib_aux_check_for_index(L, slot++, "attribute", &index, internal_attribute_cmd, register_attribute_cmd, internal_attribute_base, register_attribute_base, max_attribute_register_index, 0);
1854    if (state >= 0) {
1855        halfword value = lmt_optinteger(L, slot++, unused_attribute_value);
1856        tex_set_tex_attribute_register(index, value, flags, state);
1857    }
1858    return 0;
1859}
1860
1861static int texlib_getattribute(lua_State *L)
1862{
1863    int index;
1864    int state = texlib_aux_check_for_index(L, 1, "attribute", &index, internal_attribute_cmd, register_attribute_cmd, internal_attribute_base, register_attribute_base, max_attribute_register_index, 0);
1865    lua_pushinteger(L, state >= 0 ? tex_get_tex_attribute_register(index, state) : 0);
1866    return 1;
1867}
1868
1869/*tex todo: we can avoid memcpy as there is no need to go through the pool */
1870
1871/* use string_to_toks */
1872
1873static int texlib_istoks(lua_State *L)
1874{
1875    return texlib_aux_checked_register(L, register_toks_cmd, register_toks_base, max_toks_register_index, -1);
1876}
1877
1878/* [global] name|integer string|nil */
1879
1880static int texlib_settoks(lua_State *L)
1881{
1882    int flags = 0;
1883    int index = 0;
1884    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1885    int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base,max_toks_register_index, 0);
1886    if (state >= 0) {
1887        lstring value = { .c = NULL, .l = 0 };
1888        switch (lua_type(L, slot)) {
1889            case LUA_TSTRING:
1890                value.c = lua_tolstring(L, slot, &value.l);
1891                break;
1892            case LUA_TNIL:
1893            case LUA_TNONE:
1894                break;
1895            default:
1896                return luaL_error(L, "string or nil expected");
1897        }
1898        tex_set_tex_toks_register(index, value, flags, state);
1899    }
1900    return 0;
1901}
1902
1903/* [global] name|index catcode string */
1904
1905static int texlib_scantoks(lua_State *L) // TODO
1906{
1907    int index = 0;
1908    int flags = 0;
1909    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
1910    int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base,max_toks_register_index, 0);
1911    if (state >= 0) {
1912        lstring value = { .c = NULL, .l = 0 };
1913        int cattable = lmt_checkinteger(L, slot++);
1914        switch (lua_type(L, slot)) {
1915            case LUA_TSTRING:
1916                value.c = lua_tolstring(L, slot, &value.l);
1917                break;
1918            case LUA_TNIL:
1919            case LUA_TNONE:
1920                break;
1921            default:
1922                return luaL_error(L, "string or nil expected");
1923        }
1924        tex_scan_tex_toks_register(index, cattable, value, flags, state);
1925    }
1926    return 0;
1927}
1928
1929static int texlib_gettoks(lua_State *L)
1930{
1931    int index;
1932    int slot = 1;
1933    int state = texlib_aux_check_for_index(L, slot++, "toks", &index, internal_toks_cmd, register_toks_cmd, internal_toks_base, register_toks_base, max_toks_register_index, 0);
1934    if (state >= 0) {
1935        if (lua_toboolean(L, slot)) {
1936            lmt_token_register_to_lua(L, state ? toks_parameter(index) : toks_register(index), 0);
1937        } else {
1938
1939            strnumber value = tex_get_tex_toks_register(index, state);
1940            lua_pushstring(L, tex_to_cstring(value));
1941            tex_flush_str(value);
1942        }
1943    } else {
1944        lua_pushnil(L);
1945        return 1;
1946    }
1947    return 1;
1948}
1949
1950static int texlib_getmarknames(lua_State *L)
1951{
1952    lua_createtable(L, 5, 1);
1953    lua_push_key_at_index(L, current,     current_marks_code);
1954    lua_push_key_at_index(L, top,         top_marks_code);
1955    lua_push_key_at_index(L, first,       first_marks_code);
1956    lua_push_key_at_index(L, bottom,      bot_marks_code);
1957    lua_push_key_at_index(L, splitfirst,  split_first_marks_code);
1958    lua_push_key_at_index(L, splitbottom, split_bot_marks_code);
1959    return 1;
1960}
1961
1962static int texlib_getmark(lua_State *L)
1963{
1964    if (lua_gettop(L) == 0) {
1965        lua_pushinteger(L, lmt_mark_state.mark_data.ptr);
1966        return 1;
1967    } else {
1968        halfword mark = lmt_get_mark_class(L, 1);
1969        if (mark >= 0) {
1970            halfword index = lmt_opthalfword(L, 2, 0);
1971            if (index >= 0 && index <= lmt_mark_state.mark_data.ptr) {
1972                halfword ptr = tex_get_some_mark(mark, index);
1973                if (ptr) {
1974                    char *str = tex_tokenlist_to_tstring(ptr, 1, NULL, 0, 0, 0, 0, 1); /* single hashes */
1975                    if (str) {
1976                        lua_pushstring(L, str);
1977                    } else {
1978                        lua_pushliteral(L, "");
1979                    }
1980                    return 1;
1981                }
1982            } else {
1983                /*tex 
1984                    An error message is actually counterproductive, but if needed we can 
1985                    have |tex.usedmark(...)|.
1986                */
1987                /* luaL_error(L, "valid mark class expected"); */
1988            }
1989        }
1990    }
1991    lua_pushnil(L);
1992    return 1;
1993}
1994
1995int lmt_get_box_id(lua_State *L, int i, int report)
1996{
1997    int index = -1;
1998    switch (lua_type(L, i)) {
1999        case LUA_TSTRING:
2000            {
2001                size_t len = 0;
2002                const char *str = lua_tolstring(L, i, &len);
2003                int cs = tex_string_locate_only(str, len);
2004                int cmd = eq_type(cs);
2005                switch (cmd) {
2006                    case char_given_cmd:
2007                    case integer_cmd:
2008                        index = eq_value(cs);
2009                        break;
2010                    case register_integer_cmd:
2011                        index = register_integer_number(eq_value(cs));
2012                        break;
2013                    default:
2014                        /* we don't accept other commands as it makes no sense */
2015                        break;
2016                }
2017                break;
2018            }
2019        case LUA_TNUMBER:
2020            index = lmt_tointeger(L, i);
2021        default:
2022            break;
2023    }
2024    if (index >= 0 && index <= max_box_register_index) {
2025        return index;
2026    } else {
2027        if (report) {
2028            luaL_error(L, "string or a number within range expected");
2029        }
2030        return -1;
2031    }
2032}
2033
2034static int texlib_getbox(lua_State *L)
2035{
2036    halfword index = lmt_get_box_id(L, 1, 1);
2037    lmt_node_list_to_lua(L, index >= 0 ? tex_get_tex_box_register(index, 0) : null);
2038    return 1;
2039}
2040
2041static int texlib_splitbox(lua_State *L)
2042{
2043    int index = lmt_get_box_id(L, 1, 1);
2044    if (index >= 0) {
2045        if (lua_isnumber(L, 2)) {
2046            int packing = packing_additional;
2047            switch (lua_type(L, 3)) {
2048                case LUA_TSTRING:
2049                    {
2050                        const char *str = lua_tostring(L, 3);
2051                        if (lua_key_eq(str, exactly)) {
2052                            packing = packing_exactly;
2053                        } else if (lua_key_eq(str, additional)) {
2054                            packing = packing_additional;
2055                        }
2056                        break;
2057                    }
2058                case LUA_TNUMBER:
2059                    {
2060                        packing = lmt_tointeger(L, 3);
2061                        if (packing != packing_exactly && packing != packing_additional) {
2062                            packing = packing_exactly;
2063                            luaL_error(L, "wrong mode in splitbox");
2064                        }
2065                        break;
2066                    }
2067            }
2068            lmt_node_list_to_lua(L, tex_vsplit(index, lmt_toroundnumber(L, 2), packing));
2069        } else {
2070            /* maybe a warning */
2071            lua_pushnil(L);
2072        }
2073    } else {
2074        lua_pushnil(L);
2075    }
2076    return 1;
2077}
2078
2079/* todo */
2080
2081static int texlib_isbox(lua_State *L)
2082{
2083    lua_pushboolean(L, lmt_get_box_id(L, 1, 0) >= 0);
2084    return 1;
2085}
2086
2087static int texlib_setbox(lua_State *L)
2088{
2089    int flags = 0;
2090    int slot = lmt_check_for_flags(L, 1, &flags, 1, 0);
2091    int index = lmt_get_box_id(L, slot++, 1);
2092    if (index >= 0) {
2093        halfword box = null;
2094        switch (lua_type(L, slot)) {
2095            case LUA_TBOOLEAN:
2096                if (lua_toboolean(L, slot)) {
2097                    return 0;
2098                } else {
2099                    box = null;
2100                    break;
2101                }
2102            case LUA_TNIL:
2103            case LUA_TNONE:
2104                break;
2105            default:
2106                box = lmt_node_list_from_lua(L, slot);
2107                if (box) {
2108                    switch (node_type(box)) {
2109                        case hlist_node:
2110                        case vlist_node:
2111                            break;
2112                        default:
2113                            return luaL_error(L, "invalid node type %s passed", get_node_name(node_type(box)));
2114                    }
2115                }
2116                break;
2117        }
2118        tex_set_tex_box_register(index, box, flags, 0);
2119    }
2120    return 0;
2121}
2122
2123/* [global] index first second */
2124
2125static int texlib_setlccode(lua_State *L)
2126{
2127    int top = lua_gettop(L);
2128    if (top >= 2) {
2129        quarterword level;
2130        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2131        int ch1 = lmt_checkinteger(L, slot++);
2132        if (character_in_range(ch1)) {
2133            halfword ch2 = lmt_checkhalfword(L, slot++);
2134            if (character_in_range(ch2)) {
2135                tex_set_lc_code(ch1, ch2, level);
2136                if (slot <= top) {
2137                    halfword ch3 = lmt_checkhalfword(L, slot);
2138                    if (character_in_range(ch3)) {
2139                        tex_set_uc_code(ch1, ch3, level);
2140                    } else {
2141                        texlib_aux_show_character_error(L, ch3);
2142                    }
2143                }
2144            } else {
2145                texlib_aux_show_character_error(L, ch2);
2146            }
2147        } else {
2148            texlib_aux_show_character_error(L, ch1);
2149        }
2150    }
2151    return 0;
2152}
2153
2154static int texlib_setuccode(lua_State *L)
2155{
2156    int top = lua_gettop(L);
2157    if (top >= 2) {
2158        quarterword level;
2159        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2160        int ch1 = lmt_checkinteger(L, slot++);
2161        if (character_in_range(ch1)) {
2162            halfword ch2 = lmt_checkhalfword(L, slot++);
2163            if (character_in_range(ch2)) {
2164                tex_set_uc_code(ch1, ch2, level);
2165                if (slot <= top) {
2166                    halfword ch3 = lmt_checkhalfword(L, slot);
2167                    if (character_in_range(ch3)) {
2168                        tex_set_lc_code(ch1, ch3, level);
2169                    } else {
2170                        texlib_aux_show_character_error(L, ch3);
2171                    }
2172                }
2173            } else {
2174                texlib_aux_show_character_error(L, ch2);
2175            }
2176        } else {
2177            texlib_aux_show_character_error(L, ch1);
2178        }
2179    }
2180    return 0;
2181}
2182
2183static int texlib_setsfcode(lua_State *L)
2184{
2185    int top = lua_gettop(L);
2186    if (top >= 2) {
2187        quarterword level;
2188        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2189        int ch = lmt_checkinteger(L, slot++);
2190        if (character_in_range(ch)) {
2191            halfword val = lmt_checkhalfword(L, slot);
2192            if (half_in_range(val)) {
2193                tex_set_sf_code(ch, val, level);
2194            } else {
2195                texlib_aux_show_half_error(L, val);
2196            }
2197        } else {
2198            texlib_aux_show_character_error(L, ch);
2199        }
2200    }
2201    return 0;
2202}
2203
2204static int texlib_sethccode(lua_State *L)
2205{
2206    int top = lua_gettop(L);
2207    if (top >= 2) {
2208        quarterword level;
2209        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2210        int ch = lmt_checkinteger(L, slot++);
2211        if (character_in_range(ch)) {
2212            halfword val = lmt_checkhalfword(L, slot);
2213            if (half_in_range(val)) {
2214                tex_set_hc_code(ch, val, level);
2215            } else {
2216                texlib_aux_show_half_error(L, val);
2217            }
2218        } else {
2219            texlib_aux_show_character_error(L, ch);
2220        }
2221    }
2222    return 0;
2223}
2224
2225static int texlib_sethmcode(lua_State *L)
2226{
2227    int top = lua_gettop(L);
2228    if (top >= 2) {
2229        quarterword level;
2230        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2231        int ch = lmt_checkinteger(L, slot++);
2232        if (character_in_range(ch)) {
2233            halfword val = lmt_checkhalfword(L, slot);
2234            tex_set_hm_code(ch, val, level);
2235        } else {
2236            texlib_aux_show_character_error(L, ch);
2237        }
2238    }
2239    return 0;
2240}
2241
2242static int texlib_setamcode(lua_State *L)
2243{
2244    int top = lua_gettop(L);
2245    if (top >= 2) {
2246        quarterword level;
2247        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2248        int ch = lmt_checkinteger(L, slot++);
2249        if (character_in_range(ch)) {
2250            halfword val = lmt_checkhalfword(L, slot);
2251            tex_set_am_code(ch, val, level);
2252        } else {
2253            texlib_aux_show_character_error(L, ch);
2254        }
2255    }
2256    return 0;
2257}
2258
2259static int texlib_setcccode(lua_State *L)
2260{
2261    int top = lua_gettop(L);
2262    if (top >= 2) {
2263        quarterword level;
2264        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2265        int ch = lmt_checkinteger(L, slot++);
2266        if (character_in_range(ch)) {
2267            halfword val = lmt_checkquarterword(L, slot);
2268            tex_set_cc_code(ch, val, level);
2269        } else {
2270            texlib_aux_show_character_error(L, ch);
2271        }
2272    }
2273    return 0;
2274}
2275
2276static int texlib_getlccode(lua_State *L)
2277{
2278    int ch = lmt_checkinteger(L, 1);
2279    if (character_in_range(ch)) {
2280        lua_pushinteger(L, tex_get_lc_code(ch));
2281    } else {
2282        texlib_aux_show_character_error(L, ch);
2283        lua_pushinteger(L, 0);
2284    }
2285    return 1;
2286}
2287
2288static int texlib_getuccode(lua_State *L)
2289{
2290    int ch = lmt_checkinteger(L, 1);
2291    if (character_in_range(ch)) {
2292        lua_pushinteger(L, tex_get_uc_code(ch));
2293    } else {
2294        texlib_aux_show_character_error(L, ch);
2295        lua_pushinteger(L, 0);
2296    }
2297    return 1;
2298}
2299
2300static int texlib_getsfcode(lua_State *L)
2301{
2302    int ch = lmt_checkinteger(L, 1);
2303    if (character_in_range(ch)) {
2304        lua_pushinteger(L, tex_get_sf_code(ch));
2305    } else {
2306        texlib_aux_show_character_error(L, ch);
2307        lua_pushinteger(L, 0);
2308    }
2309    return 1;
2310}
2311
2312static int texlib_gethccode(lua_State *L)
2313{
2314    int ch = lmt_checkinteger(L, 1);
2315    if (character_in_range(ch)) {
2316        lua_pushinteger(L, tex_get_hc_code(ch));
2317    } else {
2318        texlib_aux_show_character_error(L, ch);
2319        lua_pushinteger(L, 0);
2320    }
2321    return 1;
2322}
2323
2324static int texlib_gethmcode(lua_State *L)
2325{
2326    int ch = lmt_checkinteger(L, 1);
2327    if (character_in_range(ch)) {
2328        lua_pushinteger(L, tex_get_hm_code(ch));
2329    } else {
2330        texlib_aux_show_character_error(L, ch);
2331        lua_pushinteger(L, 0);
2332    }
2333    return 1;
2334}
2335
2336static int texlib_getamcode(lua_State *L)
2337{
2338    int ch = lmt_checkinteger(L, 1);
2339    if (character_in_range(ch)) {
2340        lua_pushinteger(L, tex_get_am_code(ch));
2341    } else {
2342        texlib_aux_show_character_error(L, ch);
2343        lua_pushinteger(L, 0);
2344    }
2345    return 1;
2346}
2347
2348static int texlib_getcccode(lua_State *L)
2349{
2350    int ch = lmt_checkinteger(L, 1);
2351    if (character_in_range(ch)) {
2352        lua_pushinteger(L, tex_get_cc_code(ch));
2353    } else {
2354        texlib_aux_show_character_error(L, ch);
2355        lua_pushinteger(L, 0);
2356    }
2357    return 1;
2358}
2359
2360/* [global] [cattable] code value */
2361
2362static int texlib_setcatcode(lua_State *L)
2363{
2364    int top = lua_gettop(L);
2365    if (top >= 2) {
2366        quarterword level;
2367        int slot = lmt_check_for_level(L, 1, &level, cur_level);
2368        int cattable = ((top - slot + 1) >= 3) ? lmt_checkinteger(L, slot++) : cat_code_table_par;
2369        int ch = lmt_checkinteger(L, slot++);
2370        if (character_in_range(ch)) {
2371            halfword val = lmt_checkhalfword(L, slot);
2372            if (catcode_in_range(val)) {
2373                tex_set_cat_code(cattable, ch, val, level);
2374            } else {
2375                texlib_aux_show_catcode_error(L, val);
2376            }
2377        } else {
2378            texlib_aux_show_character_error(L, ch);
2379        }
2380    }
2381    return 0;
2382}
2383
2384/* [cattable] code */
2385
2386static int texlib_getcatcode(lua_State *L)
2387{
2388    int slot = 1;
2389    int cattable = (lua_gettop(L) > 1) ? lmt_checkinteger(L, slot++) : cat_code_table_par;
2390    int ch = lmt_checkinteger(L, slot);
2391    if (character_in_range(ch)) {
2392        lua_pushinteger(L, tex_get_cat_code(cattable, ch));
2393    } else {
2394        texlib_aux_show_character_error(L, ch);
2395        lua_pushinteger(L, 12); /* other */
2396    }
2397    return 1;
2398}
2399
2400/*
2401    [global] code { c f ch }
2402    [global] code   c f ch   (a bit easier on memory, counterpart of getter)
2403*/
2404
2405static int texlib_setmathcode(lua_State *L)
2406{
2407    quarterword level;
2408    int slot = lmt_check_for_level(L, 1, &level, cur_level);
2409    int ch = lmt_checkinteger(L, slot++);
2410    if (character_in_range(ch)) {
2411        halfword cval, fval, chval;
2412        switch (lua_type(L, slot)) {
2413            case LUA_TNUMBER:
2414                cval = lmt_checkhalfword(L, slot++);
2415                fval = lmt_checkhalfword(L, slot++);
2416                chval = lmt_checkhalfword(L, slot);
2417                break;
2418            case LUA_TTABLE:
2419                lua_rawgeti(L, slot, 1);
2420                cval = lmt_checkhalfword(L, -1);
2421                lua_rawgeti(L, slot, 2);
2422                fval = lmt_checkhalfword(L, -1);
2423                lua_rawgeti(L, slot, 3);
2424                chval = lmt_checkhalfword(L, -1);
2425                lua_pop(L, 3);
2426                break;
2427            default:
2428                return luaL_error(L, "number of table expected");
2429        }
2430        if (class_in_range(cval)) {
2431            if (family_in_range(fval)) {
2432                if (character_in_range(chval)) {
2433                    mathcodeval m;
2434                    m.character_value = chval;
2435                    m.class_value = (short) cval;
2436                    m.family_value = (short) fval;
2437                    tex_set_math_code(ch, m, (quarterword) (level));
2438                } else {
2439                    texlib_aux_show_character_error(L, chval);
2440                }
2441            } else {
2442                texlib_aux_show_family_error(L, fval);
2443            }
2444        } else {
2445            texlib_aux_show_class_error(L, cval);
2446        }
2447    } else {
2448        texlib_aux_show_character_error(L, ch);
2449    }
2450return 0;
2451}
2452
2453static int texlib_getmathcode(lua_State* L)
2454{
2455    mathcodeval mval = tex_no_math_code();
2456    int ch = lmt_checkinteger(L, -1);
2457    if (character_in_range(ch)) {
2458        mval = tex_get_math_code(ch);
2459    } else {
2460        texlib_aux_show_character_error(L, ch);
2461    }
2462    lua_createtable(L, 3, 0);
2463    lua_pushinteger(L, mval.class_value);
2464    lua_rawseti(L, -2, 1);
2465    lua_pushinteger(L, mval.family_value);
2466    lua_rawseti(L, -2, 2);
2467    lua_pushinteger(L, mval.character_value);
2468    lua_rawseti(L, -2, 3);
2469    return 1;
2470}
2471
2472static int texlib_getmathcodes(lua_State* L)
2473{
2474    mathcodeval mval = tex_no_math_code();
2475    int ch = lmt_checkinteger(L, -1);
2476    if (character_in_range(ch)) {
2477        mval = tex_get_math_code(ch);
2478    } else {
2479        texlib_aux_show_character_error(L, ch);
2480    }
2481    lua_pushinteger(L, mval.class_value);
2482    lua_pushinteger(L, mval.family_value);
2483    lua_pushinteger(L, mval.character_value);
2484    return 3;
2485}
2486
2487/*
2488    [global] code { c f ch }
2489    [global] code   c f ch   (a bit easier on memory, counterpart of getter)
2490*/
2491
2492static int texlib_setdelcode(lua_State* L)
2493{
2494    quarterword level;
2495    int slot = lmt_check_for_level(L, 1, &level, cur_level);
2496    /* todo: when no integer than do a reset */
2497    int ch = lmt_checkinteger(L, slot++);
2498    if (character_in_range(ch)) {
2499        halfword sfval, scval, lfval, lcval;
2500        switch (lua_type(L, slot)) {
2501            case LUA_TNUMBER:
2502                sfval = lmt_checkhalfword(L, slot++);
2503                scval = lmt_checkhalfword(L, slot++);
2504                lfval = lmt_checkhalfword(L, slot++);
2505                lcval = lmt_checkhalfword(L, slot);
2506                break;
2507            case LUA_TTABLE:
2508                lua_rawgeti(L, slot, 1);
2509                sfval = lmt_checkhalfword(L, -1);
2510                lua_rawgeti(L, slot, 2);
2511                scval = lmt_checkhalfword(L, -1);
2512                lua_rawgeti(L, slot, 3);
2513                lfval = lmt_checkhalfword(L, -1);
2514                lua_rawgeti(L, slot, 4);
2515                lcval = lmt_checkhalfword(L, -1);
2516                lua_pop(L, 4);
2517                break;
2518            default:
2519                return luaL_error(L, "number of table expected");
2520        }
2521        if (family_in_range(sfval)) {
2522            if (character_in_range(scval)) {
2523                if (family_in_range(lfval)) {
2524                    if (character_in_range(lcval)) {
2525                        delcodeval d;
2526                        d.small.class_value = 0;
2527                        d.small.family_value = (short) sfval;
2528                        d.small.character_value = scval;
2529                        d.large.class_value = 0;
2530                        d.large.family_value = (short) lfval;
2531                        d.large.character_value = lcval;
2532                        tex_set_del_code(ch, d, (quarterword) (level));
2533                    }
2534                    else {
2535                        texlib_aux_show_character_error(L, lcval);
2536                    }
2537                }
2538                else {
2539                    texlib_aux_show_family_error(L, lfval);
2540                }
2541            }
2542            else {
2543                texlib_aux_show_character_error(L, scval);
2544            }
2545        }
2546        else {
2547            texlib_aux_show_family_error(L, sfval);
2548        }
2549    }
2550    else {
2551    texlib_aux_show_character_error(L, ch);
2552    }
2553    return 0;
2554}
2555
2556static int texlib_getdelcode(lua_State* L)
2557{
2558    delcodeval dval = tex_no_del_code();
2559    int ch = lmt_checkinteger(L, -1);
2560    if (character_in_range(ch)) {
2561        dval = tex_get_del_code(ch);
2562    } else {
2563        texlib_aux_show_character_error(L, ch);
2564    }
2565    if (tex_has_del_code(dval)) {
2566        lua_createtable(L, 4, 0);
2567        lua_pushinteger(L, dval.small.family_value);
2568        lua_rawseti(L, -2, 1);
2569        lua_pushinteger(L, dval.small.character_value);
2570        lua_rawseti(L, -2, 2);
2571        lua_pushinteger(L, dval.large.family_value);
2572        lua_rawseti(L, -2, 3);
2573        lua_pushinteger(L, dval.large.character_value);
2574        lua_rawseti(L, -2, 4);
2575    } else {
2576        lua_pushnil(L);
2577    }
2578    return 1;
2579}
2580
2581static int texlib_getdelcodes(lua_State* L)
2582{
2583    delcodeval dval = tex_no_del_code();
2584    int ch = lmt_checkinteger(L, -1);
2585    if (character_in_range(ch)) {
2586        dval = tex_get_del_code(ch);
2587    } else {
2588        texlib_aux_show_character_error(L, ch);
2589    }
2590    if (tex_has_del_code(dval)) {
2591        lua_pushinteger(L, dval.small.family_value);
2592        lua_pushinteger(L, dval.small.character_value);
2593        lua_pushinteger(L, dval.large.family_value);
2594        lua_pushinteger(L, dval.large.character_value);
2595    } else {
2596        lua_pushnil(L);
2597    }
2598    return 4;
2599}
2600
2601static halfword texlib_aux_getdimension(lua_State* L, int index)
2602{
2603    switch (lua_type(L, index)) {
2604        case LUA_TNUMBER:
2605            return lmt_toroundnumber(L, index);
2606        case LUA_TSTRING:
2607            return texlib_aux_dimension_to_number(L, lua_tostring(L, index));
2608        default:
2609            luaL_error(L, "string or number expected (dimension)");
2610            return 0;
2611    }
2612}
2613
2614static halfword texlib_aux_getinteger(lua_State* L, int index)
2615{
2616    switch (lua_type(L, index)) {
2617        case LUA_TNUMBER:
2618            return lmt_toroundnumber(L, index);
2619        default:
2620            luaL_error(L, "number expected (integer)");
2621            return 0;
2622    }
2623}
2624
2625static halfword texlib_toparshape(lua_State *L, int i)
2626{
2627    if (lua_type(L, i) == LUA_TTABLE) {
2628        halfword n = (halfword) luaL_len(L, i);
2629        if (n > 0) {
2630            halfword p = tex_new_specification_node(n, par_shape_code, 0); /* todo: repeat but then not top based */
2631            lua_push_key(repeat);
2632            if (lua_rawget(L, -2) == LUA_TBOOLEAN && lua_toboolean(L, -1)) {
2633                tex_set_specification_option(p, specification_option_repeat);
2634            }
2635            lua_pop(L, 1);
2636            for (int j = 1; j <= n; j++) {
2637                halfword indent = 0;
2638                halfword width = 0;
2639                if (lua_rawgeti(L, i, j) == LUA_TTABLE) {
2640                    if (lua_rawgeti(L, -1, 1) == LUA_TNUMBER) {
2641                        indent = lmt_toroundnumber(L, -1);
2642                        if (lua_rawgeti(L, -2, 2) == LUA_TNUMBER) {
2643                            width = lmt_toroundnumber(L, -1);
2644                        }
2645                        lua_pop(L, 1);
2646                    }
2647                    lua_pop(L, 1);
2648                }
2649                lua_pop(L, 1);
2650                tex_set_specification_indent(p, j, indent);
2651                tex_set_specification_width(p, j, width);
2652            }
2653            return p;
2654        }
2655    }
2656    return null;
2657}
2658
2659static halfword texlib_tobalanceshape(lua_State *L, int i)
2660{
2661    if (lua_type(L, i) == LUA_TTABLE) {
2662        halfword n = (halfword) luaL_len(L, i);
2663        if (n > 0) {
2664            halfword p = tex_new_specification_node(n, balance_shape_code, 0); /* todo: repeat but then not top based */
2665            lua_push_key(repeat);
2666            if (lua_rawget(L, -2) == LUA_TBOOLEAN && lua_toboolean(L, -1)) {
2667                tex_set_specification_option(p, specification_option_repeat); /* not yet used */
2668            }
2669            lua_pop(L, 1);
2670            {
2671                halfword target = 0;
2672                set_numeric_field_by_index(target, identifier, 0);
2673                balance_shape_identifier(p) = target;
2674            }
2675            for (int j = 1; j <= n; j++) {
2676                if (lua_rawgeti(L, i, j) == LUA_TTABLE) {
2677                    halfword target = 0;
2678                    set_numeric_field_by_index(target, index, 0);
2679                    tex_set_balance_index(p, j, target);
2680                    set_numeric_field_by_index(target, vsize, 0);
2681                    tex_set_balance_vsize(p, j, target);
2682                    set_numeric_field_by_index(target, extra, 0);
2683                    tex_set_balance_extra(p, j, target);
2684                    set_numeric_field_by_index(target, options, 0);
2685                    tex_set_balance_options(p, j, target);
2686                    /* */
2687                    lua_push_key(topskip);                    
2688                    switch (lua_rawget(L, -2)) { 
2689                        case LUA_TNUMBER: 
2690                            { 
2691                                target = tex_new_glue_spec_node(zero_glue);
2692                                glue_amount(target) = lmt_toroundnumber(L, -1);
2693                                tex_set_balance_topskip(p, j, target);
2694                                break;
2695                            }
2696                        case LUA_TUSERDATA: 
2697                            { 
2698                                target = lmt_optional_isnode(L, -1);
2699                                tex_set_balance_topskip(p, j, tex_copy_node(target && node_type(target) == glue_spec_node ? target : balance_top_skip_par));
2700                                break;
2701                            }
2702                    }
2703                    lua_pop(L, 1);
2704                    /* */
2705                    lua_push_key(bottomskip);
2706                    switch (lua_rawget(L, -2)) { 
2707                        case LUA_TNUMBER: 
2708                            { 
2709                                target = tex_new_glue_spec_node(zero_glue);
2710                                glue_amount(target) = lmt_toroundnumber(L, -1);
2711                                tex_set_balance_bottomskip(p, j, target);
2712                                break;
2713                            }
2714                        case LUA_TUSERDATA: 
2715                            { 
2716                                target = lmt_optional_isnode(L, -1);
2717                                tex_set_balance_bottomskip(p, j, tex_copy_node(target && node_type(target) == glue_spec_node ? target : balance_bottom_skip_par));
2718                                break;
2719                            }
2720                    }
2721                    lua_pop(L, 1);
2722                    /* */
2723                }
2724                lua_pop(L, 1);
2725            }
2726            return p;
2727        }
2728    }
2729    return null;
2730}
2731
2732static int texlib_setbalanceshape(lua_State *L)
2733{
2734    halfword p = texlib_tobalanceshape(L, 1);
2735    update_tex_balance_shape(p);
2736    lua_pushinteger(L, p ? specification_count(p) : 0);
2737    return 1;
2738}
2739
2740static int texlib_getcurrentmvl(lua_State *L)
2741{
2742    lua_pushinteger(L, tex_current_mvl(NULL, NULL));
2743    return 1;
2744}
2745
2746static int texlib_shiftparshape(lua_State *L)
2747{
2748    if (par_shape_par) {
2749        tex_shift_specification_list(par_shape_par, lmt_tointeger(L, 1), lua_toboolean(L, 2));
2750    }
2751    return 0;
2752}
2753
2754static int texlib_snapshotpar(lua_State *L)
2755{
2756    halfword par = tex_find_par_par(cur_list.head);
2757    if (par) {
2758        if (lua_type(L, 1) == LUA_TNUMBER) {
2759            tex_snapshot_par(par, lmt_tointeger(L, 1));
2760        }
2761        lua_pushinteger(L, par_state(par));
2762        return 1;
2763    } else {
2764        return 0;
2765    }
2766}
2767
2768static int texlib_getparstatefields(lua_State *L)
2769{
2770    lua_createtable(L, 7, 0);
2771    lua_push_key_at_index(L, hsize,      1);
2772    lua_push_key_at_index(L, leftskip,   2);
2773    lua_push_key_at_index(L, rightskip,  3);
2774    lua_push_key_at_index(L, hangindent, 4);
2775    lua_push_key_at_index(L, hangafter,  5);
2776    lua_push_key_at_index(L, parindent,  6);
2777    lua_push_key_at_index(L, parshape,   7);
2778    return 1;
2779}
2780
2781static int texlib_getparstate(lua_State *L)
2782{
2783    lua_createtable(L, 0, 7);
2784    lua_push_integer_at_key(L, hsize, hsize_par);
2785    lua_push_integer_at_key(L, leftskip, left_skip_par ? glue_amount(left_skip_par) : 0);
2786    lua_push_integer_at_key(L, rightskip, right_skip_par ? glue_amount(right_skip_par) : 0);
2787    lua_push_integer_at_key(L, hangindent, hang_indent_par);
2788    lua_push_integer_at_key(L, hangafter, hang_after_par);
2789    lua_push_integer_at_key(L, parindent, par_indent_par);
2790    lua_push_specification_at_key(L, parshape, par_shape_par);
2791    return 1;
2792}
2793
2794static int texlib_set_item(lua_State* L, int index, int prefixes)
2795{
2796    int flags = 0;
2797    int slot = lmt_check_for_flags(L, index, &flags, prefixes, 0);
2798    size_t sl;
2799    const char *st = lua_tolstring(L, slot++, &sl);
2800    if (sl > 0) {
2801        int cs = tex_string_locate_only(st, sl);
2802        if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
2803            int cmd = eq_type(cs);
2804            switch (cmd) {
2805                case internal_integer_cmd:
2806                case register_integer_cmd: /* ? */
2807                    switch (lua_type(L, slot)) {
2808                        case LUA_TNUMBER:
2809                            {
2810                                int n = lmt_tointeger(L, slot++);
2811                                if (cmd == register_integer_cmd) {
2812                                    tex_word_define(flags, eq_value(cs), n);
2813                                } else {
2814                                    tex_assign_internal_integer_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
2815                                }
2816                                break;
2817                            }
2818                        default:
2819                            luaL_error(L, "number expected");
2820                            break;
2821                    }
2822                    return 1;
2823                case internal_posit_cmd:
2824                case register_posit_cmd: /* ? */
2825                    switch (lua_type(L, slot)) {
2826                        case LUA_TNUMBER:
2827                            {
2828                                int n = tex_double_to_posit(lua_tonumber(L, slot++)).v;
2829                                if (cmd == register_posit_cmd) {
2830                                    tex_word_define(flags, eq_value(cs), n);
2831                                } else {
2832                                    tex_assign_internal_posit_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
2833                                }
2834                                break;
2835                            }
2836                     // case userdata:
2837                     //     {
2838                     //         /* todo */
2839                     //         break;
2840                     //     }
2841                        default:
2842                            luaL_error(L, "number expected");
2843                            break;
2844                    }
2845                    return 1;
2846                case internal_dimension_cmd:
2847                case register_dimension_cmd:
2848                    {
2849                        halfword n = texlib_aux_getdimension(L, slot);
2850                        if (cmd == register_dimension_cmd) {
2851                            tex_word_define(flags, eq_value(cs), n);
2852                        } else {
2853                            tex_assign_internal_dimension_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
2854                        }
2855                        return 1;
2856                    }
2857                case internal_glue_cmd:
2858                case register_glue_cmd:
2859                    switch (lua_type(L, slot)) {
2860                        case LUA_TNUMBER:
2861                            {
2862                                int top = lua_gettop(L);
2863                                halfword value = tex_copy_node(zero_glue);
2864                                glue_amount(value) = lmt_toroundnumber(L, slot++);
2865                                if (slot <= top) {
2866                                    glue_stretch(value) = lmt_toroundnumber(L, slot++);
2867                                    if (slot <= top) {
2868                                        glue_shrink(value) = lmt_toroundnumber(L, slot++);
2869                                        if (slot <= top) {
2870                                            glue_stretch_order(value) = tex_checked_glue_order(lmt_tohalfword(L, slot++));
2871                                            if (slot <= top) {
2872                                                glue_shrink_order(value) = tex_checked_glue_order(lmt_tohalfword(L, slot));
2873                                            }
2874                                        }
2875                                    }
2876                                }
2877                                if (cmd == register_glue_cmd) {
2878                                    tex_word_define(flags, eq_value(cs), value);
2879                                } else {
2880                                    tex_assign_internal_skip_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), value);
2881                                }
2882                                break;
2883                            }
2884                        case LUA_TUSERDATA:
2885                            {
2886                                halfword n = lmt_check_isnode(L, slot);
2887                                if (node_type(n) == glue_spec_node) {
2888                                    if (cmd == register_glue_cmd) {
2889                                        tex_word_define(flags, eq_value(cs), n);
2890                                    } else {
2891                                        tex_assign_internal_skip_value(lua_toboolean(L, slot) ? add_frozen_flag(flags) : flags, eq_value(cs), n);
2892                                    }
2893                                } else {
2894                                    luaL_error(L, "gluespec node expected");
2895                                }
2896                                break;
2897                            }
2898                        default:
2899                            luaL_error(L, "number or node expected");
2900                            break;
2901                    }
2902                    return 1;
2903                case internal_toks_cmd:
2904                case register_toks_cmd:
2905                    switch (lua_type(L, slot)) {
2906                        case LUA_TSTRING:
2907                            {
2908                                int t = lmt_token_list_from_lua(L, slot);
2909                             // define(flags, eq_value(cs), call_cmd, t); /* was call_cmd */
2910                                tex_define(flags, eq_value(cs), cmd == internal_toks_cmd ? internal_toks_reference_cmd : register_toks_reference_cmd, t); /* eq_value(cs) and not cs ? */
2911                                break;
2912                            }
2913                        default:
2914                            luaL_error(L, "string expected");
2915                            break;
2916                    }
2917                    return 1;
2918                case page_property_cmd:
2919                    /*tex This could be |set_page_property_value| instead. */
2920                    switch (eq_value(cs)) {
2921                     // case page_goal_code:
2922                     // case page_total_code:
2923                     // case page_vsize_code:
2924                     // case page_last_height_code:
2925                     // case page_last_depth_code:
2926                        case page_depth_code:
2927                            lmt_page_builder_state.depth = texlib_aux_getdimension(L, slot);
2928                            break;
2929                     // case page_stretch_code:
2930                     // case page_filstretch_code:
2931                     // case page_fillstretch_code:
2932                     // case page_filllstretch_code:
2933                     // case page_shrink_code:
2934                        case insert_storing_code:
2935                            lmt_insert_state.storing = texlib_aux_getinteger(L, slot);
2936                            break;
2937                     // case dead_cycles_code:
2938                     // case insert_penalties_code:
2939                     // case interaction_mode_code:
2940                        default:
2941                            return 0;
2942                    }
2943                case auxiliary_cmd:
2944                    /*tex This could be |set_aux_value| instead. */
2945                    switch (eq_value(cs)) {
2946                        case space_factor_code:
2947                            cur_list.space_factor = texlib_aux_getinteger(L, slot);
2948                            return 1;
2949                        case prev_depth_code:
2950                            cur_list.prev_depth = texlib_aux_getdimension(L, slot);
2951                            return 1;
2952                        case prev_graf_code:
2953                            cur_list.prev_graf = texlib_aux_getinteger(L, slot);
2954                            return 1;
2955                        default:
2956                            return 0;
2957                    }
2958                case box_property_cmd:
2959                    /*tex This could be |set_box_property_value| instead. */
2960                    return 0;
2961                case specification_cmd:
2962                    {
2963                        halfword spec = eq_value(cs);
2964                        if (spec) {
2965                            switch (node_subtype(spec)) {
2966                                case par_shape_code:
2967                                    {
2968                                        halfword p = texlib_toparshape(L, slot);
2969                                        tex_define(flags, spec, specification_reference_cmd, p);
2970                                       // lua_toboolean(L, slot + 1) ? add_frozen_flag(flags) : flags
2971                                        if (is_frozen(flags) && cur_mode == hmode) {
2972                                            tex_update_par_par(specification_reference_cmd, internal_specification_number(spec));
2973                                        }
2974                                        break;
2975                                    }
2976                             // case balance_shape_code:
2977                             //     {
2978                             //         halfword p = texlib_tobalanceshape(L, slot);
2979                             //         tex_define(flags, spec, specification_reference_cmd, p);
2980                             //         break;
2981                             //     }
2982                                /* todo, when we need it, for instance lists */
2983                            }
2984                        }
2985                        return 0;
2986                    }
2987            }
2988        }
2989    }
2990    return 0;
2991}
2992
2993static int texlib_set(lua_State *L)
2994{
2995    texlib_set_item(L, 1, 1);
2996    return 0;
2997}
2998
2999static int texlib_newindex(lua_State *L)
3000{
3001    if (! texlib_set_item(L, 2, 0)) {
3002        lua_rawset(L, 1);
3003    }
3004    return 0;
3005}
3006
3007static int texlib_aux_convert(lua_State *L, int code, int index, halfword dflt)
3008{
3009    int classification = some_convert_classification[code];
3010    int value = classification == classification_integer ? (dflt ? lmt_optinteger(L, index, dflt) : lmt_checkinteger(L, index)) : 0;
3011    int texstr = tex_the_convert_string(code, value);
3012    if (texstr) {
3013        lua_pushstring(L, tex_to_cstring(texstr));
3014        tex_flush_str(texstr);
3015    } else {
3016        lua_pushnil(L);
3017    }
3018    return 1;
3019}
3020
3021/*tex This still kind of incomplete. */
3022
3023static int texlib_aux_scan_internal(lua_State *L, int cmd, int code, int values)
3024{
3025    int retval = 1 ;
3026    int save_cur_val = cur_val;
3027    int save_cur_val_level = cur_val_level;
3028    tex_scan_something_simple(cmd, code);
3029    switch (cur_val_level) {
3030        case integer_val_level:
3031        case attribute_val_level:
3032        case dimension_val_level:
3033            lua_pushinteger(L, cur_val);
3034            break;
3035        case posit_val_level:
3036            lua_pushnumber(L, tex_posit_to_double(cur_val));
3037            break;
3038        case glue_val_level:
3039        case muglue_val_level:
3040            switch (values) {
3041                case 0:
3042                    lua_pushinteger(L, glue_amount(cur_val));
3043                    tex_flush_node(cur_val);
3044                    break;
3045                case 1:
3046                    lua_pushinteger(L, glue_amount(cur_val));
3047                    lua_pushinteger(L, glue_stretch(cur_val));
3048                    lua_pushinteger(L, glue_shrink(cur_val));
3049                    lua_pushinteger(L, glue_stretch_order(cur_val));
3050                    lua_pushinteger(L, glue_shrink_order(cur_val));
3051                    tex_flush_node(cur_val);
3052                    retval = 5;
3053                    break;
3054                default:
3055                    lmt_push_node_fast(L, cur_val);
3056                    break;
3057            }
3058            break;
3059        case list_val_level:
3060            lmt_push_node_fast(L, cur_val);
3061            break;
3062        default:
3063            {
3064                int texstr = tex_the_scanned_result();
3065                const char *str = tex_to_cstring(texstr);
3066                if (str) {
3067                    lua_pushstring(L, str);
3068                } else {
3069                    lua_pushnil(L);
3070                }
3071                tex_flush_str(texstr);
3072            }
3073            break;
3074    }
3075    cur_val = save_cur_val;
3076    cur_val_level = save_cur_val_level;
3077    return retval;
3078}
3079
3080static int texlib_aux_item(lua_State *L, int code, int all)
3081{
3082    if (some_item_classification[code] == classification_no_arguments) {
3083        return texlib_aux_scan_internal(L, convert_cmd, code, all);
3084    } else {
3085        return 0;
3086    }
3087}
3088
3089static int texlib_setmath(lua_State *L)
3090{
3091    int top = lua_gettop(L);
3092    if (top >= 3) {
3093        quarterword level;
3094        int slot = lmt_check_for_level(L, 1, &level, cur_level);
3095        int param = lmt_get_math_parameter(L, slot++, -1);
3096        int style = lmt_get_math_style(L, slot++, -1);
3097        if (param < 0 || style < 0) {
3098            /* invalid spec, just ignore it  */
3099        } else {
3100            switch (math_parameter_value_type(param)) {
3101                case math_integer_parameter:
3102                case math_dimension_parameter:
3103                case math_style_parameter:
3104                    tex_def_math_parameter(style, param, (scaled) lmt_optroundnumber(L, slot, 0), level, indirect_math_regular, 0);
3105                    break;
3106                case math_muglue_parameter:
3107                    {
3108                        halfword p = tex_copy_node(zero_glue);
3109                        glue_amount(p) = lmt_optroundnumber(L, slot++, 0);
3110                        glue_stretch(p) = lmt_optroundnumber(L, slot++, 0);
3111                        glue_shrink(p) = lmt_optroundnumber(L, slot++, 0);
3112                        glue_stretch_order(p) = tex_checked_glue_order(lmt_optroundnumber(L, slot++, 0));
3113                        glue_shrink_order(p) = tex_checked_glue_order(lmt_optroundnumber(L, slot, 0));
3114                        tex_def_math_parameter(style, param, (scaled) p, level, indirect_math_regular, 0);
3115                        break;
3116                    }
3117            }
3118        }
3119    }
3120    return 0;
3121}
3122
3123static int texlib_getmath(lua_State *L)
3124{
3125    if (lua_gettop(L) == 2) {
3126        int param = lmt_get_math_parameter(L, 1, -1);
3127        int style = lmt_get_math_style(L, 2, -1);
3128        if (param >= 0 && style >= 0) {
3129            scaled value = tex_get_math_parameter(style, param, NULL);
3130            if (value != undefined_math_parameter) {
3131                switch (math_parameter_value_type(param)) {
3132                    case math_integer_parameter:
3133                    case math_dimension_parameter:
3134                    case math_style_parameter:
3135                        lua_pushinteger(L, value);
3136                        return 1;
3137                    case math_muglue_parameter:
3138                        if (value <= thick_muskip_code) {
3139                            value = glue_parameter(value);
3140                        }
3141                        lua_pushinteger(L, glue_amount(value));
3142                        lua_pushinteger(L, glue_stretch(value));
3143                        lua_pushinteger(L, glue_shrink(value));
3144                        lua_pushinteger(L, glue_stretch_order(value));
3145                        lua_pushinteger(L, glue_shrink_order(value));
3146                        return 5;
3147                }
3148            }
3149        }
3150    }
3151    lua_pushnil(L);
3152    return 1;
3153}
3154
3155/*tex
3156
3157    This one is purely for diagnostic purposed as normally there is some scaling
3158    involved related to the current style and such.
3159
3160*/
3161
3162// todo when no argument then default to font.currnt()
3163
3164static int texlib_getfontname(lua_State *L)
3165{
3166    return texlib_aux_convert(L, font_name_code, 1, cur_font_par);
3167}
3168
3169static int texlib_getfontidentifier(lua_State *L)
3170{
3171    return texlib_aux_convert(L, font_identifier_code, 1, cur_font_par);
3172}
3173
3174static int texlib_getfontoffamily(lua_State *L)
3175{
3176    int f = lmt_checkinteger(L, 1);
3177    int s = lmt_optinteger(L, 2, 0);
3178    lua_pushinteger(L, tex_fam_fnt(f, s));
3179    return 1;
3180}
3181
3182static int texlib_getnumber(lua_State *L)
3183{
3184    return texlib_aux_convert(L, number_code, 1, 0);
3185}
3186
3187// static int texlib_getdimension(lua_State *L)
3188// {
3189//     return texlib_aux_convert(L, to_dimension_code);
3190// }
3191
3192static int texlib_getromannumeral(lua_State *L)
3193{
3194    return texlib_aux_convert(L, roman_numeral_code, 1, 0);
3195}
3196
3197/*tex
3198    In principle we could provide mote but it makes no sense. So far I never needed more and it
3199    would mean a lot of extra code to support it, like box properties that we can already access
3200    otherwise anyway.
3201*/
3202
3203int lmt_push_specification(lua_State *L, halfword ptr, int onlycount)
3204{
3205    if (ptr) {
3206        halfword code = node_subtype(ptr);
3207        switch (code) {
3208            case par_shape_code:
3209                {
3210                    int n = specification_count(ptr);
3211                    if (onlycount == 1) {
3212                        lua_pushinteger(L, n);
3213                    } else {
3214                        int r = specification_repeat(ptr);
3215                        lua_createtable(L, n, r ? 1 : 0);
3216                        if (r) {
3217                            lua_push_boolean_at_key(L, repeat, r);
3218                        }
3219                        for (int m = 1; m <= n; m++) {
3220                            lua_createtable(L, 2, 0);
3221                            lua_pushinteger(L, tex_get_specification_indent(ptr, m));
3222                            lua_rawseti(L, -2, 1);
3223                            lua_pushinteger(L, tex_get_specification_width(ptr, m));
3224                            lua_rawseti(L, -2, 2);
3225                            lua_rawseti(L, -2, m);
3226                        }
3227                    }
3228                    return 1;
3229                }
3230            case balance_shape_code:
3231                {
3232                    int n = specification_count(ptr);
3233                    if (onlycount == 1) {
3234                        lua_pushinteger(L, n);
3235                    } else {
3236                        int r = specification_repeat(ptr);
3237                        lua_createtable(L, n, r ? 1 : 0);
3238                        if (r) {
3239                            lua_push_boolean_at_key(L, repeat, r);
3240                        }
3241                        for (int m = 1; m <= n; m++) {
3242                            lua_createtable(L, 0, 5);
3243                            lua_push_integer_at_key(L, vsize, tex_get_balance_vsize(ptr, m));
3244                            lua_push_integer_at_key(L, options, tex_get_balance_options(ptr, m));
3245                            lua_push_integer_at_key(L, index, tex_get_balance_index(ptr, m));
3246                            /* for now, will become an array */
3247                            lua_push_integer_at_key(L, topskip, glue_amount(tex_get_balance_topskip(ptr, m)));
3248                            lua_push_integer_at_key(L, bottomskip, glue_amount(tex_get_balance_bottomskip(ptr, m)));
3249                            /* */
3250                            lua_rawseti(L, -2, m);
3251                        }
3252                    }
3253                    return 1;
3254                }
3255            case fitness_classes_code:
3256                {
3257                    int n = specification_count(ptr);
3258                    if (onlycount == 1) {
3259                        lua_pushinteger(L, n);
3260                    } else {
3261                        for (int m = 1; m <= n; m++) {
3262                            lua_pushinteger(L, tex_get_specification_fitness_class(ptr, m));
3263                            lua_rawseti(L, -2, m);
3264                        }
3265                    }
3266                    return 1;
3267                }
3268            case adjacent_demerits_code:
3269                {
3270                    int n = specification_count(ptr);
3271                    if (onlycount == 1) {
3272                        lua_pushinteger(L, n);
3273                    } else {
3274                        for (int m = 1; m <= n; m++) {
3275                            lua_createtable(L, 2, 0);
3276                            lua_pushinteger(L, tex_get_specification_adjacent_u(ptr, m));
3277                            lua_rawseti(L, -2, 1);
3278                            lua_pushinteger(L, tex_get_specification_adjacent_d(ptr, m));
3279                            lua_rawseti(L, -2, 2);
3280                            lua_rawseti(L, -2, m);
3281                        }
3282                    }
3283                    return 1;
3284                }
3285            case par_passes_code:
3286            case balance_passes_code:
3287                {
3288                    return 0;
3289                }
3290            case inter_line_penalties_code:
3291            case club_penalties_code:
3292            case widow_penalties_code:
3293            case display_widow_penalties_code:
3294            case orphan_penalties_code:
3295            case toddler_penalties_code:
3296            case orphan_line_factors_code:
3297            case math_forward_penalties_code:
3298            case math_backward_penalties_code:
3299            case integer_list_code:
3300            case dimension_list_code:
3301            case posit_list_code:
3302                {
3303                    int n = specification_count(ptr);
3304                    if (onlycount == 1) {
3305                        lua_pushinteger(L, n);
3306                    } else {
3307                        lua_createtable(L, n, 0);
3308                        if (specification_double(ptr)) {
3309                            for (int m = 1; m <= n; m++) {
3310                                lua_createtable(L, 2, 0);
3311                                if (code == posit_list_code) {
3312                                    if (specification_integer(ptr)) {
3313                                        lua_pushinteger(L, tex_get_specification_nepalty(ptr, m));
3314                                    } else {
3315                                        lua_pushnumber(L, tex_posit_to_double(tex_get_specification_nepalty(ptr, m)));
3316                                    }
3317                                    lua_rawseti(L, -2, 1);
3318                                    lua_pushnumber(L, tex_posit_to_double(tex_get_specification_penalty(ptr, m)));
3319                                    lua_rawseti(L, -2, 2);
3320                                } else {
3321                                    lua_pushinteger(L, tex_get_specification_nepalty(ptr, m));
3322                                    lua_rawseti(L, -2, 1);
3323                                    lua_pushinteger(L, tex_get_specification_penalty(ptr, m));
3324                                    lua_rawseti(L, -2, 2);
3325                                }
3326                                lua_rawseti(L, -2, m);
3327                            }
3328                        } else {
3329                            for (int m = 1; m <= n; m++) {
3330                                if (code == posit_list_code) {
3331                                    lua_pushnumber(L, tex_posit_to_double(tex_get_specification_penalty(ptr, m)));
3332                                } else {
3333                                    lua_pushinteger(L, tex_get_specification_penalty(ptr, m));
3334                                }
3335                                lua_rawseti(L, -2, m);
3336                            }
3337                        }
3338                    }
3339                    return 1;
3340                }
3341        }
3342    }
3343    lua_pushnil(L);
3344    return 1;
3345}
3346
3347static int texlib_get_internal(lua_State *L, int index, int all)
3348{
3349    if (lua_type(L, index) == LUA_TSTRING) {
3350        size_t l;
3351        const char *s = lua_tolstring(L, index, &l);
3352        if (l == 0) {
3353            return 0;
3354        } else if (lua_key_eq(s, prevdepth)) {
3355            lua_pushinteger(L, cur_list.prev_depth);
3356            return 1;
3357        } else if (lua_key_eq(s, prevgraf)) {
3358            lua_pushinteger(L, cur_list.prev_graf);
3359            return 1;
3360        } else if (lua_key_eq(s, spacefactor)) {
3361            lua_pushinteger(L, cur_list.space_factor);
3362            return 1;
3363        } else {
3364            int cs = tex_string_locate_only(s, l);
3365            if (cs != undefined_control_sequence && has_eq_flag_bits(cs, primitive_flag_bit)) {
3366                int cmd = eq_type(cs);
3367                int code = eq_value(cs);
3368                /*tex
3369                    This will become more simple over time as we specify more code classifications,
3370                    and thereby can support more.
3371                */
3372                switch (cmd) {
3373                    /*tex
3374                        Here are a few exceptions. More can be added when needed. Simple internals
3375                        are handled as default.
3376                    */
3377                    case some_item_cmd:
3378                        return texlib_aux_item(L, code, all);
3379                    case convert_cmd:
3380                        return texlib_aux_convert(L, code, index + 1, 0);
3381                    case specification_cmd:
3382                        return lmt_push_specification(L, specification_parameter(internal_specification_number(code)), all); /* all == countonly */
3383                    /*
3384                        These scan for more, we could push something into the input if really
3385                        needed, but that is then more a hack and it makes more sense to have
3386                        dedicated getters. We could add a property to the cmd definitions.
3387                    */
3388                    case font_property_cmd:
3389                    case hyphenation_cmd:
3390                    case box_property_cmd:
3391                    case define_family_cmd:
3392                    case math_parameter_cmd:
3393                    case association_cmd:
3394                    case register_cmd:
3395                        break;
3396                    /*tex
3397                        What is left are simple values that take no further scanning.
3398                    */
3399                    default:
3400                        return texlib_aux_scan_internal(L, cmd, code, all);
3401                }
3402            }
3403        }
3404     /* tex_formatted_warning("internal","unsupported command '%s'", lua_tostring(L, index)); */ /* can be a user one */
3405    }
3406    return 0;
3407}
3408
3409static int texlib_getspecification(lua_State *L)
3410{
3411    size_t l;
3412    const char *s = lua_tolstring(L, 1, &l);
3413    if (l) {
3414        int cs = tex_string_locate_only(s, l);
3415        if (cs != undefined_control_sequence) {
3416            switch (eq_type(cs)) {
3417                case specificationspec_cmd:
3418                    lmt_push_specification(L, eq_value(cs), lua_toboolean(L, 2));
3419                    return 1;
3420                case specification_cmd:
3421                    lmt_push_specification(L, specification_parameter(internal_specification_number(eq_value(cs))), lua_toboolean(L, 2));
3422                    return 1;
3423            }
3424        }
3425    }
3426    lua_pushnil(L);
3427    return 1;
3428}
3429
3430static int texlib_get(lua_State *L)
3431{
3432    /* stack: key [boolean] */
3433    int ret = texlib_get_internal(L, 1, (lua_type(L, 2) == LUA_TBOOLEAN) ? lua_toboolean(L, 2) : -1);
3434    if (ret) {
3435        return ret;
3436    } else {
3437        lua_pushnil(L);
3438        return 1;
3439    }
3440}
3441
3442static int texlib_index(lua_State *L)
3443{
3444    /* stack: table key */
3445    int ret = texlib_get_internal(L, 2, -1);
3446    if (ret) {
3447        return ret;
3448    } else {
3449        lua_rawget(L, 1);
3450        return 1;
3451    }
3452}
3453
3454static int texlib_getlistfields(lua_State *L)
3455{
3456    lua_createtable(L, 17, 0);
3457    lua_push_key_at_index(L, pageinserthead,     1);
3458    lua_push_key_at_index(L, contributehead,     2);
3459    lua_push_key_at_index(L, pagehead,           3);
3460    lua_push_key_at_index(L, temphead,           4);
3461    lua_push_key_at_index(L, holdhead,           5);
3462    lua_push_key_at_index(L, postadjusthead,     6);
3463    lua_push_key_at_index(L, preadjusthead,      7);
3464    lua_push_key_at_index(L, postmigratehead,    8);
3465    lua_push_key_at_index(L, premigratehead,     9);
3466    lua_push_key_at_index(L, alignhead,         10);
3467    lua_push_key_at_index(L, pagediscardshead,  11);
3468    lua_push_key_at_index(L, splitdiscardshead, 12);
3469    lua_push_key_at_index(L, bestpagebreak,     13);
3470    lua_push_key_at_index(L, leastpagecost,     14);
3471    lua_push_key_at_index(L, bestsize,          15);
3472    lua_push_key_at_index(L, insertpenalties,   16);
3473    lua_push_key_at_index(L, insertheights,     17);
3474    return 1;
3475}
3476
3477static int texlib_getlist(lua_State *L)
3478{
3479    const char *s = lua_tostring(L, 1);
3480    if (! s) {
3481        lua_pushnil(L);
3482    } else if (lua_key_eq(s, pageinserthead)) {
3483        lmt_push_node_fast(L, tex_get_special_node_list(page_insert_list_type, NULL));
3484    } else if (lua_key_eq(s, contributehead)) {
3485        lmt_push_node_fast(L, tex_get_special_node_list(contribute_list_type, NULL));
3486    } else if (lua_key_eq(s, pagehead)) {
3487        lmt_push_node_fast(L, tex_get_special_node_list(page_list_type, NULL));
3488    } else if (lua_key_eq(s, temphead)) {
3489        lmt_push_node_fast(L, tex_get_special_node_list(temp_list_type, NULL));
3490    } else if (lua_key_eq(s, holdhead)) {
3491        lmt_push_node_fast(L, tex_get_special_node_list(hold_list_type, NULL));
3492    } else if (lua_key_eq(s, postadjusthead)) {
3493        lmt_push_node_fast(L, tex_get_special_node_list(post_adjust_list_type, NULL));
3494    } else if (lua_key_eq(s, preadjusthead)) {
3495        lmt_push_node_fast(L, tex_get_special_node_list(pre_adjust_list_type, NULL));
3496    } else if (lua_key_eq(s, postmigratehead)) {
3497        lmt_push_node_fast(L, tex_get_special_node_list(post_migrate_list_type, NULL));
3498    } else if (lua_key_eq(s, premigratehead)) {
3499        lmt_push_node_fast(L, tex_get_special_node_list(pre_migrate_list_type, NULL));
3500    } else if (lua_key_eq(s, alignhead)) {
3501        lmt_push_node_fast(L, tex_get_special_node_list(align_list_type, NULL));
3502    } else if (lua_key_eq(s, pagediscardshead)) {
3503        lmt_push_node_fast(L, tex_get_special_node_list(page_discards_list_type, NULL));
3504    } else if (lua_key_eq(s, splitdiscardshead)) {
3505        lmt_push_node_fast(L, tex_get_special_node_list(split_discards_list_type, NULL));
3506    } else if (lua_key_eq(s, bestpagebreak)) {
3507        lmt_push_node_fast(L, lmt_page_builder_state.best_break);
3508    } else if (lua_key_eq(s, leastpagecost)) {
3509        lua_pushinteger(L, lmt_page_builder_state.least_cost);
3510    } else if (lua_key_eq(s, bestsize)) {
3511        lua_pushinteger(L, lmt_page_builder_state.best_size); /* is pagegoal but can be unset and also persistent */
3512    } else if (lua_key_eq(s, insertpenalties)) {
3513        lua_pushinteger(L, lmt_page_builder_state.insert_penalties);
3514    } else if (lua_key_eq(s, insertheights)) {
3515        lua_pushinteger(L, lmt_page_builder_state.insert_heights);
3516    } else {
3517        lua_pushnil(L);
3518    }
3519    return 1;
3520}
3521
3522/* todo: accept direct node too */
3523
3524static int texlib_setlist(lua_State *L)
3525{
3526    const char *s = lua_tostring(L, 1);
3527    if (! s) {
3528        /* This is silently ignored */
3529    } else if (lua_key_eq(s, bestsize)) {
3530        lmt_page_builder_state.best_size = lmt_toscaled(L, 2); /* is pagegoal but can be unset and also persistent */
3531    } else if (lua_key_eq(s, leastpagecost)) {
3532        lmt_page_builder_state.least_cost = lmt_tointeger(L, 2);
3533    } else if (lua_key_eq(s, insertpenalties)) {
3534        lmt_page_builder_state.insert_penalties = lmt_tointeger(L, 2);
3535    } else if (lua_key_eq(s, insertheights)) {
3536        lmt_page_builder_state.insert_heights = lmt_tointeger(L, 2);
3537    } else {
3538        halfword n = null;
3539        if (! lua_isnil(L, 2)) {
3540            n = lmt_check_isnode(L, 2);
3541        }
3542        if (lua_key_eq(s, pageinserthead)) {
3543            tex_set_special_node_list(page_insert_list_type, n);
3544        } else if (lua_key_eq(s, contributehead)) {
3545            tex_set_special_node_list(contribute_list_type, n);
3546        } else if (lua_key_eq(s, pagehead)) {
3547            tex_set_special_node_list(page_list_type, n);
3548        } else if (lua_key_eq(s, temphead)) {
3549            tex_set_special_node_list(temp_list_type, n);
3550        } else if (lua_key_eq(s, pagediscardshead)) {
3551            tex_set_special_node_list(page_discards_list_type, n);
3552        } else if (lua_key_eq(s, splitdiscardshead)) {
3553            tex_set_special_node_list(split_discards_list_type, n);
3554        } else if (lua_key_eq(s, holdhead)) {
3555            tex_set_special_node_list(hold_list_type, n);
3556        } else if (lua_key_eq(s, postadjusthead)) {
3557            tex_set_special_node_list(post_adjust_list_type, n);
3558        } else if (lua_key_eq(s, preadjusthead)) {
3559            tex_set_special_node_list(pre_adjust_list_type, n);
3560        } else if (lua_key_eq(s, postmigratehead)) {
3561            tex_set_special_node_list(post_migrate_list_type, n);
3562        } else if (lua_key_eq(s, premigratehead)) {
3563            tex_set_special_node_list(pre_migrate_list_type, n);
3564        } else if (lua_key_eq(s, alignhead)) {
3565            tex_set_special_node_list(align_list_type, n);
3566        } else if (lua_key_eq(s, bestpagebreak)) {
3567            lmt_page_builder_state.best_break = n;
3568        }
3569    }
3570    return 0;
3571}
3572
3573static int texlib_getnestfields(lua_State *L)
3574{
3575    lua_createtable(L, 18, 0);
3576    lua_push_key_at_index(L, delimiter,        1);
3577    lua_push_key_at_index(L, direction,        2);
3578    lua_push_key_at_index(L, head,             3);
3579    lua_push_key_at_index(L, mathdir,          4);
3580    lua_push_key_at_index(L, mathstyle,        5);
3581    lua_push_key_at_index(L, mathscale,        6);
3582    lua_push_key_at_index(L, mathbegin,        7);
3583    lua_push_key_at_index(L, mathend,          8);
3584    lua_push_key_at_index(L, mathmode,         9);
3585    lua_push_key_at_index(L, mathflatten,     10);
3586    lua_push_key_at_index(L, mathmainstyle,   11);
3587    lua_push_key_at_index(L, mathparentstyle, 12);
3588    lua_push_key_at_index(L, modeline,        13);
3589    lua_push_key_at_index(L, noad,            14);
3590    lua_push_key_at_index(L, prevdepth,       15);
3591    lua_push_key_at_index(L, prevgraf,        16);
3592    lua_push_key_at_index(L, spacefactor,     17);
3593    lua_push_key_at_index(L, tail,            18);
3594    return 1;
3595}
3596
3597static void texlib_get_nest_field(lua_State *L, const char *field, list_state_record *r)
3598{
3599
3600    if (lua_key_eq(field, mode)) {
3601        lua_pushinteger(L, r->mode);
3602    } else if (lua_key_eq(field, head) || lua_key_eq(field, list)) {
3603        /* we no longer check for special list nodes here so beware of prev-of-head */
3604        lmt_push_node_fast(L, r->head);
3605    } else if (lua_key_eq(field, tail)) {
3606        /* we no longer check for special list nodes here so beware of next-of-tail */
3607        lmt_push_node_fast(L, r->tail);
3608    } else if (lua_key_eq(field, delimiter)) {
3609        lmt_push_node_fast(L, r->delimiter);
3610    } else if (lua_key_eq(field, prevgraf)) {
3611        lua_pushinteger(L, r->prev_graf);
3612    } else if (lua_key_eq(field, modeline)) {
3613        lua_pushinteger(L, r->mode_line);
3614    } else if (lua_key_eq(field, prevdepth)) {
3615        lua_pushinteger(L, r->prev_depth);
3616    } else if (lua_key_eq(field, spacefactor)) {
3617        lua_pushinteger(L, r->space_factor);
3618    } else if (lua_key_eq(field, noad)) {
3619        lmt_push_node_fast(L, r->incomplete_noad);
3620    } else if (lua_key_eq(field, direction)) {
3621        lmt_push_node_fast(L, r->direction_stack);
3622    } else if (lua_key_eq(field, mathdir)) {
3623        lua_pushinteger(L, r->math_dir);
3624    } else if (lua_key_eq(field, mathstyle)) {
3625        lua_pushinteger(L, r->math_style);
3626    } else if (lua_key_eq(field, mathscale)) {
3627        lua_pushinteger(L, r->math_scale);
3628    } else if (lua_key_eq(field, mathbegin)) {
3629        lua_pushinteger(L, r->math_begin);
3630    } else if (lua_key_eq(field, mathend)) {
3631        lua_pushinteger(L, r->math_end);
3632    } else if (lua_key_eq(field, mathmode)) {
3633        lua_pushinteger(L, r->math_mode);
3634    } else if (lua_key_eq(field, mathflatten)) {
3635        lua_pushinteger(L, r->math_flatten);
3636    } else if (lua_key_eq(field, mathmainstyle)) {
3637        lua_pushinteger(L, r->math_main_style);
3638    } else if (lua_key_eq(field, mathparentstyle)) {
3639        lua_pushinteger(L, r->math_parent_style);
3640    } else {
3641        lua_pushnil(L);
3642    }
3643}
3644
3645static void texlib_set_nest_field(lua_State *L, int n, const char *field, list_state_record *r)
3646{
3647    if (lua_key_eq(field, mode)) {
3648        r->mode = lmt_tointeger(L, n);
3649    } else if (lua_key_eq(field, head) || lua_key_eq(field, list)) {
3650        r->head = lmt_check_isnode(L, n);
3651    } else if (lua_key_eq(field, tail)) {
3652        r->tail = lmt_check_isnode(L, n);
3653    } else if (lua_key_eq(field, delimiter)) {
3654        r->delimiter = lmt_check_isnode(L, n);
3655    } else if (lua_key_eq(field, prevgraf)) {
3656        r->prev_graf = lmt_tointeger(L, n);
3657    } else if (lua_key_eq(field, modeline)) {
3658        r->mode_line = lmt_tointeger(L, n);
3659    } else if (lua_key_eq(field, prevdepth)) {
3660        r->prev_depth = lmt_toroundnumber(L, n);
3661    } else if (lua_key_eq(field, spacefactor)) {
3662        r->space_factor = lmt_toroundnumber(L, n);
3663    } else if (lua_key_eq(field, noad)) {
3664        r->incomplete_noad = lmt_check_isnode(L, n);
3665    } else if (lua_key_eq(field, direction)) {
3666        r->direction_stack = lmt_check_isnode(L, n);
3667    } else if (lua_key_eq(field, mathdir)) {
3668        int d = lmt_tointeger(L, n);
3669        if (d == dir_lefttoright || d  == dir_righttoleft) {
3670            r->math_dir = d;
3671        }
3672    } else if (lua_key_eq(field, mathstyle)) {
3673        int s = lmt_tointeger(L, n);
3674        if (is_valid_math_style(s)) {
3675            r->math_style = s;
3676        }
3677    } else if (lua_key_eq(field, mathscale)) {
3678        r->math_scale = lmt_tointeger(L, n);
3679    } else if (lua_key_eq(field, mathbegin)) {
3680        r->math_begin = lmt_tointeger(L, n);
3681    } else if (lua_key_eq(field, mathend)) {
3682        r->math_end = lmt_tointeger(L, n);
3683    } else if (lua_key_eq(field, mathmode)) {
3684        int m = lmt_tointeger(L, n);
3685        if (m >= first_math_shift_cs_code && m <= last_math_shift_cs_code) {
3686            r->math_mode = m;
3687        }
3688    } else if (lua_key_eq(field, mathflatten)) {
3689        r->math_flatten = lmt_tointeger(L, n);
3690    } else if (lua_key_eq(field, mathmainstyle)) {
3691        int s = lmt_tointeger(L, n);
3692        if (is_valid_math_style(s)) {
3693            r->math_main_style = s;
3694        }
3695    } else if (lua_key_eq(field, mathparentstyle)) {
3696        int s = lmt_tointeger(L, n);
3697        if (is_valid_math_style(s)) {
3698            r->math_parent_style = s;
3699        }
3700    }
3701}
3702
3703static int texlib_aux_nest_getfield(lua_State *L)
3704{
3705    list_state_record **rv = lua_touserdata(L, -2);
3706    list_state_record *r = *rv;
3707    const char *field = lua_tostring(L, -1);
3708    texlib_get_nest_field(L, field, r);
3709    return 1;
3710}
3711
3712static int texlib_aux_nest_setfield(lua_State *L)
3713{
3714    list_state_record **rv = lua_touserdata(L, -3);
3715    list_state_record *r = *rv;
3716    const char *field = lua_tostring(L, -2);
3717    texlib_set_nest_field(L, -1, field, r);
3718    return 0;
3719}
3720
3721static const struct luaL_Reg texlib_nest_metatable[] = {
3722    { "__index",    texlib_aux_nest_getfield },
3723    { "__newindex", texlib_aux_nest_setfield },
3724    { NULL,         NULL                     },
3725};
3726
3727static void texlib_aux_init_nest_lib(lua_State *L)
3728{
3729    luaL_newmetatable(L, TEX_NEST_INSTANCE);
3730    luaL_setfuncs(L, texlib_nest_metatable, 0);
3731    lua_pop(L, 1);
3732}
3733
3734/* getnest(<number>|top|ptr,[fieldname]) */
3735
3736static int texlib_getnestlevel(lua_State *L)
3737{
3738    lua_pushinteger(L, lmt_nest_state.nest_data.ptr);
3739    return 1;
3740}
3741
3742static int texlib_getnest(lua_State *L)
3743{
3744    int p = -1 ;
3745    int t = lua_gettop(L);
3746    if (t == 0) {
3747        p = lmt_nest_state.nest_data.ptr;
3748    } else {
3749        switch (lua_type(L, 1)) {
3750            case LUA_TNUMBER:
3751                {
3752                    int ptr = lmt_tointeger(L, 1);
3753                    if (ptr >= 0 && ptr <= lmt_nest_state.nest_data.ptr) {
3754                        p = ptr;
3755                    }
3756                }
3757                break;
3758            case LUA_TSTRING:
3759                {
3760                    const char *s = lua_tostring(L, 1);
3761                    if (lua_key_eq(s, top)) {
3762                        p = lmt_nest_state.nest_data.ptr;
3763                    } else if (lua_key_eq(s, ptr) || lua_key_eq(s, level)) {
3764                        lua_pushinteger(L, lmt_nest_state.nest_data.ptr);
3765                        return 1;
3766                    }
3767                }
3768                break;
3769        }
3770    }
3771    if (p > -1) {
3772         if (t > 1) {
3773            const char *field = lua_tostring(L, 2);
3774            if (field) {
3775                texlib_get_nest_field(L, field, &lmt_nest_state.nest[p]);
3776            } else {
3777                lua_pushnil(L);
3778            }
3779        } else {
3780            list_state_record **nestitem = lua_newuserdatauv(L, sizeof(list_state_record *), 0);
3781            *nestitem = &lmt_nest_state.nest[p];
3782            luaL_getmetatable(L, TEX_NEST_INSTANCE);
3783            lua_setmetatable(L, -2);
3784        }
3785    } else {
3786        lua_pushnil(L);
3787    }
3788    return 1;
3789}
3790
3791/* setnest(<number>|top,fieldname,value) */
3792
3793static int texlib_setnest(lua_State *L)
3794{
3795    if (lua_gettop(L) > 2) {
3796        int p = -1 ;
3797        switch (lua_type(L, 1)) {
3798            case LUA_TNUMBER:
3799                {
3800                    int ptr = lmt_tointeger(L, 1);
3801                    if (ptr >= 0 && ptr <= lmt_nest_state.nest_data.ptr) {
3802                        p = ptr;
3803                    }
3804                }
3805                break;
3806            case LUA_TSTRING:
3807                {
3808                    const char *s = lua_tostring(L, 1);
3809                    if (lua_key_eq(s, top)) {
3810                        p = lmt_nest_state.nest_data.ptr;
3811                    }
3812                }
3813                break;
3814        }
3815        if (p > -1) {
3816            const char *field = lua_tostring(L, 2);
3817            if (field) {
3818                texlib_set_nest_field(L, 3, field, &lmt_nest_state.nest[p]);
3819            }
3820        }
3821    }
3822    return 0;
3823}
3824
3825static int texlib_round(lua_State *L)
3826{
3827 /* lua_pushinteger(L, lmt_roundedfloat((double) lua_tonumber(L, 1))); */
3828    lua_pushinteger(L, clippedround((double) lua_tonumber(L, 1)));
3829    return 1;
3830}
3831
3832static int texlib_scale(lua_State *L)
3833{
3834    double delta = luaL_checknumber(L, 2);
3835    switch (lua_type(L, 1)) {
3836        case LUA_TTABLE:
3837            {
3838                /*tex
3839                    We could preallocate the table or maybe scale in-place. The
3840                    new table is at index 3.
3841                */
3842                lua_newtable(L);
3843                lua_pushnil(L);
3844                while (lua_next(L, 1)) {
3845                    /*tex We have a numeric value. */
3846                    lua_pushvalue(L, -2);
3847                    lua_insert(L, -2);
3848                    if (lua_type(L, -2) == LUA_TNUMBER) {
3849                        double m = (double) lua_tonumber(L, -1) * delta;
3850                        lua_pop(L, 1);
3851                     /* lua_pushinteger(L, lmt_roundedfloat(m)); */
3852                        lua_pushinteger(L, clippedround(m));
3853                    }
3854                    lua_rawset(L, 3);
3855                }
3856            }
3857            break;
3858        case LUA_TNUMBER:
3859         /* lua_pushinteger(L, lmt_roundedfloat((double) lua_tonumber(L, 1) * delta)); */
3860            lua_pushinteger(L, clippedround((double) lua_tonumber(L, 1) * delta));
3861            break;
3862        default:
3863            lua_pushnil(L);
3864            break;
3865    }
3866    return 1;
3867}
3868
3869/*tex
3870    For compatibility reasons we keep the check for a boolean for a while. For consistency
3871    we now support flags too: |global cs id|.
3872
3873*/
3874
3875static int texlib_definefont(lua_State *L)
3876{
3877    size_t len;
3878    int slot = 1;
3879    int flags = (lua_isboolean(L, slot) && lua_toboolean(L, slot++)) ? add_global_flag(0) : 0;
3880    const char *csname = lua_tolstring(L, slot++, &len);
3881    halfword id = lmt_tohalfword(L, slot++);
3882    int cs = tex_string_locate(csname, len, 1);
3883    lmt_check_for_flags(L, slot, &flags, 1, 1);
3884    tex_define(flags, cs, set_font_cmd, id);
3885    return 0;
3886}
3887
3888static int texlib_hashtokens(lua_State *L)
3889{
3890    int cs = 1;
3891    int nt = 0;
3892    int nx = 0;
3893    int all = lua_toboolean(L, 1);
3894    lua_createtable(L, hash_size, 0); /* way too much */
3895    /* todo: check active characters as these have three bogus bytes in front */
3896    if (all) {
3897        while (cs <= hash_size) {
3898            strnumber s = cs_text(cs);
3899            if (s > 0) {
3900                halfword n = cs_next(cs);
3901                if (n) {
3902                    int mt = 0;
3903                    lua_createtable(L, 2, 0);
3904                    lua_pushstring(L, tex_to_cstring(s));
3905                    ++nt;
3906                    lua_rawseti(L, -2, ++mt);
3907                    while (n) {
3908                        s = cs_text(n);
3909                        if (s) {
3910                            lua_pushstring(L, tex_to_cstring(s));
3911                            lua_rawseti(L, -2, ++mt);
3912                            ++nt;
3913                            ++nx;
3914                        }
3915                        n = cs_next(n);
3916                    }
3917                } else {
3918                    lua_pushstring(L, tex_to_cstring(s));
3919                    ++nt;
3920                }
3921            } else {
3922                lua_pushboolean(L, 0);
3923            }
3924            lua_rawseti(L, -2, cs);
3925            cs++;
3926        }
3927    } else {
3928        while (cs < hash_size) {
3929            strnumber s = cs_text(cs);
3930            if (s > 0) {
3931                halfword n = cs_next(cs);
3932                lua_pushstring(L, tex_to_cstring(s));
3933                lua_rawseti(L, -2, ++nt);
3934                while (n) {
3935                    s = cs_text(n);
3936                    if (s) {
3937                        lua_pushstring(L, tex_to_cstring(s));
3938                        lua_rawseti(L, -2, ++nt);
3939                        ++nx;
3940                    }
3941                    n = cs_next(n);
3942                }
3943            }
3944            cs++;
3945        }
3946    }
3947    lua_pushinteger(L, --cs);
3948    lua_pushinteger(L, nt);
3949    lua_pushinteger(L, nx);
3950    return 4;
3951}
3952
3953static int texlib_primitives(lua_State *L)
3954{
3955    int cs = 0;
3956    int nt = 0;
3957    lua_createtable(L, prim_size, 0);
3958    while (cs < prim_size) {
3959        strnumber s = get_prim_text(cs);
3960        if (s > 0 && (get_prim_origin(cs) != no_command)) {
3961            lua_pushstring(L, tex_to_cstring(s));
3962            lua_rawseti(L, -2, ++nt);
3963        }
3964        cs++;
3965    }
3966    return 1;
3967}
3968
3969static int texlib_extraprimitives(lua_State *L)
3970{
3971    int mask = 0;
3972    int cs = 0;
3973    int nt = 0;
3974    int n = lua_gettop(L);
3975    if (n == 0) {
3976        mask = tex_command + etex_command + luatex_command;
3977    } else {
3978        for (int i = 1; i <= n; i++) {
3979            if (lua_type(L, i) == LUA_TSTRING) {
3980                const char *s = lua_tostring(L, i);
3981                if (lua_key_eq(s, tex)) {
3982                    mask |= tex_command;
3983                } else if (lua_key_eq(s, etex)) {
3984                    mask |= etex_command;
3985                } else if (lua_key_eq(s, luatex)) {
3986                    mask |= luatex_command;
3987                }
3988            }
3989        }
3990    }
3991    lua_createtable(L, prim_size, 0);
3992    while (cs < prim_size) {
3993        strnumber s = get_prim_text(cs);
3994        if (s > 0 && (get_prim_origin(cs) & mask)) {
3995            lua_pushstring(L, tex_to_cstring(s));
3996            lua_rawseti(L, -2, ++nt);
3997        }
3998        cs++;
3999    }
4000    return 1;
4001}
4002
4003static void texlib_aux_enableprimitive(const char *pre, size_t prel, const char *prm)
4004{
4005    strnumber s = tex_maketexstring(prm);
4006    halfword prm_val = tex_prim_lookup(s); /* todo: no need for tex string */
4007    tex_flush_str(s);
4008    if (prm_val != undefined_primitive && get_prim_origin(prm_val) != no_command) {
4009        char *newprm;
4010        size_t newlen;
4011        halfword cmd = get_prim_eq_type(prm_val);
4012        halfword chr = get_prim_equiv(prm_val);
4013        if (strncmp(pre, prm, prel) != 0) {
4014            /* not a prefix */
4015            newlen = strlen(prm) + prel;
4016            newprm = (char *) lmt_memory_malloc((size_t) newlen + 1);
4017            if (newprm) {
4018                strcpy(newprm, pre);
4019                strcat(newprm + prel, prm);
4020            } else {
4021                tex_overflow_error("primitives", (int) newlen + 1);
4022            }
4023        } else {
4024            newlen = strlen(prm);
4025            newprm = (char *) lmt_memory_malloc((size_t) newlen + 1);
4026            if (newprm) {
4027                strcpy(newprm, prm);
4028            } else {
4029                tex_overflow_error("primitives", (int) newlen + 1);
4030            }
4031        }
4032        if (newprm) {
4033            halfword val = tex_string_locate(newprm, newlen, 1);
4034            if (val == undefined_control_sequence || eq_type(val) == undefined_cs_cmd) {
4035                tex_primitive_def(newprm, newlen, (singleword) cmd, chr);
4036            }
4037            lmt_memory_free(newprm);
4038        }
4039    }
4040}
4041
4042static int texlib_enableprimitives(lua_State *L)
4043{
4044    if (lua_gettop(L) == 2) {
4045        size_t prelen;
4046        const char *prefix = luaL_checklstring(L, 1, &prelen);
4047        switch (lua_type(L, 2)) {
4048            case LUA_TTABLE:
4049                {
4050                    int i = 1;
4051                    while (1) {
4052                        if (lua_rawgeti(L, 2, i) == LUA_TSTRING) {
4053                            const char *primitive = lua_tostring(L, 3);
4054                            texlib_aux_enableprimitive(prefix, prelen, primitive);
4055                        } else {
4056                            lua_pop(L, 1);
4057                            break;
4058                        }
4059                        lua_pop(L, 1);
4060                        i++;
4061                    }
4062                }
4063                break;
4064            case LUA_TBOOLEAN:
4065                if (lua_toboolean(L, 2)) {
4066                    for (int cs = 0; cs < prim_size; cs++) {
4067                        strnumber s = get_prim_text(cs);
4068                        if (s > 0) {
4069                            const char *primitive = tex_to_cstring(s);
4070                            texlib_aux_enableprimitive(prefix, prelen, primitive);
4071                        }
4072                    }
4073                }
4074                break;
4075            default:
4076                luaL_error(L, "array of names or 'true' expected");
4077        }
4078    } else {
4079        luaL_error(L, "wrong number of arguments");
4080    }
4081    return 0;
4082}
4083
4084/*tex penalties */
4085
4086static halfword texlib_todemerits(lua_State *L, int i, quarterword s)
4087{
4088    /* maybe some day */
4089    (void) *L;
4090    (void) i;
4091    (void) s;
4092    return 0;
4093}
4094
4095static halfword texlib_topenalties(lua_State *L, int i, quarterword s)
4096{
4097    int n = 0;
4098    lua_pushnil(L);
4099    while (lua_next(L, i)) {
4100        n++;
4101        lua_pop(L, 1);
4102    }
4103    if (n > 0) {
4104        int j = 0;
4105        halfword p = tex_new_specification_node(n, s, 0); /* todo: repeat */
4106        lua_pushnil(L);
4107        while (lua_next(L, i)) {
4108            j++;
4109            if (lua_type(L, -1) == LUA_TNUMBER) {
4110                tex_set_specification_penalty(p, j, lmt_tohalfword(L, -1));
4111            }
4112            lua_pop(L, 1);
4113        }
4114        return p;
4115    } else {
4116        return null;
4117    }
4118}
4119
4120static halfword texlib_toparpasses(lua_State *L, int i)
4121{
4122    /* maybe some day */
4123    (void) *L;
4124    (void) i;
4125    return 0;
4126}
4127
4128static halfword texlib_tobalancepasses(lua_State *L, int i)
4129{
4130    /* maybe some day */
4131    (void) *L;
4132    (void) i;
4133    return 0;
4134}
4135
4136/*tex We should check for proper glue spec nodes ... todo. */
4137
4138# define get_dimension_par(P,A,B) \
4139    lua_push_key(A); \
4140    P = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : B; \
4141    lua_pop(L, 1);
4142
4143# define get_glue_par(P,A,B) \
4144    lua_push_key(A); \
4145    P = (lua_rawget(L, -2) == LUA_TUSERDATA) ? lmt_check_isnode(L, -1) : B; \
4146    lua_pop(L, 1);
4147
4148# define get_integer_par(P,A,B) \
4149    lua_push_key(A); \
4150    P = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_tohalfword(L, -1) : B; \
4151    lua_pop(L, 1);
4152
4153# define get_penalties_par(P,A,B,C) \
4154    lua_push_key(A); \
4155    P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_topenalties(L, lua_gettop(L), C) : B; \
4156    lua_pop(L, 1);
4157
4158# define get_par_passes_par(P,A,B) \
4159    lua_push_key(A); \
4160    P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_toparpasses(L, lua_gettop(L)) : B; \
4161    lua_pop(L, 1);
4162
4163# define get_balance_passes_par(P,A,B) \
4164    lua_push_key(A); \
4165    P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_tobalancepasses(L, lua_gettop(L)) : B; \
4166    lua_pop(L, 1);
4167
4168# define get_demerits_par(P,A,B,C) \
4169    lua_push_key(A); \
4170    P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_todemerits(L, lua_gettop(L), C) : B; \
4171    lua_pop(L, 1);
4172
4173# define get_shape_par(P,A,B) \
4174    lua_push_key(A); \
4175    P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_toparshape(L, lua_gettop(L)) : B; \
4176    lua_pop(L, 1);
4177
4178# define get_balance_shape(P,A,B) \
4179    lua_push_key(A); \
4180    P = (lua_rawget(L, -2) == LUA_TTABLE) ? texlib_tobalanceshape(L, lua_gettop(L)) : B; \
4181    lua_pop(L, 1);
4182
4183/*tex
4184    The next function needs to be kept in sync with the regular linebreak handler, wrt the special
4185    skips. This one can be called from within the callback so then we already have intialized.
4186*/
4187
4188/* par leftinit rightinit leftindent ... leftfill rightfill */
4189
4190static int texlib_getlinebreakparameterfields(lua_State *L)
4191{
4192    lua_createtable(L, 61, 0);
4193    lua_push_key_at_index(L, direction,              1);
4194    lua_push_key_at_index(L, tracingparagraphs,      2);
4195    lua_push_key_at_index(L, tracingfitness ,        3);
4196    lua_push_key_at_index(L, tracingpasses,          4);
4197    lua_push_key_at_index(L, pretolerance,           5);
4198    lua_push_key_at_index(L, tolerance,              6);
4199    lua_push_key_at_index(L, emergencystretch,       7);
4200    lua_push_key_at_index(L, emergencyextrastretch,  8);
4201    lua_push_key_at_index(L, looseness,              9);
4202    lua_push_key_at_index(L, adjustspacing,         10);
4203    lua_push_key_at_index(L, protrudechars,         11);
4204    lua_push_key_at_index(L, adjdemerits,           12);
4205    lua_push_key_at_index(L, linepenalty,           13);
4206    lua_push_key_at_index(L, lastlinefit,           14);
4207    lua_push_key_at_index(L, doublehyphendemerits,  15);
4208    lua_push_key_at_index(L, finalhyphendemerits,   16);
4209    lua_push_key_at_index(L, hsize,                 17);
4210    lua_push_key_at_index(L, leftskip,              18);
4211    lua_push_key_at_index(L, rightskip,             19);
4212    lua_push_key_at_index(L, emergencyleftskip,     20);
4213    lua_push_key_at_index(L, emergencyrightskip,    21);
4214    lua_push_key_at_index(L, hangindent,            22);
4215    lua_push_key_at_index(L, hangafter,             23);
4216    lua_push_key_at_index(L, interlinepenalty,      24);
4217    lua_push_key_at_index(L, clubpenalty,           25);
4218    lua_push_key_at_index(L, widowpenalty,          26);
4219    lua_push_key_at_index(L, displaywidowpenalty,   27);
4220    lua_push_key_at_index(L, toddlerpenalties,      28);
4221    lua_push_key_at_index(L, lefttwindemerits,      29);
4222    lua_push_key_at_index(L, righttwindemerits,     30);
4223    lua_push_key_at_index(L, singlelinepenalty,     31);
4224    lua_push_key_at_index(L, hyphenpenalty,         32);
4225    lua_push_key_at_index(L, exhyphenpenalty,       33);
4226    lua_push_key_at_index(L, brokenpenalty,         34);
4227    lua_push_key_at_index(L, baselineskip,          35);
4228    lua_push_key_at_index(L, lineskip,              36);
4229    lua_push_key_at_index(L, lineskiplimit,         37);
4230    lua_push_key_at_index(L, adjustspacing,         38);
4231    lua_push_key_at_index(L, adjustspacingstep,     39);
4232    lua_push_key_at_index(L, adjustspacingshrink,   40);
4233    lua_push_key_at_index(L, adjustspacingstretch,  41);
4234    lua_push_key_at_index(L, hyphenationmode,       42);
4235    lua_push_key_at_index(L, shapingpenaltiesmode,  43);
4236    lua_push_key_at_index(L, shapingpenalty,        44);
4237    lua_push_key_at_index(L, parshape,              45);
4238    lua_push_key_at_index(L, interlinepenalties,    46);
4239    lua_push_key_at_index(L, clubpenalties,         47);
4240    lua_push_key_at_index(L, widowpenalties,        48);
4241    lua_push_key_at_index(L, displaywidowpenalties, 49);
4242    lua_push_key_at_index(L, brokenpenalties,       50);
4243    lua_push_key_at_index(L, orphanpenalties,       51);
4244    lua_push_key_at_index(L, fitnessclasses,        52);
4245    lua_push_key_at_index(L, adjacentdemerits,      53);
4246    lua_push_key_at_index(L, orphanlinefactors,     54);
4247    lua_push_key_at_index(L, parpasses,             55);
4248    lua_push_key_at_index(L, linebreakchecks,       56);
4249    lua_push_key_at_index(L, linebreakoptional,     57);
4250    lua_push_key_at_index(L, parfillleftskip,       58);
4251    lua_push_key_at_index(L, parfillrightskip,      59);
4252    lua_push_key_at_index(L, parinitleftskip,       60);
4253    lua_push_key_at_index(L, parinitrightskip,      61);
4254    return 1;
4255}
4256
4257static int texlib_getlinebreakresultfields(lua_State *L)
4258{
4259    lua_createtable(L, 4, 0);
4260    lua_push_key_at_index(L, demerits,  1);
4261    lua_push_key_at_index(L, looseness, 2);
4262    lua_push_key_at_index(L, prevdepth, 3);
4263    lua_push_key_at_index(L, prevgraf,  4);
4264    return 1;
4265}
4266
4267static int texlib_preparelinebreak(lua_State *L)
4268{
4269    halfword direct;
4270    halfword par = lmt_check_isdirectornode(L, 1, &direct);
4271    if (node_type(par) == par_node) { /* maybe check for h|v subtype */
4272        halfword tail = tex_tail_of_node_list(par);
4273        if (node_type(tail) == glue_node && node_subtype(tail) == par_fill_right_skip_glue) {
4274            tex_formatted_warning("linebreak", "list seems already prepared");
4275        } else {
4276            halfword parinit_left_skip_glue = null;
4277            halfword parinit_right_skip_glue = null;
4278            halfword parfill_left_skip_glue = null;
4279            halfword parfill_right_skip_glue = null;
4280            halfword final_line_penalty = null;
4281            tex_line_break_prepare(par, &tail, &parinit_left_skip_glue, &parinit_right_skip_glue, &parfill_left_skip_glue, &parfill_right_skip_glue, &final_line_penalty);
4282            lmt_push_directornode(L, par, direct);
4283            lmt_push_directornode(L, tail, direct);
4284            lmt_push_directornode(L, parinit_left_skip_glue, direct);
4285            lmt_push_directornode(L, parinit_right_skip_glue, direct);
4286            lmt_push_directornode(L, parfill_left_skip_glue , direct);
4287            lmt_push_directornode(L, parfill_right_skip_glue, direct);
4288         /* lmt_push_directornode(L, final_line_penalty, direct); */ /*tex Not that relevant to know. */
4289            return 6;
4290        }
4291    }
4292    lua_pushnil(L);
4293    return 1;
4294}
4295
4296static int texlib_linebreak(lua_State *L)
4297{
4298 // halfword par = lmt_check_isnode(L, 1);
4299    halfword direct;
4300    halfword par = lmt_check_isdirectornode(L, 1, &direct);
4301    if (node_type(par) == par_node) { /* maybe check for h|v subtype */
4302        line_break_properties properties;
4303        halfword tail = par;
4304        halfword has_indent = null;
4305        halfword has_penalty = 0;
4306        halfword prepared = 0;
4307        properties.initial_par = par;
4308        properties.group_context = lua_group;
4309        properties.par_context = lua_par_context;
4310        properties.paragraph_dir = par_dir(par);
4311        properties.paragraph_options = par_options(par);
4312        properties.parfill_left_skip = null;
4313        properties.parfill_right_skip = null;
4314        properties.parinit_left_skip = null;
4315        properties.parinit_right_skip = null;
4316        properties.extra_hyphen_penalty = 0;
4317        properties.emergency_original = 0;
4318        properties.math_penalty_factor = 0;
4319        properties.sf_factor = 0;
4320        properties.sf_stretch_factor = 0;
4321        while (tail) {
4322            switch (node_type(tail)) {
4323                case glue_node:
4324                    switch (node_subtype(tail)) {
4325                        case indent_skip_glue:
4326                            if (has_indent) {
4327                                tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "indent");
4328                                goto NOTHING;
4329                            } else {
4330                                has_indent = 1;
4331                            }
4332                            break;
4333                        case par_fill_left_skip_glue:
4334                            if (properties.parfill_left_skip) {
4335                                tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "leftskip");
4336                                goto NOTHING;
4337                            } else {
4338                                properties.parfill_left_skip = tail;
4339                            }
4340                            break;
4341                        case par_fill_right_skip_glue:
4342                            if (properties.parfill_right_skip) {
4343                                tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "rightskip");
4344                                goto NOTHING;
4345                            } else {
4346                                properties.parfill_right_skip = tail;
4347                            }
4348                            break;
4349                        case par_init_left_skip_glue:
4350                            if (properties.parinit_left_skip) {
4351                                tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "leftinit");
4352                                goto NOTHING;
4353                            } else {
4354                                properties.parinit_left_skip = tail;
4355                            }
4356                            break;
4357                        case par_init_right_skip_glue:
4358                            if (properties.parinit_right_skip) {
4359                                tex_formatted_warning("linebreak", "duplicate %s glue in tex.linebreak", "rightinit");
4360                                goto NOTHING;
4361                            } else {
4362                                properties.parinit_right_skip = tail;
4363                            }
4364                            break;
4365                    }
4366                    break;
4367                case penalty_node:
4368                    if (node_subtype(tail) == line_penalty_subtype && penalty_amount(tail) == infinite_penalty && ! (properties.parfill_left_skip && properties.parfill_right_skip)) {
4369                        has_penalty = 1;
4370                    }
4371            }
4372            if (node_next(tail)) {
4373                tail = node_next(tail);
4374            } else {
4375                break;
4376            }
4377        }
4378        {
4379            int has_init = properties.parinit_left_skip && properties.parinit_right_skip;
4380            int has_fill = properties.parfill_left_skip && properties.parfill_right_skip;
4381            if (lmt_linebreak_state.calling_back) {
4382                if (has_indent && ! (has_init && has_fill && has_penalty)) {
4383                    tex_formatted_warning("linebreak", "[ par + leftinit + rightinit + indentglue + ... + penalty + leftfill + righfill ] expected");
4384                    goto NOTHING;
4385                } else if (! (has_fill && has_penalty)) {
4386                    tex_formatted_warning("linebreak", "[ par + indentbox + ... + penalty + leftfill + righfill ] expected");
4387                    goto NOTHING;
4388                } else {
4389                    prepared = 1;
4390                }
4391            } else {
4392                if (! (has_indent && has_init && has_fill)) {
4393                    tex_formatted_warning("linebreak", "[ leftinit | rightinit | leftfill | rigthfill ] expected");
4394                    goto NOTHING;
4395                } else {
4396                 // prepared = 0;
4397                    prepared = has_init && has_fill;
4398                }
4399            }
4400        }
4401        tex_push_nest();
4402        node_next(temp_head) = par;
4403        /*tex initialize local parameters */
4404        if (lua_gettop(L) != 2 || lua_type(L, 2) != LUA_TTABLE) {
4405            lua_newtable(L);
4406        }
4407        lua_push_key(direction);
4408        if (lua_rawget(L, -2) == LUA_TNUMBER) {
4409            properties.paragraph_dir = checked_direction_value(lmt_tointeger(L, -1));
4410        }
4411        lua_pop(L, 1);
4412        lua_push_key(options);
4413        if (lua_rawget(L, -2) == LUA_TNUMBER) {
4414            properties.paragraph_options = (singleword) lmt_tointeger(L, -1);
4415        }
4416        lua_pop(L, 1);
4417        get_integer_par   (properties.tracing_paragraphs,           tracingparagraphs,         tracing_paragraphs_par);
4418        get_integer_par   (properties.tracing_fitness,              tracingfitness ,           tracing_fitness_par);
4419        get_integer_par   (properties.tracing_passes,               tracingpasses,             tracing_passes_par);
4420        get_integer_par   (properties.pretolerance,                 pretolerance,              tex_get_par_par(par, par_pre_tolerance_code));
4421        get_integer_par   (properties.tolerance,                    tolerance,                 tex_get_par_par(par, par_tolerance_code));
4422        get_dimension_par (properties.emergency_stretch,            emergencystretch,          tex_get_par_par(par, par_emergency_stretch_code));
4423        get_dimension_par (properties.emergency_extra_stretch,      emergencyextrastretch,     tex_get_par_par(par, par_emergency_extra_stretch_code));
4424        get_integer_par   (properties.looseness,                    looseness,                 tex_get_par_par(par, par_looseness_code));
4425        get_integer_par   (properties.adjust_spacing,               adjustspacing,             tex_get_par_par(par, par_adjust_spacing_code));
4426        get_integer_par   (properties.protrude_chars,               protrudechars,             tex_get_par_par(par, par_protrude_chars_code));
4427        get_integer_par   (properties.adj_demerits,                 adjdemerits,               tex_get_par_par(par, par_adj_demerits_code));
4428        get_integer_par   (properties.line_penalty,                 linepenalty,               tex_get_par_par(par, par_line_penalty_code));
4429        get_integer_par   (properties.last_line_fit,                lastlinefit,               tex_get_par_par(par, par_last_line_fit_code));
4430        get_integer_par   (properties.double_hyphen_demerits,       doublehyphendemerits,      tex_get_par_par(par, par_double_hyphen_demerits_code));
4431        get_integer_par   (properties.final_hyphen_demerits,        finalhyphendemerits,       tex_get_par_par(par, par_final_hyphen_demerits_code));
4432        get_dimension_par (properties.hsize,                        hsize,                     tex_get_par_par(par, par_hsize_code));
4433        get_glue_par      (properties.left_skip,                    leftskip,                  tex_get_par_par(par, par_left_skip_code));
4434        get_glue_par      (properties.right_skip,                   rightskip,                 tex_get_par_par(par, par_right_skip_code));
4435        get_glue_par      (properties.emergency_left_skip,          emergencyleftskip,         tex_get_par_par(par, par_emergency_left_skip_code));
4436        get_glue_par      (properties.emergency_right_skip,         emergencyrightskip,        tex_get_par_par(par, par_emergency_right_skip_code));
4437        get_dimension_par (properties.hang_indent,                  hangindent,                tex_get_par_par(par, par_hang_indent_code));
4438        get_integer_par   (properties.hang_after,                   hangafter,                 tex_get_par_par(par, par_hang_after_code));
4439        get_integer_par   (properties.inter_line_penalty,           interlinepenalty,          tex_get_par_par(par, par_inter_line_penalty_code));
4440        get_integer_par   (properties.club_penalty,                 clubpenalty,               tex_get_par_par(par, par_club_penalty_code));
4441        get_integer_par   (properties.widow_penalty,                widowpenalty,              tex_get_par_par(par, par_widow_penalty_code));
4442        get_integer_par   (properties.display_widow_penalty,        displaywidowpenalty,       tex_get_par_par(par, par_display_widow_penalty_code));
4443        get_integer_par   (properties.toddler_penalties,            toddlerpenalties,          tex_get_par_par(par, par_toddler_penalties_code));
4444        get_integer_par   (properties.left_twin_demerits,           lefttwindemerits,          tex_get_par_par(par, par_left_twin_demerits_code));
4445        get_integer_par   (properties.right_twin_demerits,          righttwindemerits,         tex_get_par_par(par, par_right_twin_demerits_code));
4446        get_integer_par   (properties.single_line_penalty,          singlelinepenalty,         tex_get_par_par(par, par_single_line_penalty_code));
4447        get_integer_par   (properties.hyphen_penalty,               hyphenpenalty,             tex_get_par_par(par, par_hyphen_penalty_code));
4448        get_integer_par   (properties.ex_hyphen_penalty,            exhyphenpenalty,           tex_get_par_par(par, par_ex_hyphen_penalty_code));
4449        get_integer_par   (properties.broken_penalty,               brokenpenalty,             tex_get_par_par(par, par_broken_penalty_code));
4450        get_glue_par      (properties.baseline_skip,                baselineskip,              tex_get_par_par(par, par_baseline_skip_code));
4451        get_glue_par      (properties.line_skip,                    lineskip,                  tex_get_par_par(par, par_line_skip_code));
4452        get_dimension_par (properties.line_skip_limit,              lineskiplimit,             tex_get_par_par(par, par_line_skip_limit_code));
4453        get_integer_par   (properties.adjust_spacing,               adjustspacing,             tex_get_par_par(par, par_adjust_spacing_code));
4454        get_integer_par   (properties.adjust_spacing_step,          adjustspacingstep,         tex_get_par_par(par, par_adjust_spacing_step_code));
4455        get_integer_par   (properties.adjust_spacing_shrink,        adjustspacingshrink,       tex_get_par_par(par, par_adjust_spacing_shrink_code));
4456        get_integer_par   (properties.adjust_spacing_stretch,       adjustspacingstretch,      tex_get_par_par(par, par_adjust_spacing_stretch_code));
4457        get_integer_par   (properties.hyphenation_mode,             hyphenationmode,           tex_get_par_par(par, par_hyphenation_mode_code));
4458        get_integer_par   (properties.shaping_penalties_mode,       shapingpenaltiesmode,      tex_get_par_par(par, par_shaping_penalties_mode_code));
4459        get_integer_par   (properties.shaping_penalty,              shapingpenalty,            tex_get_par_par(par, par_shaping_penalty_code));
4460        get_shape_par     (properties.par_shape,                    parshape,                  tex_get_par_par(par, par_par_shape_code));
4461        get_penalties_par (properties.inter_line_penalties,         interlinepenalties,        tex_get_par_par(par, par_inter_line_penalties_code), inter_line_penalties_code);
4462        get_penalties_par (properties.club_penalties,               clubpenalties,             tex_get_par_par(par, par_club_penalties_code), club_penalties_code);
4463        get_penalties_par (properties.widow_penalties,              widowpenalties,            tex_get_par_par(par, par_widow_penalties_code), widow_penalties_code);
4464        get_penalties_par (properties.display_widow_penalties,      displaywidowpenalties,     tex_get_par_par(par, par_display_widow_penalties_code), display_widow_penalties_code);
4465        get_penalties_par (properties.broken_penalties,             brokenpenalties,           tex_get_par_par(par, par_broken_penalties_code), broken_penalties_code);
4466        get_penalties_par (properties.orphan_penalties,             orphanpenalties,           tex_get_par_par(par, par_orphan_penalties_code), orphan_penalties_code);
4467        get_demerits_par  (properties.fitness_classes,              fitnessclasses,            tex_get_par_par(par, par_fitness_classes_code), fitness_classes_code);
4468        get_demerits_par  (properties.adjacent_demerits,            adjacentdemerits,          tex_get_par_par(par, par_adjacent_demerits_code), adjacent_demerits_code);
4469        get_demerits_par  (properties.orphan_line_factors,          orphanlinefactors,         tex_get_par_par(par, par_orphan_line_factors_code), orphan_line_factors_code);
4470        get_par_passes_par(properties.par_passes,                   parpasses,                 line_break_passes_par > 0 ? tex_get_par_par(par, par_par_passes_code) : null);
4471        get_integer_par   (properties.line_break_checks,            linebreakchecks,           tex_get_par_par(par, par_line_break_checks_code));
4472        get_integer_par   (properties.line_break_optional,          linebreakoptional,         line_break_optional_par); /* hm */
4473        if (! prepared) {
4474            halfword attr_template = tail;
4475            halfword final_line_penalty = tex_new_penalty_node(infinite_penalty, line_penalty_subtype);
4476            /* */
4477            get_glue_par(properties.parfill_left_skip,  parfillleftskip,  tex_get_par_par(par, par_par_fill_left_skip_code));
4478            get_glue_par(properties.parfill_right_skip, parfillrightskip, tex_get_par_par(par, par_par_fill_right_skip_code));
4479            get_glue_par(properties.parinit_left_skip,  parinitleftskip,  tex_get_par_par(par, par_par_init_left_skip_code));
4480            get_glue_par(properties.parinit_right_skip, parinitrightskip, tex_get_par_par(par, par_par_init_right_skip_code));
4481            /* */
4482            properties.parfill_left_skip = tex_new_glue_node(properties.parfill_left_skip, par_fill_left_skip_glue);
4483            properties.parfill_right_skip = tex_new_glue_node(properties.parfill_right_skip, par_fill_right_skip_glue);
4484            tex_attach_attribute_list_copy(final_line_penalty, attr_template);
4485            tex_attach_attribute_list_copy(properties.parfill_left_skip, attr_template);
4486            tex_attach_attribute_list_copy(properties.parfill_right_skip, attr_template);
4487            tex_couple_nodes(tail, final_line_penalty);
4488            tex_couple_nodes(final_line_penalty, properties.parfill_left_skip);
4489            tex_couple_nodes(properties.parfill_left_skip, properties.parfill_right_skip);
4490            if (node_next(par)) { /* test can go, also elsewhere */
4491                 halfword n = node_next(par);
4492                 while (n) {
4493                     if (node_type(n) == glue_node && node_subtype(n) == indent_skip_glue) {
4494                         properties.parinit_left_skip = tex_new_glue_node(properties.parinit_left_skip, par_init_left_skip_glue);
4495                         properties.parinit_right_skip = tex_new_glue_node(properties.parinit_right_skip, par_init_right_skip_glue);
4496                         tex_attach_attribute_list_copy(properties.parinit_left_skip, attr_template); // maybe head .. also elsewhere
4497                         tex_attach_attribute_list_copy(properties.parinit_right_skip, attr_template); // maybe head .. also elsewhere
4498                         tex_try_couple_nodes(properties.parinit_right_skip, n);
4499                         tex_try_couple_nodes(properties.parinit_left_skip, properties.parinit_right_skip);
4500                         tex_try_couple_nodes(par, properties.parinit_left_skip);
4501                         break;
4502                     } else {
4503                         n = node_next(n);
4504                     }
4505                 }
4506             }
4507        }
4508        lmt_linebreak_state.last_line_fill = properties.parfill_right_skip; /*tex I need to redo this. */
4509        tex_do_line_break(&properties);
4510        {
4511            halfword fewest_demerits = 0;
4512            halfword actual_looseness = 0;
4513            /*tex return the generated list, and its prevdepth */
4514            tex_get_linebreak_info(&fewest_demerits, &actual_looseness) ;
4515            lmt_push_directornode(L, node_next(cur_list.head), direct);
4516            lua_createtable(L, 0, 4);
4517            /* set_integer_by_key(L, demerits, fewest_demerits); */
4518            lua_push_key(demerits);
4519            lua_pushinteger(L, fewest_demerits);
4520            lua_settable(L, -3);
4521            /* set_integer_by_key(L, looseness, actual_looseness); */
4522            lua_push_key(looseness);
4523            lua_pushinteger(L, actual_looseness);
4524            lua_settable(L, -3);
4525            /* set_integer_by_key(L, prevdepth, cur_list.prev_depth); */
4526            lua_push_key(prevdepth);
4527            lua_pushinteger(L, cur_list.prev_depth);
4528            lua_settable(L, -3);
4529            /* set_integer_by_key(L, prevgraf, cur_list.prev_graf); */
4530            lua_push_key(prevgraf);
4531            lua_pushinteger(L, cur_list.prev_graf);
4532            lua_settable(L, -3);
4533        }
4534        tex_pop_nest();
4535        if (properties.par_shape               != tex_get_par_par(par, par_par_shape_code))               { tex_flush_node(properties.par_shape); }
4536        if (properties.inter_line_penalties    != tex_get_par_par(par, par_inter_line_penalties_code))    { tex_flush_node(properties.inter_line_penalties); }
4537        if (properties.club_penalties          != tex_get_par_par(par, par_club_penalties_code))          { tex_flush_node(properties.club_penalties); }
4538        if (properties.widow_penalties         != tex_get_par_par(par, par_widow_penalties_code))         { tex_flush_node(properties.widow_penalties); }
4539        if (properties.display_widow_penalties != tex_get_par_par(par, par_display_widow_penalties_code)) { tex_flush_node(properties.display_widow_penalties); }
4540        if (properties.broken_penalties        != tex_get_par_par(par, par_broken_penalties_code))        { tex_flush_node(properties.broken_penalties); }
4541        if (properties.orphan_penalties        != tex_get_par_par(par, par_orphan_penalties_code))        { tex_flush_node(properties.orphan_penalties); }
4542        if (properties.fitness_classes         != tex_get_par_par(par, par_fitness_classes_code))         { tex_flush_node(properties.fitness_classes); }
4543        if (properties.adjacent_demerits       != tex_get_par_par(par, par_adjacent_demerits_code))       { tex_flush_node(properties.adjacent_demerits); }
4544        if (properties.orphan_line_factors     != tex_get_par_par(par, par_orphan_line_factors_code))     { tex_flush_node(properties.orphan_line_factors); }
4545        return 2;
4546    } else {
4547        tex_formatted_warning("linebreak", "[ par ... ] expected");
4548    }
4549  NOTHING:
4550    lmt_push_directornode(L, par, direct);
4551    return 1;
4552}
4553
4554static int texlib_getbalanceparameterfields(lua_State *L)
4555{
4556    lua_createtable(L, 17, 0);
4557    lua_push_key_at_index(L, tracingbalancing,  1);
4558    lua_push_key_at_index(L, tracingfitness ,   2);
4559    lua_push_key_at_index(L, tracingpasses,     3);
4560    lua_push_key_at_index(L, pretolerance,      4);
4561    lua_push_key_at_index(L, tolerance,         5);
4562    lua_push_key_at_index(L, emergencystretch,  6);
4563    lua_push_key_at_index(L, emergencyshrink,   7);
4564    lua_push_key_at_index(L, looseness,         8);
4565    lua_push_key_at_index(L, adjdemerits,       9);
4566    lua_push_key_at_index(L, vsize,            10);
4567    lua_push_key_at_index(L, topskip,          11);
4568    lua_push_key_at_index(L, bottomskip,       12);
4569    lua_push_key_at_index(L, shape,            13);
4570    lua_push_key_at_index(L, penalty,          14);
4571    lua_push_key_at_index(L, passes,           15);
4572    lua_push_key_at_index(L, checks,           16);
4573    lua_push_key_at_index(L, packing,          17);
4574    return 1;
4575}
4576
4577static int texlib_getbalanceresultfields(lua_State *L)
4578{
4579    lua_createtable(L, 4, 0);
4580 // lua_push_key_at_index(L, demerits,  1);
4581 // lua_push_key_at_index(L, looseness, 2);
4582    return 1;
4583}
4584
4585static int texlib_balance(lua_State *L)
4586{
4587    halfword direct;
4588    halfword head = lmt_check_isdirectornode(L, 1, &direct);
4589    if (head) {
4590        balance_properties properties;
4591        /* */
4592        tex_push_nest();
4593        node_next(temp_head) = head;
4594        tex_balance_preset(&properties);
4595        /* */
4596        if (lua_gettop(L) != 2 || lua_type(L, 2) != LUA_TTABLE) {
4597            lua_newtable(L);
4598        }
4599        get_integer_par       (properties.tracing_balancing, tracingbalancing, properties.tracing_balancing);
4600        get_integer_par       (properties.tracing_fitness,   tracingfitness ,  properties.tracing_fitness);
4601        get_integer_par       (properties.tracing_passes,    tracingpasses,    properties.tracing_passes);
4602        get_integer_par       (properties.pretolerance,      pretolerance,     properties.pretolerance);
4603        get_integer_par       (properties.tolerance,         tolerance,        properties.tolerance);
4604        get_dimension_par     (properties.emergency_stretch, emergencystretch, properties.emergency_stretch);
4605        get_dimension_par     (properties.emergency_shrink,  emergencyshrink,  properties.emergency_shrink);
4606        get_integer_par       (properties.looseness,         looseness,        properties.looseness);
4607        get_integer_par       (properties.adj_demerits,      adjdemerits,      properties.adj_demerits);
4608        get_dimension_par     (properties.vsize,             vsize,            properties.vsize);
4609        get_glue_par          (properties.topskip,           topskip,          properties.topskip);
4610        get_glue_par          (properties.bottomskip,        bottomskip,       properties.bottomskip);
4611        get_balance_shape     (properties.shape,             shape,            null);
4612        get_integer_par       (properties.penalty,           penalty,          properties.penalty);
4613        get_balance_passes_par(properties.passes,            passes,           balance_break_passes_par > 0 ? balance_passes_par : null);
4614        get_integer_par       (properties.checks,            checks,           properties.checks);
4615        get_integer_par       (properties.packing,           packing,          properties.packing);
4616        /* */
4617        tex_balance(&properties, head);
4618        /* */
4619        {
4620            halfword current = node_next(cur_list.head);
4621            halfword count = 0;
4622         // halfword fewest_demerits = 0;
4623         // halfword actual_looseness = 0;
4624         // tex_get_linebreak_info(&fewest_demerits, &actual_looseness) ;
4625            lua_createtable(L, 0, 0); /* we can have a count */
4626            while (current) { 
4627                halfword upcoming = node_next(current);
4628                node_prev(current) = null;
4629                node_next(current) = null;
4630                lmt_push_directornode(L, current, direct);
4631                lua_rawseti(L, -2, ++count);
4632                current = upcoming;
4633            }
4634            node_next(cur_list.head) = null;
4635            lua_createtable(L, 0, 4);
4636         // lua_push_key(demerits);
4637         // lua_pushinteger(L, fewest_demerits);
4638         // lua_settable(L, -3);
4639         // lua_push_key(looseness);
4640         // lua_pushinteger(L, actual_looseness);
4641         // lua_settable(L, -3);
4642        }
4643        /* */
4644        tex_balance_reset(&properties);
4645        /* */
4646        node_next(temp_head) = null;
4647        tex_pop_nest();
4648        return 2; /* for now empty table */
4649    } else {
4650        lmt_push_directornode(L, head, direct);
4651        return 1;
4652    }
4653}
4654
4655static int texlib_resetparagraph(lua_State *L)
4656{
4657    (void) L;
4658    tex_normal_paragraph(reset_par_context);
4659    return 0;
4660}
4661
4662static int texlib_shipout(lua_State *L)
4663{
4664    int boxnum = lmt_get_box_id(L, 1, 1);
4665    if (box_register(boxnum)) {
4666        tex_flush_node_list(box_register(boxnum));
4667        box_register(boxnum) = null;
4668    }
4669    return 0;
4670}
4671
4672static int texlib_badness(lua_State *L)
4673{
4674    scaled t = lmt_roundnumber(L, 1);
4675    scaled s = lmt_roundnumber(L, 2);
4676    lua_pushinteger(L, tex_badness(t, s));
4677    return 1;
4678}
4679
4680static int texlib_showcontext(lua_State *L)
4681{
4682    (void) L;
4683    tex_show_context();
4684    return 0;
4685}
4686
4687/*tex
4688    When we pass |true| the page builder will only be invoked in the main vertical list in which
4689    case |lmt_nest_state.nest_data.ptr == 1| or |cur_list.mode != vmode|.
4690*/
4691
4692static int texlib_getpagestatevalues(lua_State *L)
4693{
4694    lua_createtable(L, 3, 1);
4695    lua_push_key_at_index(L, none,   contribute_nothing);
4696    lua_push_key_at_index(L, insert, contribute_insert);  /*tex An insert node has been contributed, but no boxes. */
4697    lua_push_key_at_index(L, box,    contribute_box);     /*tex A box has been contributed. */
4698    lua_push_key_at_index(L, rule,   contribute_rule);    /*tex A rule has been contributed. */
4699    return 1;
4700}
4701
4702static int texlib_triggerbuildpage(lua_State *L)
4703{
4704    if (lua_toboolean(L, 1) && cur_list.mode != vmode) {
4705        return 0;
4706    }
4707    tex_build_page(triggered_page_context, 0);
4708    return 0;
4709}
4710
4711static int texlib_getpagestate(lua_State *L)
4712{
4713    lua_pushinteger(L, lmt_page_builder_state.contents);
4714    return 1;
4715}
4716
4717static int texlib_getlocallevel(lua_State *L)
4718{
4719    lua_pushinteger(L, lmt_main_control_state.local_level);
4720    return 1;
4721}
4722
4723/* input state aka synctex */
4724
4725static int texlib_setinputstatemode(lua_State *L)
4726{
4727    input_file_state.mode = lmt_tohalfword(L, 1);
4728    return 0;
4729}
4730static int texlib_getinputstatemode(lua_State *L)
4731{
4732    lua_pushinteger(L, input_file_state.mode);
4733    return 1;
4734}
4735
4736static int texlib_setinputstatefile(lua_State *L)
4737{
4738    lmt_input_state.cur_input.state_file = lmt_tointeger(L, 1);
4739    return 0;
4740}
4741
4742static int texlib_getinputstatefile(lua_State *L)
4743{
4744    lua_pushinteger(L, lmt_input_state.cur_input.state_file);
4745    return 1;
4746}
4747
4748static int texlib_forceinputstatefile(lua_State *L)
4749{
4750    input_file_state.forced_file = lmt_tointeger(L, 1);
4751    return 0;
4752}
4753
4754static int texlib_forceinputstateline(lua_State *L)
4755{
4756    input_file_state.forced_line = lmt_tointeger(L, 1);
4757    return 0;
4758}
4759
4760static int texlib_setinputstateline(lua_State *L)
4761{
4762    input_file_state.line = lmt_tohalfword(L, 1);
4763    return 0;
4764}
4765
4766static int texlib_getinputstateline(lua_State *L)
4767{
4768    lua_pushinteger(L, input_file_state.line);
4769    return 1;
4770}
4771
4772/*tex
4773    This is experimental and might change. In version 10 we hope to have the final version available.
4774    It actually took quite a bit of time to understand the implications of mixing lua prints in here.
4775    The current variant is (so far) the most robust (wrt crashes and side effects).
4776*/
4777
4778// # define mode cur_list.mode_field
4779
4780/*tex
4781    When we add save levels then we can get crashes when one flushed bad groups due to out of order
4782    flushing. So we play safe! But still we can have issues so best make sure you're in hmode.
4783*/
4784
4785static int texlib_forcehmode(lua_State *L)
4786{
4787    if (is_v_mode(cur_list.mode)) {
4788        if (lua_type(L, 1) == LUA_TBOOLEAN) {
4789            tex_begin_paragraph(lua_toboolean(L, 1), force_par_trigger);
4790        } else {
4791            tex_begin_paragraph(1, force_par_trigger);
4792        }
4793    }
4794    return 0;
4795}
4796
4797/* tex
4798    The first argument can be a number (of a token register), a macro name or the name of a token
4799    list. The second argument is optional and when true forces expansion inside a definition. The
4800    optional third argument can be used to force grouping. The return value indicates an error: 0
4801    means no error, 1 means that a bad register number has been passed, a value of 2 indicated an
4802    unknown register or macro name, while 3 reports that the macro is not suitable for local
4803    control because it takes arguments.
4804*/
4805
4806static int texlib_runlocal(lua_State *L)
4807{
4808 // int obeymode = lua_toboolean(L, 4);
4809    int obeymode = 1; /* always 1 */
4810    halfword token = -1;
4811    int ismacro = 0 ;
4812    switch (lua_type(L, 1)) {
4813        case LUA_TFUNCTION:
4814            {
4815                /* todo: also a variant that calls an already registered function */
4816                int reference;
4817                halfword c, e;
4818                lua_pushvalue(L, 1);
4819                reference = luaL_ref(L, LUA_REGISTRYINDEX);
4820                c = tex_get_available_token(token_val(lua_local_call_cmd, reference));
4821                e = tex_get_available_token(token_val(end_local_cmd, 0));
4822                token_link(c) = e;
4823                tex_begin_inserted_list(c);
4824                if (lmt_token_state.luacstrings > 0) {
4825                    tex_lua_string_start();
4826                }
4827                if (tracing_nesting_par > 2) {
4828                    tex_local_control_message("entering token scanner via function");
4829                }
4830                tex_local_control(obeymode);
4831                luaL_unref(L, LUA_REGISTRYINDEX, reference);
4832// tex_cleanup_input_state();
4833                return 0;
4834            }
4835        case LUA_TNUMBER:
4836            {
4837                halfword k = lmt_checkhalfword(L, 1);
4838                if (k >= 0 && k <= 65535) {
4839                    token = toks_register(k);
4840                    goto WRAPUP;
4841                } else {
4842                    tex_local_control_message("invalid token register number");
4843                    return 0;
4844                }
4845            }
4846        case LUA_TSTRING:
4847            {
4848                size_t lname = 0;
4849                const char *name = lua_tolstring(L, 1, &lname);
4850                int cs = tex_string_locate_only(name, lname);
4851                int cmd = eq_type(cs);
4852                if (cmd < call_cmd) { // is_call_cmd
4853                    // todo: use the better register helpers and range checkers
4854                    switch (cmd) {
4855                        case register_toks_cmd:
4856                            token = toks_register(register_toks_number(eq_value(cs)));
4857                            goto WRAPUP;
4858                        case undefined_cs_cmd:
4859                            tex_local_control_message("undefined macro or token register");
4860                            return 0;
4861                        default:
4862                            /* like cs == case undefined_control_sequence */
4863                            tex_local_control_message("invalid macro or token register");
4864                            return 0;
4865                    }
4866                } else {
4867                    halfword ref = eq_value(cs);
4868                    halfword head = token_link(ref);
4869                    if (head && get_token_preamble(ref)) {
4870                        tex_local_control_message("macro takes arguments and is ignored");
4871                        return 0;
4872                    } else {
4873                        token = cs_token_flag + cs;
4874                        ismacro = 1 ;
4875                        goto WRAPUP;
4876                    }
4877                }
4878            }
4879        case LUA_TUSERDATA:
4880            /* no checking yet */
4881            token = token_info(lmt_token_code_from_lua(L, 1));
4882            ismacro = 1;
4883            goto WRAPUP;
4884        default:
4885            return 0;
4886    }
4887  WRAPUP:
4888    if (token < 0) {
4889        /* nothing to do */
4890    } else if (lmt_input_state.scanner_status != scanner_is_defining || lua_toboolean(L, 2)) {
4891        // todo: make list
4892        int grouped = lua_toboolean(L, 3);
4893        if (grouped) {
4894            tex_begin_inserted_list(tex_get_available_token(token_val(right_brace_cmd, 0)));
4895        }
4896        tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
4897        if (ismacro) {
4898            tex_begin_inserted_list(tex_get_available_token(token));
4899        } else {
4900            tex_begin_token_list(token, local_text);
4901        }
4902        if (grouped) {
4903            tex_begin_inserted_list(tex_get_available_token(token_val(left_brace_cmd, 0)));
4904        }
4905        /*tex hm, needed here? */
4906        if (lmt_token_state.luacstrings > 0) {
4907            tex_lua_string_start();
4908        }
4909        if (tracing_nesting_par > 2) {
4910            if (ismacro) {
4911                tex_local_control_message("entering token scanner via macro");
4912            } else {
4913                tex_local_control_message("entering token scanner via register");
4914            }
4915        }
4916        tex_local_control(obeymode);
4917// tex_cleanup_input_state();
4918    } else if (ismacro) {
4919        tex_back_input(token);
4920    } else {
4921        halfword h = null;
4922        halfword t = null;
4923        halfword r = token_link(token);
4924        while (r) {
4925            t = tex_store_new_token(t, token_info(r));
4926            if (! h) {
4927                h = t;
4928            }
4929            r = token_link(r);
4930        }
4931        tex_begin_inserted_list(h);
4932    }
4933    return 0;
4934}
4935
4936static int texlib_quitlocal(lua_State *L)
4937{
4938    (void) L;
4939    if (tracing_nesting_par > 2) {
4940        tex_local_control_message("quitting token scanner");
4941    }
4942    tex_end_local_control();
4943    return 0;
4944}
4945
4946/* todo: no tryagain and justincase here */
4947
4948static int texlib_expandasvalue(lua_State *L) /* mostly like the mp one */
4949{
4950    int kind = lmt_tointeger(L, 1);
4951    halfword tail = null;
4952    halfword head = lmt_macro_to_tok(L, 2, &tail);
4953    if (head) {
4954        switch (kind) {
4955            case lua_value_none_code:
4956            case lua_value_dimension_code:
4957                {
4958                    halfword value = 0;
4959                    halfword space = tex_get_available_token(space_token);
4960                    halfword relax = tex_get_available_token(deep_frozen_relax_token);
4961                    token_link(tail) = space;
4962                    token_link(space) = relax;
4963                    tex_begin_inserted_list(head);
4964                    lmt_error_state.intercept = 1;
4965                    lmt_error_state.last_intercept = 0;
4966                    value = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
4967                    lmt_error_state.intercept = 0;
4968                    while (cur_tok != deep_frozen_relax_token) {
4969                        tex_get_token();
4970                    }
4971                    if (! lmt_error_state.last_intercept) {
4972                        lua_pushinteger(L, value);
4973                        break;
4974                    } else if (kind == lua_value_none_code) {
4975                        head = lmt_macro_to_tok(L, 2, &tail);
4976                        goto TRYAGAIN;
4977                    } else {
4978                        head = lmt_macro_to_tok(L, 2, &tail);
4979                        goto JUSTINCASE;
4980                    }
4981                }
4982            case lua_value_integer_code:
4983            case lua_value_cardinal_code:
4984            case lua_value_boolean_code:
4985              TRYAGAIN:
4986                {
4987                    halfword value = 0;
4988                    halfword space = tex_get_available_token(space_token);
4989                    halfword relax = tex_get_available_token(deep_frozen_relax_token);
4990                    token_link(tail) = space;
4991                    token_link(space) = relax;
4992                    tex_begin_inserted_list(head);
4993                    lmt_error_state.intercept = 1;
4994                    lmt_error_state.last_intercept = 0;
4995                    value = tex_scan_integer(0, NULL, NULL);
4996                    lmt_error_state.intercept = 0;
4997                    while (cur_tok != deep_frozen_relax_token) {
4998                        tex_get_token();
4999                    }
5000                    if (lmt_error_state.last_intercept) {
5001                        head = lmt_macro_to_tok(L, 2, &tail);
5002                        goto JUSTINCASE;
5003                    } else if (kind == lua_value_boolean_code) {
5004                        lua_pushboolean(L, value);
5005                        break;
5006                    } else {
5007                        lua_pushinteger(L, value);
5008                        break;
5009                    }
5010                }
5011            case lua_value_conditional_code:
5012                /* for now */
5013            default:
5014              JUSTINCASE:
5015                {
5016                    int len = 0;
5017                    const char *str = (const char *) lmt_get_expansion(head, &len);
5018                    lua_pushlstring(L, str, str ? len : 0); /* len includes \0 */
5019                    break;
5020                }
5021        }
5022        return 1;
5023    } else {
5024        return 0;
5025    }
5026}
5027
5028/* [catcodetable] string, expand-in-def, group, ignore-unknown-cs */
5029
5030static int texlib_runstring(lua_State *L)
5031{
5032    int top = lua_gettop(L);
5033    if (top > 0) {
5034        size_t lstr = 0;
5035        const char *str = NULL;
5036        int slot = 1;
5037        halfword ct = lua_type(L, slot) == LUA_TNUMBER ? lmt_tohalfword(L, slot++) : cat_code_table_par;
5038        if (! tex_valid_catcode_table(ct)) {
5039            ct = cat_code_table_par;
5040        }
5041        str = lua_tolstring(L, slot++, &lstr);
5042        if (lstr > 0) {
5043            int obeymode = 1; /* always 1 */
5044            int expand =  lua_toboolean(L, slot++);
5045            int grouped = lua_toboolean(L, slot++);
5046            int ignore = lua_toboolean(L, slot++);
5047            halfword h = get_reference_token();
5048            halfword t = h;
5049            if (grouped) {
5050             // t = tex_store_new_token(a, left_brace_token + '{');
5051                t = tex_store_new_token(t, token_val(right_brace_cmd, 0));
5052            }
5053            /*tex Options: 1=create (will trigger an error), 2=ignore. */
5054            tex_parse_str_to_tok(h, &t, ct, str, lstr, ignore ? 2 : 1);
5055            if (grouped) {
5056             // t = tex_store_new_token(a, left_brace_token + '}');
5057                t = tex_store_new_token(t, token_val(left_brace_cmd, 0));
5058            }
5059            if (lmt_input_state.scanner_status != scanner_is_defining || expand) {
5060             // t = tex_store_new_token(t, token_val(end_local_cmd, 0));
5061                tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
5062                tex_begin_token_list(h, local_text);
5063                if (lmt_token_state.luacstrings > 0) {
5064                    tex_lua_string_start();
5065                }
5066                if (tracing_nesting_par > 2) {
5067                    tex_local_control_message("entering token scanner via register");
5068                }
5069                tex_local_control(obeymode);
5070tex_cleanup_input_state();
5071            } else {
5072                tex_begin_inserted_list(h);
5073            }
5074        }
5075    }
5076    return 0;
5077}
5078
5079/* new, can go into luatex too */
5080
5081static int texlib_getmathdir(lua_State *L)
5082{
5083    lua_pushinteger(L, math_direction_par);
5084    return 1;
5085}
5086
5087static int texlib_setmathdir(lua_State *L)
5088{
5089    tex_set_math_dir(lmt_tohalfword(L, 1));
5090    return 0;
5091}
5092
5093static int texlib_getpardir(lua_State *L)
5094{
5095    lua_pushinteger(L, par_direction_par);
5096    return 1;
5097}
5098
5099static int texlib_setpardir(lua_State *L)
5100{
5101    tex_set_par_dir(lmt_tohalfword(L, 1));
5102    return 0;
5103}
5104
5105static int texlib_gettextdir(lua_State *L)
5106{
5107    lua_pushinteger(L, text_direction_par);
5108    return 1;
5109}
5110
5111static int texlib_settextdir(lua_State *L)
5112{
5113    tex_set_text_dir(lmt_tohalfword(L, 1));
5114    return 0;
5115}
5116
5117/* Getting the line direction makes no sense, it's just the text direction. */
5118
5119static int texlib_setlinedir(lua_State *L)
5120{
5121    tex_set_line_dir(lmt_tohalfword(L, 1));
5122    return 0;
5123}
5124
5125static int texlib_getboxdir(lua_State *L)
5126{
5127    int index = lmt_tointeger(L, 1);
5128    if (index >= 0 && index <= max_box_register_index) {
5129        if (box_register(index)) {
5130            lua_pushinteger(L, box_dir(box_register(index)));
5131        } else {
5132            lua_pushnil(L);
5133        }
5134        return 1;
5135    } else {
5136        texlib_aux_show_box_index_error(L);
5137    }
5138    return 0;
5139}
5140
5141static int texlib_setboxdir(lua_State *L)
5142{
5143    int index = lmt_tointeger(L, 1);
5144    if (index >= 0 && index <= max_box_register_index) {
5145        tex_set_box_dir(index, lmt_tosingleword(L, 2));
5146    } else {
5147        texlib_aux_show_box_index_error(L);
5148    }
5149    return 0;
5150}
5151
5152static int texlib_gethelptext(lua_State *L)
5153{
5154    if (lmt_error_state.help_text) {
5155        lua_pushstring(L, lmt_error_state.help_text);
5156    } else {
5157        lua_pushnil(L);
5158    }
5159    return 1;
5160}
5161
5162static int texlib_setinteraction(lua_State *L)
5163{
5164    if (lua_type(L,1) == LUA_TNUMBER) {
5165        int i = lmt_tointeger(L, 1);
5166        if (i >= 0 && i <= 3) {
5167            lmt_error_state.interaction = i;
5168        }
5169    }
5170    return 0;
5171}
5172
5173static int texlib_getinteraction(lua_State *L)
5174{
5175    lua_pushinteger(L, lmt_error_state.interaction);
5176    return 1;
5177}
5178
5179static int texlib_setglyphdata(lua_State *L)
5180{
5181    update_tex_glyph_data(0, lmt_opthalfword(L, 1, unused_attribute_value));
5182    return 0;
5183}
5184
5185static int texlib_getglyphdata(lua_State *L)
5186{
5187    lua_pushinteger(L, glyph_data_par);
5188    return 1;
5189}
5190
5191static int texlib_setglyphstate(lua_State *L)
5192{
5193    update_tex_glyph_state(0, lmt_opthalfword(L, 1, unused_state_value));
5194    return 0;
5195}
5196
5197static int texlib_getglyphstate(lua_State *L)
5198{
5199    lua_pushinteger(L, glyph_state_par);
5200    return 1;
5201}
5202
5203static int texlib_setglyphscript(lua_State *L)
5204{
5205    update_tex_glyph_script(0, lmt_opthalfword(L, 1, unused_script_value));
5206    return 0;
5207}
5208
5209static int texlib_getglyphscript(lua_State *L)
5210{
5211    lua_pushinteger(L, glyph_script_par);
5212    return 1;
5213}
5214
5215static int texlib_getglyphscales(lua_State *L)
5216{
5217    lua_pushinteger(L, glyph_scale_par);
5218    lua_pushinteger(L, glyph_x_scale_par);
5219    lua_pushinteger(L, glyph_y_scale_par);
5220    lua_pushinteger(L, glyph_data_par);
5221    return 4;
5222}
5223
5224static int texlib_fatalerror(lua_State *L)
5225{
5226    const char *s = lua_tostring(L, 1);
5227    tex_fatal_error(s);
5228    return 1;
5229}
5230
5231static int texlib_lastnodetype(lua_State *L)
5232{
5233    halfword tail = cur_list.tail;
5234    int type = -1;
5235    int subtype = -1;
5236    if (tail) {
5237        halfword mode = cur_list.mode;
5238        if (mode != nomode && tail != contribute_head && node_type(tail) != glyph_node) {
5239            type = node_type(tail);
5240            subtype = node_subtype(tail);
5241        } else if (mode == vmode && tail == cur_list.head) {
5242            type = lmt_page_builder_state.last_node_type;
5243            subtype = lmt_page_builder_state.last_node_subtype;
5244        } else if (mode == nomode || tail == cur_list.head) {
5245            /* already -1 */
5246        } else {
5247            type = node_type(tail);
5248            subtype = node_subtype(tail);
5249        }
5250    }
5251    if (type >= 0) {
5252        lua_pushinteger(L, type);
5253        lua_pushinteger(L, subtype);
5254    } else {
5255        lua_pushnil(L);
5256        lua_pushnil(L);
5257    }
5258    return 2;
5259}
5260
5261/* we can have all defs here */
5262
5263static int texlib_chardef(lua_State *L)
5264{
5265    size_t len;
5266    const char *str = lua_tolstring(L, 1, &len);
5267    if (len > 0) {
5268        int cs = tex_string_locate(str, len, 1);
5269        int flags = 0;
5270        lmt_check_for_flags(L, 3, &flags, 1, 0);
5271        if (tex_define_permitted(cs, flags)) {
5272            int code = lmt_tointeger(L, 2);
5273            if (code >= 0 && code <= max_character_code) {
5274                tex_define(flags, cs, (quarterword) char_given_cmd, code);
5275            } else {
5276                tex_formatted_error("lua", "chardef only accepts codes in the range 0-%i", max_character_code);
5277            }
5278        }
5279    }
5280    return 0;
5281}
5282
5283/* todo: same range checks as in texlib_setmathcode */
5284
5285static int texlib_mathchardef(lua_State *L)
5286{
5287    size_t len;
5288    const char *str = lua_tolstring(L, 1, &len);
5289    if (len > 0) {
5290        int cs = tex_string_locate(str, len, 1);
5291        int flags = 0;
5292        int slot = lmt_check_for_flags(L, 5, &flags, 1, 0);
5293        if (tex_define_permitted(cs, flags)) {
5294            mathcodeval mval;
5295            mathdictval dval;
5296            mval.class_value = (short) lmt_tointeger(L, 2);
5297            mval.family_value = (short) lmt_tointeger(L, 3);
5298            mval.character_value = lmt_tointeger(L, 4);
5299            /* flags */
5300            dval.properties = lmt_optquarterword(L, slot++, 0);
5301            dval.group = lmt_optquarterword(L, slot++, 0);
5302            dval.index = lmt_optinteger(L, slot++, 0);
5303            if (class_in_range(mval.class_value) && family_in_range(mval.family_value) && character_in_range(mval.character_value)) {
5304                tex_define(flags, cs, mathspec_cmd, tex_new_math_dict_spec(dval, mval, umath_mathcode));
5305            } else {
5306                tex_normal_error("lua", "mathchardef needs proper class, family and character codes");
5307            }
5308        } else {
5309            /* maybe a message */
5310        }
5311    }
5312    return 0;
5313}
5314
5315static int texlib_setintegervalue(lua_State *L)
5316{
5317    size_t len;
5318    const char *str = lua_tolstring(L, 1, &len);
5319    if (len > 0) {
5320        int cs = tex_string_locate(str, len, 1);
5321        int flags = 0;
5322        lmt_check_for_flags(L, 3, &flags, 1, 0);
5323        if (tex_define_permitted(cs, flags)) {
5324            int value = lmt_optroundnumber(L, 2, 0);
5325            if (value >= min_integer && value <= max_integer) {
5326                tex_define(flags, cs, (quarterword) integer_cmd, value);
5327            } else {
5328                tex_formatted_error("lua", "integer only accepts values in the range %i-%i", min_integer, max_integer);
5329            }
5330        }
5331    }
5332    return 0;
5333}
5334
5335static int texlib_setfloatvalue(lua_State *L)
5336{
5337    size_t len;
5338    const char *str = lua_tolstring(L, 1, &len);
5339    if (len > 0) {
5340        int cs = tex_string_locate(str, len, 1);
5341        int flags = 0;
5342        lmt_check_for_flags(L, 3, &flags, 1, 0);
5343        if (tex_define_permitted(cs, flags)) {
5344            unsigned value = tex_double_to_posit(luaL_optnumber(L, 2, 0)).v;
5345         /* if we check we need to do it on the double or look at somethign else */
5346         /* if ((value >= min_posit) && (value <= max_posit)) { */ /* always true */
5347                tex_define(flags, cs, (quarterword) posit_cmd, value);
5348         /* } else { */
5349         /*     tex_formatted_error("lua", "posit only accepts values in the range %i-%i", min_posit, max_posit); */
5350         /* } */
5351        }
5352    }
5353    return 0;
5354}
5355
5356static int texlib_setcardinalvalue(lua_State *L)
5357{
5358    size_t len;
5359    const char *str = lua_tolstring(L, 1, &len);
5360    if (len > 0) {
5361        int cs = tex_string_locate(str, len, 1);
5362        int flags = 0;
5363        lmt_check_for_flags(L, 3, &flags, 1, 0);
5364        if (tex_define_permitted(cs, flags)) {
5365            unsigned value = lmt_opturoundnumber(L, 2, 0);
5366         /* if we check we need to do it on the double or look at somethign else */
5367         /* if ((value >= min_cardinal) && (value <= max_cardinal)) { */ /* always true */
5368                tex_define(flags, cs, (quarterword) integer_cmd, value);
5369         /* } else { */
5370         /*     tex_formatted_error("lua", "cardinal only accepts values in the range %d-%d", min_cardinal, max_cardinal); */
5371         /* } */
5372        }
5373    }
5374    return 0;
5375}
5376
5377static int texlib_setdimensionvalue(lua_State *L)
5378{
5379    size_t len;
5380    const char *str = lua_tolstring(L, 1, &len);
5381    if (len > 0) {
5382        int cs = tex_string_locate(str, len, 1);
5383        int flags = 0;
5384        lmt_check_for_flags(L, 3, &flags, 1, 0);
5385        if (tex_define_permitted(cs, flags)) {
5386            int value = lmt_optroundnumber(L, 2, 0);
5387            if (value >= min_dimension && value <= max_dimension) {
5388                tex_define(flags, cs, (quarterword) dimension_cmd, value);
5389            } else {
5390                tex_formatted_error("lua", "dimension only accepts values in the range %i-%i", min_dimension, max_dimension);
5391            }
5392        }
5393    }
5394    return 0;
5395}
5396
5397// static int texlib_setgluespecvalue(lua_State *L)
5398// {
5399//     return 0;
5400// }
5401
5402static int texlib_aux_getvalue(lua_State *L, halfword level, halfword cs)
5403{
5404    halfword chr = eq_value(cs);
5405    if (chr && ! get_token_preamble(chr)) { /* or get_token_parameters as we don't want trailing # */
5406        halfword value = 0;
5407        tex_begin_inserted_list(tex_get_available_token(cs_token_flag + cs));
5408        if (tex_scan_tex_value(level, &value)) {
5409            if (level == posit_val_level) {
5410                lua_pushnumber(L, tex_posit_to_double(value));
5411            } else {
5412                lua_pushinteger(L, value);
5413            }
5414            return 1;
5415        }
5416    }
5417    lua_pushnil(L);
5418    return 1;
5419}
5420
5421static int texlib_getintegervalue(lua_State *L) /* todo, now has duplicate in tokenlib */
5422{
5423    switch (lua_type(L, 1)) {
5424        case LUA_TSTRING:
5425            {
5426                size_t len;
5427                const char *str = lua_tolstring(L, 1, &len);
5428                if (len > 0) {
5429                    int cs = tex_string_locate_only(str, len);
5430                    switch (eq_type(cs)) {
5431                        case integer_cmd:
5432                            lua_pushinteger(L, eq_value(cs));
5433                            return 1;
5434                        case call_cmd:
5435                        case protected_call_cmd:
5436                        case semi_protected_call_cmd:
5437                        case constant_call_cmd:
5438                            return texlib_aux_getvalue(L, integer_val_level, cs);
5439                        default:
5440                            /* twice a lookup but fast enough for now */
5441                            return texlib_getcount(L);
5442                    }
5443                } else {
5444                    break;
5445                }
5446            }
5447        case LUA_TNUMBER:
5448            {
5449                halfword i = lmt_tohalfword(L, 1) - 0xFFFF;
5450                if (i < (eqtb_size + lmt_hash_state.hash_data.ptr + 1) && eq_type(i) == integer_cmd) {
5451                    lua_pushinteger(L, eq_value(i));
5452                    return 1;
5453                } else {
5454                    break;
5455                }
5456            }
5457    }
5458    lua_pushnil(L);
5459    return 1;
5460}
5461
5462static int texlib_getfloatvalue(lua_State *L) /* todo, now has duplicate in tokenlib */
5463{
5464    if (lua_type(L, 1) == LUA_TSTRING) {
5465        size_t len;
5466        const char *str = lua_tolstring(L, 1, &len);
5467        if (len > 0) {
5468            int cs = tex_string_locate_only(str, len);
5469            switch (eq_type(cs)) {
5470                case posit_cmd:
5471                    lua_pushnumber(L, tex_posit_to_double(eq_value(cs)));
5472                    return 1;
5473                case call_cmd:
5474                case protected_call_cmd:
5475                case semi_protected_call_cmd:
5476                case constant_call_cmd:
5477                    return texlib_aux_getvalue(L, posit_val_level, cs);
5478                default:
5479                    /* twice a lookup but fast enough for now */
5480                    return texlib_getfloat(L);
5481            }
5482        }
5483    }
5484    lua_pushnil(L);
5485    return 1;
5486}
5487
5488static int texlib_getdimensionvalue(lua_State *L) /* todo, now has duplicate in tokenlib */
5489{
5490    switch (lua_type(L, 1)) {
5491        case LUA_TSTRING:
5492            {
5493                size_t len;
5494                const char *str = lua_tolstring(L, 1, &len);
5495                if (len > 0) {
5496                    int cs = tex_string_locate_only(str, len);
5497                    switch (eq_type(cs)) {
5498                        case dimension_cmd:
5499                            lua_pushinteger(L, eq_value(cs));
5500                            return 1;
5501                        case posit_cmd:
5502                            lua_pushinteger(L, tex_posit_to_dimension(eq_value(cs)));
5503                            return 1;
5504                        case call_cmd:
5505                        case protected_call_cmd:
5506                        case semi_protected_call_cmd:
5507                        case constant_call_cmd:
5508                            return texlib_aux_getvalue(L, dimension_val_level, cs);
5509                        default:
5510                            /* twice a lookup but fast enough for now */
5511                            return texlib_getdimen(L);
5512                    }
5513                } else {
5514                    break;
5515                }
5516            }
5517        case LUA_TNUMBER:
5518            {
5519                halfword i = lmt_tohalfword(L, 1) - 0xFFFF;
5520                if (i < (eqtb_size + lmt_hash_state.hash_data.ptr + 1) && eq_type(i) == dimension_cmd) {
5521                    lua_pushinteger(L, eq_value(i));
5522                    return 1;
5523                } else {
5524                    break;
5525                }
5526            }
5527        case LUA_TUSERDATA:
5528            {
5529                halfword cs = lmt_get_lua_token_cs(L, 1);
5530                if (cs) {
5531                    switch (eq_type(cs)) {
5532                        case dimension_cmd:
5533                            lua_pushinteger(L, eq_value(cs));
5534                            return 1;
5535                        case posit_cmd:
5536                            lua_pushinteger(L, tex_posit_to_dimension(eq_value(cs)));
5537                            return 1;
5538                        case call_cmd:
5539                        case protected_call_cmd:
5540                        case semi_protected_call_cmd:
5541                        case constant_call_cmd:
5542                            return texlib_aux_getvalue(L, dimension_val_level, cs);
5543                        default:
5544                            /* to be checked */
5545                            return texlib_getdimen(L);
5546                    }
5547                } else {
5548                    break;
5549                }
5550
5551            }
5552    }
5553    lua_pushnil(L);
5554    return 1;
5555}
5556
5557// static int texlib_getgluespecvalue(lua_State *L) /* todo, now has duplicate in tokenlib */
5558// {
5559//     return 1;
5560// }
5561
5562/*tex
5563    Negative values are internal and inline. At some point I might do this as with modes and tokens
5564    although we don't have lookups here.
5565
5566    In these list we don't really need the predefined keys.
5567*/
5568
5569static int texlib_getmodevalues(lua_State *L)
5570{
5571    lua_createtable(L, 4, 1);
5572    lua_push_key_at_index(L, unset,      nomode);
5573    lua_push_key_at_index(L, vertical,   vmode);
5574    lua_push_key_at_index(L, horizontal, hmode);
5575    lua_push_key_at_index(L, math,       mmode);
5576    return 1;
5577}
5578
5579static int texlib_getmode(lua_State *L)
5580{
5581    lua_pushinteger(L, tex_normalized_mode(cur_list.mode));
5582    return 1;
5583}
5584
5585static int texlib_getrunstatevalues(lua_State *L)
5586{
5587    lua_createtable(L, 2, 1);
5588    lua_set_string_by_index(L, initializing_state, "initializing");
5589    lua_set_string_by_index(L, updating_state,     "updating");
5590    lua_set_string_by_index(L, production_state,   "production");
5591    return 1;
5592}
5593
5594static int texlib_setrunstate(lua_State *L)
5595{
5596    if (lmt_main_state.run_state) {
5597        int state = lmt_tointeger(L, 1);
5598        if (state == updating_state || state == production_state) {
5599            lmt_main_state.run_state = state;
5600        }
5601    } else {
5602        /* We're in ini mode. */
5603    }
5604    return 0;
5605}
5606
5607static int texlib_getrunstate(lua_State *L)
5608{
5609    lua_pushinteger(L, lmt_main_state.run_state);
5610    return 1;
5611}
5612
5613/*tex
5614    todo: Some of these keywords can be removed from the interface keys, saves bytes and never accessed
5615    as key.
5616*/
5617
5618static int texlib_gethyphenationvalues(lua_State *L)
5619{
5620    lua_createtable(L, 2, 18);
5621    lua_push_key_at_index(L, normal,    normal_hyphenation_mode);
5622    lua_push_key_at_index(L, automatic, automatic_hyphenation_mode);
5623    lua_push_key_at_index(L, explicit,  explicit_hyphenation_mode);
5624    lua_push_key_at_index(L, syllable,  syllable_hyphenation_mode);
5625    lua_push_key_at_index(L, uppercase, uppercase_hyphenation_mode);
5626    lua_push_key_at_index(L, compound,  compound_hyphenation_mode);
5627    lua_push_key_at_index(L, collapse,  collapse_hyphenation_mode);
5628
5629    lua_set_string_by_index(L, strict_start_hyphenation_mode,        "strictstart");
5630    lua_set_string_by_index(L, strict_end_hyphenation_mode,          "strictend");
5631    lua_set_string_by_index(L, automatic_penalty_hyphenation_mode,   "automaticpenalty");
5632    lua_set_string_by_index(L, explicit_penalty_hyphenation_mode,    "explicitpenalty");
5633    lua_set_string_by_index(L, permit_glue_hyphenation_mode,         "permitglue");
5634    lua_set_string_by_index(L, permit_all_hyphenation_mode,          "permitall");
5635    lua_set_string_by_index(L, permit_math_replace_hyphenation_mode, "permitmathreplace");
5636    lua_set_string_by_index(L, force_check_hyphenation_mode,         "forcecheck");
5637    lua_set_string_by_index(L, lazy_ligatures_hyphenation_mode,      "lazyligatures");
5638    lua_set_string_by_index(L, force_handler_hyphenation_mode,       "forcehandler");
5639    lua_set_string_by_index(L, feedback_compound_hyphenation_mode,   "feedbackcompound");
5640    lua_set_string_by_index(L, ignore_bounds_hyphenation_mode,       "ignorebounds");
5641    lua_set_string_by_index(L, replace_apostrophe_hyphenation_mode,  "replaceapostrophe");
5642    return 1;
5643}
5644
5645static int texlib_getglyphoptionvalues(lua_State *L)
5646{
5647    lua_createtable(L, 3, 16);
5648    lua_set_string_by_index(L, glyph_option_normal_glyph,              "normal");
5649    lua_set_string_by_index(L, glyph_option_no_left_ligature,          "noleftligature");
5650    lua_set_string_by_index(L, glyph_option_no_right_ligature,         "norightligature");
5651    lua_set_string_by_index(L, glyph_option_no_left_kern,              "noleftkern");
5652    lua_set_string_by_index(L, glyph_option_no_right_kern,             "norightkern");
5653    lua_set_string_by_index(L, glyph_option_no_expansion,              "noexpansion");
5654    lua_set_string_by_index(L, glyph_option_no_protrusion,             "noprotrusion");
5655    lua_set_string_by_index(L, glyph_option_apply_x_offset,            "applyxoffset");
5656    lua_set_string_by_index(L, glyph_option_apply_y_offset,            "applyyoffset");
5657    lua_set_string_by_index(L, glyph_option_no_italic_correction,      "noitaliccorrection");
5658    lua_set_string_by_index(L, glyph_option_no_zero_italic_correction, "nozeroitaliccorrection");
5659    lua_set_string_by_index(L, glyph_option_math_discretionary,        "mathdiscretionary");
5660    lua_set_string_by_index(L, glyph_option_math_italics_too,          "mathsitalicstoo");
5661    lua_set_string_by_index(L, glyph_option_math_artifact,             "mathartifact");
5662    lua_set_string_by_index(L, glyph_option_weight_less,               "weightless");
5663    lua_set_string_by_index(L, glyph_option_space_factor_overload,     "spacefactoroverload");
5664    lua_set_string_by_index(L, glyph_option_check_toddler,             "checktoddler");
5665    lua_set_string_by_index(L, glyph_option_check_twin,                "checktwin");
5666    lua_set_string_by_index(L, glyph_option_is_toddler,                "istoddler");
5667    lua_set_string_by_index(L, glyph_option_is_continuation,           "iscontinuation");
5668    lua_set_string_by_index(L, glyph_option_user_first,                "userfirst");
5669    lua_set_string_by_index(L, glyph_option_user_last,                 "userlast");
5670    return 1;
5671}
5672
5673static int texlib_getglueoptionvalues(lua_State *L)
5674{
5675    lua_createtable(L, 8, 2);
5676    lua_set_string_by_index(L, glue_option_normal,            "normal");
5677    lua_set_string_by_index(L, glue_option_no_auto_break,     "noautobreak");
5678    lua_set_string_by_index(L, glue_option_has_factor,        "hasfactor");
5679    lua_set_string_by_index(L, glue_option_is_limited,        "islimited");
5680    lua_set_string_by_index(L, glue_option_limit,             "limit");
5681    lua_set_string_by_index(L, glue_option_u_leaders_line,    "uleadersline");
5682    lua_set_string_by_index(L, glue_option_set_discardable,   "setdiscardable");
5683    lua_set_string_by_index(L, glue_option_reset_discardable, "resetdiscardable");
5684    lua_set_string_by_index(L, glue_option_non_discardable,   "nondiscardable");
5685    lua_set_string_by_index(L, glue_option_in_insert,         "ininsert");
5686    return 1;
5687}
5688
5689static int texlib_getkernoptionvalues(lua_State *L)
5690{
5691    lua_createtable(L, 1, 1);
5692    lua_set_string_by_index(L, kern_option_normal,    "normal");
5693    lua_set_string_by_index(L, kern_option_in_insert, "ininsert");
5694    return 1;
5695}
5696
5697static int texlib_getmathoptionvalues(lua_State *L)
5698{
5699    lua_createtable(L, 2, 3);
5700    lua_set_string_by_index(L, math_option_normal,   "normal");
5701    lua_set_string_by_index(L, math_option_short,    "short");
5702    lua_set_string_by_index(L, math_option_orphaned, "orphaned");
5703    lua_set_string_by_index(L, math_option_display,  "display");
5704    lua_set_string_by_index(L, math_option_cramped,  "cramped");
5705    return 1;
5706}
5707
5708static int texlib_getpenaltyoptionvalues(lua_State *L)
5709{
5710    lua_createtable(L, 2, 15);
5711    lua_set_string_by_index(L, penalty_option_normal,        "normal");
5712    lua_set_string_by_index(L, penalty_option_math_forward,  "mathforward");
5713    lua_set_string_by_index(L, penalty_option_math_backward, "mathbackward");
5714    lua_set_string_by_index(L, penalty_option_orphaned,      "orphaned");
5715    lua_set_string_by_index(L, penalty_option_widowed,       "widowed");
5716    lua_set_string_by_index(L, penalty_option_clubbed,       "clubbed");
5717    lua_set_string_by_index(L, penalty_option_toddlered,     "toddlered");
5718    lua_set_string_by_index(L, penalty_option_widow,         "widow");
5719    lua_set_string_by_index(L, penalty_option_club,          "club");
5720    lua_set_string_by_index(L, penalty_option_broken,        "broken");
5721    lua_set_string_by_index(L, penalty_option_shaping,       "shaping");
5722    lua_set_string_by_index(L, penalty_option_double,        "double");
5723    lua_set_string_by_index(L, penalty_option_double_used,   "doubleused");
5724    lua_set_string_by_index(L, penalty_option_factor_used,   "factorused");
5725    lua_set_string_by_index(L, penalty_option_end_of_par,    "endofpar");
5726    lua_set_string_by_index(L, penalty_option_in_insert,     "ininsert");
5727    lua_set_string_by_index(L, penalty_option_final_balance, "finalbalance");
5728    return 1;
5729}
5730
5731static int texlib_getuleaderlocationvalues(lua_State *L)
5732{
5733    lua_createtable(L, 2, 3);
5734    lua_set_string_by_index(L, uleader_filtered_hpack, "filtered_hpack");
5735    lua_set_string_by_index(L, uleader_lua,            "lua");
5736    lua_set_string_by_index(L, uleader_before_vpack,   "before_vpack");
5737    lua_set_string_by_index(L, uleader_after_vpack,    "after_vpack");
5738    lua_set_string_by_index(L, uleader_post_linebreak, "post_linebreak");
5739    return 1;
5740}
5741
5742static int texlib_getnoadoptionvalues(lua_State *L)
5743{
5744    lua_createtable(L, 2, 48);
5745    lua_push_key_at_index(L, axis,     noad_option_axis);
5746    lua_push_key_at_index(L, exact,    noad_option_exact);
5747    lua_push_key_at_index(L, left,     noad_option_left);
5748    lua_push_key_at_index(L, middle,   noad_option_middle);
5749    lua_push_key_at_index(L, right,    noad_option_right);
5750    lua_push_key_at_index(L, void,     noad_option_void);
5751    lua_push_key_at_index(L, phantom,  noad_option_phantom);
5752    lua_push_key_at_index(L, limits,   noad_option_limits);
5753    lua_push_key_at_index(L, auto,     noad_option_auto);
5754    lua_push_key_at_index(L, shrink,   noad_option_shrink);
5755    lua_push_key_at_index(L, stretch,  noad_option_stretch);
5756    lua_push_key_at_index(L, center,   noad_option_center);
5757    lua_push_key_at_index(L, scale,    noad_option_scale);
5758    lua_push_key_at_index(L, single,   noad_option_single);
5759    lua_push_key_at_index(L, norule,   noad_option_no_rule);
5760    lua_push_key_at_index(L, keepbase, noad_option_keep_base);
5761
5762    lua_set_string_by_index(L, noad_option_auto_base,                  "autobase");
5763    lua_set_string_by_index(L, noad_option_no_axis,                    "noaxis");
5764    lua_set_string_by_index(L, noad_option_no_overflow,                "nooverflow");
5765    lua_set_string_by_index(L, noad_option_no_limits,                  "nolimits");
5766    lua_set_string_by_index(L, noad_option_no_check,                   "nocheck");
5767    lua_set_string_by_index(L, noad_option_adapt_to_left_size,         "adapttoleftsize");
5768    lua_set_string_by_index(L, noad_option_adapt_to_right_size,        "adapttorightsize");
5769    lua_set_string_by_index(L, noad_option_no_sub_script,              "nosubscript");
5770    lua_set_string_by_index(L, noad_option_no_super_script,            "nosuperscript");
5771    lua_set_string_by_index(L, noad_option_no_sub_pre_script,          "nosubprescript");
5772    lua_set_string_by_index(L, noad_option_no_super_pre_script,        "nosuperprescript");
5773    lua_set_string_by_index(L, noad_option_no_script,                  "noscript");
5774    lua_set_string_by_index(L, noad_option_openup_height,              "openupheight");
5775    lua_set_string_by_index(L, noad_option_openup_depth,               "openupdepth");
5776    lua_set_string_by_index(L, noad_option_prefer_font_thickness,      "preferfontthickness");
5777    lua_set_string_by_index(L, noad_option_no_ruling,                  "noruling");
5778    lua_set_string_by_index(L, noad_option_indexed_sub_script,         "indexedsubscript");
5779    lua_set_string_by_index(L, noad_option_indexed_super_script,       "indexedsuperscript");
5780    lua_set_string_by_index(L, noad_option_indexed_sub_pre_script,     "indexedsubprescript");
5781    lua_set_string_by_index(L, noad_option_indexed_super_pre_script,   "indexedsuperprescript");
5782    lua_set_string_by_index(L, noad_option_unpack_list,                "unpacklist");
5783    lua_set_string_by_index(L, noad_option_unroll_list,                "unrolllist");
5784    lua_set_string_by_index(L, noad_option_followed_by_space,          "followedbyspace");
5785    lua_set_string_by_index(L, noad_option_proportional,               "proportional");
5786    lua_set_string_by_index(L, noad_option_source_on_nucleus,          "sourceonnucleus");
5787    lua_set_string_by_index(L, noad_option_fixed_super_or_sub_script,  "fixedsuperorsubscript");
5788    lua_set_string_by_index(L, noad_option_fixed_super_and_sub_script, "fixedsuperandsubscript");
5789    lua_set_string_by_index(L, noad_option_auto_middle,                "automiddle");
5790    lua_set_string_by_index(L, noad_option_reflected,                  "reflected");
5791    lua_set_string_by_index(L, noad_option_continuation,               "continuation");
5792    lua_set_string_by_index(L, noad_option_inherit_class,              "inheritclass");
5793    lua_set_string_by_index(L, noad_option_discard_shape_kern,         "discardshapekern");
5794    lua_set_string_by_index(L, noad_option_realign_scripts,            "realignscripts");
5795    lua_set_string_by_index(L, noad_option_ignore_empty_sub_script,    "ignoreemptysubscript");
5796    lua_set_string_by_index(L, noad_option_ignore_empty_super_script,  "ignoreemptysuperscript");
5797    lua_set_string_by_index(L, noad_option_ignore_empty_prime_script,  "ignoreemptyprimescript");
5798    lua_set_string_by_index(L, noad_option_continuation_head,          "continuationhead");
5799    lua_set_string_by_index(L, noad_option_continuation_kernel,        "continuationkernel");
5800    lua_set_string_by_index(L, noad_option_reorder_pre_scripts,        "reorderprescripts");
5801    lua_set_string_by_index(L, noad_option_ignore,                     "ignore");
5802    lua_set_string_by_index(L, noad_option_no_more_scripts,            "nomorescripts");
5803    lua_set_string_by_index(L, noad_option_carry_over_classes,         "carryoverclasses");
5804    lua_set_string_by_index(L, noad_option_use_callback,               "usecallback");
5805    return 1;
5806}
5807
5808static int texlib_getbalancestepoptionvalues(lua_State *L)
5809{
5810    lua_createtable(L, 2, 0);
5811    lua_push_key_at_index(L, top,    balance_step_option_top);
5812    lua_push_key_at_index(L, bottom, balance_step_option_bottom);
5813    return 1;
5814}
5815
5816static int texlib_getdiscoptionvalues(lua_State *L)
5817{
5818    lua_createtable(L, 2, 7);
5819    lua_set_string_by_index(L, disc_option_normal_word,               "normalword");
5820    lua_set_string_by_index(L, disc_option_pre_word,                  "preword");
5821    lua_set_string_by_index(L, disc_option_post_word,                 "postword");
5822    lua_set_string_by_index(L, disc_option_prefer_break,              "preferbreak");
5823    lua_set_string_by_index(L, disc_option_prefer_nobreak,            "prefernobreak");
5824    lua_set_string_by_index(L, disc_option_no_italic_correction,      "noitaliccorrection");
5825    lua_set_string_by_index(L, disc_option_no_zero_italic_correction, "nozeroitaliccorrection");
5826    lua_set_string_by_index(L, disc_option_user_first,                "userfirst");
5827    lua_set_string_by_index(L, disc_option_user_last,                 "userlast");
5828    return 1;
5829}
5830
5831static int texlib_getruleoptionvalues(lua_State *L)
5832{
5833    lua_createtable(L, 2, 2);
5834    lua_set_string_by_index(L, rule_option_horizontal,  "horizontal");
5835    lua_set_string_by_index(L, rule_option_vertical,    "vertical");
5836    lua_set_string_by_index(L, rule_option_thickness,   "thickness");
5837    lua_set_string_by_index(L, rule_option_running,     "running");
5838    lua_set_string_by_index(L, rule_option_discardable, "discardable");
5839    return 1;
5840}
5841
5842static int texlib_getboxoptionvalues(lua_State *L)
5843{
5844    lua_createtable(L, 2, 0);
5845    lua_set_string_by_index(L, box_option_no_math_axis, "nomathaxis");
5846    lua_set_string_by_index(L, box_option_discardable,  "discardable");
5847    return 1;
5848}
5849
5850static int texlib_getmathsurroundvalues(lua_State *L)
5851{
5852    lua_createtable(L, 2, 6);
5853    lua_set_string_by_index(L, math_skip_surround_when_zero, "zero");
5854    lua_set_string_by_index(L, math_skip_always_left,        "left");
5855    lua_set_string_by_index(L, math_skip_always_right,       "right");
5856    lua_set_string_by_index(L, math_skip_always_both,        "both");
5857    lua_set_string_by_index(L, math_skip_always_surround,    "surround");
5858    lua_set_string_by_index(L, math_skip_ignore,             "ignore");
5859    lua_set_string_by_index(L, math_skip_only_when_skip,     "skip");
5860    lua_set_string_by_index(L, math_skip_only_when_no_skip,  "noskip");
5861    return 1;
5862}
5863
5864static int texlib_getlistanchorvalues(lua_State *L)
5865{
5866    lua_createtable(L, 14, 0);
5867    lua_set_string_by_index(L, left_origin_anchor,    "leftorigin");
5868    lua_set_string_by_index(L, left_height_anchor,    "leftheight");
5869    lua_set_string_by_index(L, left_depth_anchor,     "leftdepth");
5870    lua_set_string_by_index(L, right_origin_anchor,   "rightorigin");
5871    lua_set_string_by_index(L, right_height_anchor,   "rightheight");
5872    lua_set_string_by_index(L, right_depth_anchor,    "rightdepth");
5873    lua_set_string_by_index(L, center_origin_anchor,  "centerorigin");
5874    lua_set_string_by_index(L, center_height_anchor,  "centerheight");
5875    lua_set_string_by_index(L, center_depth_anchor,   "centerdepth");
5876    lua_set_string_by_index(L, halfway_total_anchor,  "halfwaytotal");
5877    lua_set_string_by_index(L, halfway_height_anchor, "halfwayheight");
5878    lua_set_string_by_index(L, halfway_depth_anchor,  "halfwaydepth");
5879    lua_set_string_by_index(L, halfway_left_anchor,   "halfwayleft");
5880    lua_set_string_by_index(L, halfway_right_anchor,  "halfwayright");
5881    return 1;
5882}
5883
5884static int texlib_getlistsignvalues(lua_State *L)
5885{
5886    lua_createtable(L, 0, 2);
5887    lua_set_string_by_index(L, negate_x_anchor, "negatex");
5888    lua_set_string_by_index(L, negate_y_anchor, "negatey");
5889    return 1;
5890}
5891
5892static int texlib_getlistgeometryvalues(lua_State *L)
5893{
5894    lua_createtable(L, 2, 1);
5895    lua_set_string_by_index(L, offset_geometry,      "offset");
5896    lua_set_string_by_index(L, orientation_geometry, "orientation");
5897    lua_set_string_by_index(L, anchor_geometry,      "anchor");
5898    return 1;
5899}
5900
5901
5902static int texlib_getmathgluevalues(lua_State *L)
5903{
5904    lua_createtable(L, 2, 1);
5905    lua_set_string_by_index(L, math_glue_stretch_code, "stretch");
5906    lua_set_string_by_index(L, math_glue_shrink_code,  "shrink");
5907    lua_set_string_by_index(L, math_glue_limit_code,   "limit");
5908    return 1;
5909}
5910
5911
5912static int texlib_getautomigrationvalues(lua_State *L)
5913{
5914    lua_createtable(L, 2, 3);
5915    lua_push_key_at_index(L, mark,   auto_migrate_mark);
5916    lua_push_key_at_index(L, insert, auto_migrate_insert);
5917    lua_push_key_at_index(L, adjust, auto_migrate_adjust);
5918    lua_push_key_at_index(L, pre,    auto_migrate_pre);
5919    lua_push_key_at_index(L, post,   auto_migrate_post);
5920    return 1;
5921}
5922
5923static int texlib_getprotrusionboundaryvalues(lua_State *L)
5924{
5925    lua_createtable(L, 3, 1);
5926    lua_set_string_by_index(L, protrusion_skip_none,     "skipnone");
5927    lua_set_string_by_index(L, protrusion_skip_next,     "skipnext");
5928    lua_set_string_by_index(L, protrusion_skip_previous, "skipprevious");
5929    lua_set_string_by_index(L, protrusion_skip_both,     "skipboth");
5930    return 1;
5931}
5932
5933
5934static int texlib_getlinebreakstatevalues(lua_State *L)
5935{
5936    lua_createtable(L, 2, 8);
5937    lua_set_string_by_index(L, par_has_glyph,    "glyph");
5938    lua_set_string_by_index(L, par_has_disc,     "disc");
5939    lua_set_string_by_index(L, par_has_math,     "math");
5940    lua_set_string_by_index(L, par_has_space,    "space");
5941    lua_set_string_by_index(L, par_has_glue,     "glue");
5942    lua_set_string_by_index(L, par_has_factor,   "factor");
5943    lua_set_string_by_index(L, par_has_uleader,  "uleader");
5944    lua_set_string_by_index(L, par_has_optional, "optional");
5945    lua_set_string_by_index(L, par_is_overfull,  "overfull");
5946    lua_set_string_by_index(L, par_is_underfull, "underfull");
5947    return 1;
5948}
5949
5950static int texlib_getflagvalues(lua_State *L)
5951{
5952    lua_createtable(L, 2, 18);
5953    /* what about the rest */
5954    lua_push_key_at_index(L, frozen,        frozen_flag_bit);
5955    lua_push_key_at_index(L, permanent,     permanent_flag_bit);
5956    lua_push_key_at_index(L, immutable,     immutable_flag_bit);
5957    lua_push_key_at_index(L, primitive,     primitive_flag_bit);
5958    lua_push_key_at_index(L, mutable,       mutable_flag_bit);
5959    lua_push_key_at_index(L, noaligned,     noaligned_flag_bit);
5960    lua_push_key_at_index(L, instance,      instance_flag_bit);
5961    lua_push_key_at_index(L, untraced,      untraced_flag_bit);
5962    lua_push_key_at_index(L, global,        global_flag_bit);
5963    lua_push_key_at_index(L, tolerant,      tolerant_flag_bit);
5964    lua_push_key_at_index(L, protected,     protected_flag_bit);
5965    lua_push_key_at_index(L, overloaded,    overloaded_flag_bit);
5966    lua_push_key_at_index(L, aliased,       aliased_flag_bit);
5967    lua_push_key_at_index(L, immediate,     immediate_flag_bit);
5968    lua_push_key_at_index(L, conditional,   conditional_flag_bit);
5969    lua_push_key_at_index(L, value,         value_flag_bit);
5970    lua_push_key_at_index(L, semiprotected, semiprotected_flag_bit);
5971    lua_push_key_at_index(L, inherited,     inherited_flag_bit);
5972    lua_push_key_at_index(L, constant,      constant_flag_bit);
5973    lua_push_key_at_index(L, deferred,      deferred_flag_bit);
5974    return 1;
5975}
5976
5977static int texlib_getspecialmathclassvalues(lua_State *L)
5978{
5979    lua_createtable(L, 0, 3);
5980    lua_set_string_by_index(L, math_all_class,   "all");
5981    lua_set_string_by_index(L, math_begin_class, "begin");
5982    lua_set_string_by_index(L, math_end_class,   "end");
5983    return 1;
5984}
5985
5986static int texlib_getmathscriptordervalues(lua_State *L)
5987{
5988    lua_createtable(L, 3, 1);
5989    lua_set_string_by_index(L, script_unknown_first,     "unknown");
5990    lua_set_string_by_index(L, script_primescript_first, "primescript");
5991    lua_set_string_by_index(L, script_subscript_first,   "subscript");
5992    lua_set_string_by_index(L, script_superscript_first, "superscript");
5993    return 1;
5994}
5995
5996static int texlib_getmathscriptsmodevalues(lua_State *L)
5997{
5998    lua_createtable(L, 2, 3);
5999    lua_set_string_by_index(L, fixed_super_or_sub_script_code,  "fixedsuperorsub");
6000    lua_set_string_by_index(L, fixed_super_and_sub_script_code, "fixedsuperandsub");
6001    lua_set_string_by_index(L, ignore_empty_super_script_code,  "ignoreemptysuper");
6002    lua_set_string_by_index(L, ignore_empty_sub_script_code,    "ignoreemptysub");
6003    lua_set_string_by_index(L, ignore_empty_prime_script_code,  "ignoreemptyprime");
6004    return 1;
6005}
6006
6007static int texlib_getmathclassoptionvalues(lua_State *L)
6008{
6009    lua_createtable(L, 2, 25);
6010    lua_set_string_by_index(L, no_pre_slack_class_option,                 "nopreslack");
6011    lua_set_string_by_index(L, no_post_slack_class_option,                "nopostslack");
6012    lua_set_string_by_index(L, left_top_kern_class_option,                "lefttopkern");
6013    lua_set_string_by_index(L, right_top_kern_class_option,               "righttopkern");
6014    lua_set_string_by_index(L, left_bottom_kern_class_option,             "leftbottomkern");
6015    lua_set_string_by_index(L, right_bottom_kern_class_option,            "rightbottomkern");
6016    lua_set_string_by_index(L, look_ahead_for_end_class_option,           "lookaheadforend");
6017    lua_set_string_by_index(L, no_italic_correction_class_option,         "noitaliccorrection");
6018    lua_set_string_by_index(L, check_ligature_class_option,               "checkligature");
6019    lua_set_string_by_index(L, check_kern_pair_class_option,              "checkkernpair");
6020    lua_set_string_by_index(L, check_italic_correction_class_option,      "checkitaliccorrection");
6021    lua_set_string_by_index(L, flatten_class_option,                      "flatten");
6022    lua_set_string_by_index(L, omit_penalty_class_option,                 "omitpenalty");
6023 // lua_set_string_by_index(L, open_fence_class_option,                   "openfence");
6024 // lua_set_string_by_index(L, close_fence_class_option,                  "closefence");
6025 // lua_set_string_by_index(L, middle_fence_class_option,                 "middlefence");
6026    lua_set_string_by_index(L, unpack_class_option,                       "unpack");
6027    lua_set_string_by_index(L, raise_prime_option,                        "raiseprime");
6028    lua_set_string_by_index(L, carry_over_left_top_kern_class_option,     "carryoverlefttopkern");
6029    lua_set_string_by_index(L, carry_over_left_bottom_kern_class_option,  "carryoverleftbottomkern");
6030    lua_set_string_by_index(L, carry_over_right_top_kern_class_option,    "carryoverrighttopkern");
6031    lua_set_string_by_index(L, carry_over_right_bottom_kern_class_option, "carryoverrightbottomkern");
6032    lua_set_string_by_index(L, prefer_delimiter_dimensions_class_option,  "preferdelimiterdimensions");
6033    lua_set_string_by_index(L, auto_inject_class_option,                  "autoinject");
6034    lua_set_string_by_index(L, remove_italic_correction_class_option,     "removeitaliccorrection");
6035    lua_set_string_by_index(L, operator_italic_correction_class_option,   "operatoritaliccorrection");
6036    lua_set_string_by_index(L, short_inline_class_option,                 "shortinline");
6037    lua_set_string_by_index(L, push_nesting_class_option,                 "pushnesting");
6038    lua_set_string_by_index(L, pop_nesting_class_option,                  "popnesting");
6039    lua_set_string_by_index(L, obey_nesting_class_option,                 "obeynesting");
6040    return 1;
6041}
6042
6043static int texlib_getnormalizelinevalues(lua_State *L)
6044{
6045    lua_createtable(L, 2, 8);
6046    lua_set_string_by_index(L, normalize_line_mode,          "normalizeline");
6047    lua_set_string_by_index(L, parindent_skip_mode,          "parindentskip");
6048    lua_set_string_by_index(L, swap_hangindent_mode,         "swaphangindent");
6049    lua_set_string_by_index(L, swap_parshape_mode,           "swapparshape");
6050    lua_set_string_by_index(L, break_after_dir_mode,         "breakafterdir");
6051    lua_set_string_by_index(L, remove_margin_kerns_mode,     "removemarginkerns");
6052    lua_set_string_by_index(L, clip_width_mode,              "clipwidth");
6053    lua_set_string_by_index(L, flatten_discretionaries_mode, "flattendiscretionaries");
6054    lua_set_string_by_index(L, discard_zero_tab_skips_mode,  "discardzerotabskips");
6055    lua_set_string_by_index(L, flatten_h_leaders_mode,       "flattenhleaders");
6056    lua_set_string_by_index(L, balance_inline_math_mode,     "balanceinlinemath");
6057    return 1;
6058}
6059
6060static int texlib_getnormalizeparvalues(lua_State *L)
6061{
6062    lua_createtable(L, 2, 3);
6063    lua_set_string_by_index(L, normalize_par_mode,            "normalizepar");
6064    lua_set_string_by_index(L, flatten_v_leaders_mode,        "flattenvleaders");
6065    lua_set_string_by_index(L, limit_prev_graf_mode,          "limitprevgraf");
6066    lua_set_string_by_index(L, keep_interline_penalties_mode, "keepinterlinepenalties");
6067    lua_set_string_by_index(L, remove_trailing_spaces_mode,   "removetrailingspaces");
6068    return 1;
6069}
6070
6071static int texlib_getunitclassvalues(lua_State *L)
6072{
6073    lua_createtable(L, 4, 1);
6074    lua_set_string_by_index(L, unset_unit_class,      "unset");
6075    lua_set_string_by_index(L, tex_unit_class,        "tex");
6076    lua_set_string_by_index(L, pdftex_unit_class,     "pdftex");
6077    lua_set_string_by_index(L, luametatex_unit_class, "luametatex");
6078    lua_set_string_by_index(L, user_unit_class,       "user");
6079    return 1;
6080}
6081
6082static int texlib_geterrorvalues(lua_State *L)
6083{
6084    lua_createtable(L, 7, 1);
6085    lua_set_string_by_index(L, normal_error_type,   "normal");
6086    lua_set_string_by_index(L, back_error_type,     "back");
6087    lua_set_string_by_index(L, insert_error_type,   "insert");
6088    lua_set_string_by_index(L, succumb_error_type,  "succumb");
6089    lua_set_string_by_index(L, eof_error_type,      "eof");
6090    lua_set_string_by_index(L, condition_error_type,"condition");
6091    lua_set_string_by_index(L, runaway_error_type,  "runaway");
6092    lua_set_string_by_index(L, warning_error_type,  "warning");
6093    return 1;
6094}
6095
6096static int texlib_getiovalues(lua_State *L) /* for reporting so we keep spaces */
6097{
6098    lua_createtable(L, 5, 1);
6099    lua_set_string_by_index(L, io_initial_input_code,   "initial");
6100    lua_set_string_by_index(L, io_lua_input_code,       "lua print");
6101    lua_set_string_by_index(L, io_token_input_code,     "scan token");
6102    lua_set_string_by_index(L, io_token_eof_input_code, "scan token eof");
6103    lua_set_string_by_index(L, io_tex_macro_code,       "tex macro");
6104    lua_set_string_by_index(L, io_file_input_code,      "file");
6105    return 1;
6106}
6107
6108static int texlib_getfrozenparvalues(lua_State *L)
6109{
6110    lua_createtable(L, 2, 23);
6111    lua_set_string_by_index(L, par_hsize_category,               "hsize");
6112    lua_set_string_by_index(L, par_skip_category,                "skip");
6113    lua_set_string_by_index(L, par_hang_category,                "hang");
6114    lua_set_string_by_index(L, par_indent_category,              "indent");
6115    lua_set_string_by_index(L, par_par_fill_category,            "parfill");
6116    lua_set_string_by_index(L, par_adjust_category,              "adjust");
6117    lua_set_string_by_index(L, par_protrude_category,            "protrude");
6118    lua_set_string_by_index(L, par_tolerance_category,           "tolerance");
6119    lua_set_string_by_index(L, par_stretch_category,             "stretch");
6120    lua_set_string_by_index(L, par_looseness_category,           "looseness");
6121    lua_set_string_by_index(L, par_last_line_category,           "lastline");
6122    lua_set_string_by_index(L, par_line_penalty_category,        "linepenalty");
6123    lua_set_string_by_index(L, par_club_penalty_category,        "clubpenalty");
6124    lua_set_string_by_index(L, par_widow_penalty_category,       "widowpenalty");
6125    lua_set_string_by_index(L, par_display_penalty_category,     "displaypenalty");
6126    lua_set_string_by_index(L, par_broken_penalty_category,      "brokenpenalty");
6127    lua_set_string_by_index(L, par_demerits_category,            "demerits");
6128    lua_set_string_by_index(L, par_shape_category,               "shape");
6129    lua_set_string_by_index(L, par_line_category,                "line");
6130    lua_set_string_by_index(L, par_hyphenation_category,         "hyphenation");
6131    lua_set_string_by_index(L, par_shaping_penalty_category,     "shapingpenalty");
6132    lua_set_string_by_index(L, par_orphan_penalty_category,      "orphanpenalty");
6133    lua_set_string_by_index(L, par_toddler_penalty_category,     "toddlerpenalty");
6134    lua_set_string_by_index(L, par_emergency_category,           "emergency");
6135    lua_set_string_by_index(L, par_par_passes_category,          "parpasses");
6136    lua_set_string_by_index(L, par_single_line_penalty_category, "singlelinepenalty");
6137    lua_set_string_by_index(L, par_hyphen_penalty_category,      "hyphenpenalty");
6138    lua_set_string_by_index(L, par_ex_hyphen_penalty_category,   "exhyphenpenalty");
6139    lua_set_string_by_index(L, par_line_break_checks_category,   "linebreakchecks");
6140    lua_set_string_by_index(L, par_twin_demerits_category,       "twindemerits");
6141    lua_set_string_by_index(L, par_fitness_classes_category,     "fitnessclasses");
6142 /* lua_set_string_by_index(L, par_all_category,                 "all"); */
6143    return 1;
6144}
6145
6146static int texlib_getdoublescriptoptionvalues(lua_State *L)
6147{
6148    lua_createtable(L, 2, 2);
6149    lua_set_string_by_index(L, inherit_class_double_atom_option,      "inheritclass");
6150    lua_set_string_by_index(L, discard_shape_kern_double_atom_option, "discardshapekern");
6151    lua_set_string_by_index(L, realign_scripts_double_atom_option,    "realignscripts");
6152    lua_set_string_by_index(L, reorder_double_pre_script_atom_option, "reorderprescripts");
6153    return 1;
6154}
6155
6156
6157static int texlib_getkerneloptionvalues(lua_State *L)
6158{
6159    lua_createtable(L, 2, 6);
6160    lua_set_string_by_index(L, math_kernel_no_italic_correction, "noitaliccorrection");
6161    lua_set_string_by_index(L, math_kernel_no_left_pair_kern,    "noleftpairkern");
6162    lua_set_string_by_index(L, math_kernel_no_right_pair_kern,   "norightpairkern");
6163    lua_set_string_by_index(L, math_kernel_auto_discretionary,   "autodiscretionary");
6164    lua_set_string_by_index(L, math_kernel_full_discretionary,   "fulldiscretionary");
6165    lua_set_string_by_index(L, math_kernel_ignored_character,    "ignoredcharacter");
6166    lua_set_string_by_index(L, math_kernel_is_large_operator,    "islargeoperator");
6167    lua_set_string_by_index(L, math_kernel_has_italic_shape,     "hasitalicshape");
6168    return 1;
6169}
6170
6171static int texlib_getcharactertagvalues(lua_State *L)
6172{
6173    lua_createtable(L, 2, 21);
6174    lua_set_string_by_index(L, no_tag,               "normal");
6175    lua_set_string_by_index(L, ligatures_tag,        "ligatures");
6176    lua_set_string_by_index(L, kerns_tag,            "kerns");
6177    lua_set_string_by_index(L, list_tag,             "list");
6178    lua_set_string_by_index(L, callback_tag,         "callback");
6179    lua_set_string_by_index(L, extensible_tag,       "extensible");
6180    lua_set_string_by_index(L, horizontal_tag,       "horizontal");
6181    lua_set_string_by_index(L, vertical_tag,         "vertical");
6182    lua_set_string_by_index(L, inner_left_tag,       "innerleft");
6183    lua_set_string_by_index(L, inner_right_tag,      "innerright");
6184    lua_set_string_by_index(L, inner_top_tag,        "innertop");
6185    lua_set_string_by_index(L, inner_bottom_tag,     "innerbottom");
6186    lua_set_string_by_index(L, extend_last_tag,      "extendlast");
6187    lua_set_string_by_index(L, italic_tag,           "italic");
6188    lua_set_string_by_index(L, n_ary_tag,            "nary");
6189    lua_set_string_by_index(L, radical_tag,          "radical");
6190    lua_set_string_by_index(L, punctuation_tag,      "punctuation");
6191    lua_set_string_by_index(L, keep_base_tag,        "keepbase");
6192    lua_set_string_by_index(L, expansion_tag,        "expansion");
6193    lua_set_string_by_index(L, protrusion_tag,       "protrusion");
6194    lua_set_string_by_index(L, above_baseline_tag,   "abovebaseline");
6195    lua_set_string_by_index(L, below_baseline_tag,   "belowbaseline");
6196    lua_set_string_by_index(L, force_extensible_tag, "forceextensible");
6197    return 1;
6198}
6199
6200static int texlib_getshapingpenaltiesvalues(lua_State *L)
6201{
6202    lua_createtable(L, 2, 2);
6203    lua_push_key_at_index(L, interlinepenalty, inter_line_penalty_shaping);
6204    lua_push_key_at_index(L, widowpenalty,     widow_penalty_shaping);
6205    lua_push_key_at_index(L, clubpenalty,      club_penalty_shaping);
6206    lua_push_key_at_index(L, brokenpenalty,    broken_penalty_shaping);
6207    return 1;
6208}
6209
6210static int texlib_getbadnessmodevalues(lua_State *L)
6211{
6212    lua_createtable(L, 2, 2);
6213    lua_set_string_by_index(L, badness_mode_underfull, "underfull");
6214    lua_set_string_by_index(L, badness_mode_loose,     "loose");
6215    lua_set_string_by_index(L, badness_mode_tight,     "tight");
6216    lua_set_string_by_index(L, badness_mode_overfull,  "overfull");
6217    return 1;
6218}
6219
6220static int texlib_getautoparagraphvalues(lua_State *L)
6221{
6222    lua_createtable(L, 2, 1);
6223    lua_set_string_by_index(L, auto_paragraph_text,  "text");
6224    lua_set_string_by_index(L, auto_paragraph_macro, "macro");
6225    lua_set_string_by_index(L, auto_paragraph_go_on, "continue");
6226    return 1;
6227}
6228
6229static int texlib_getprimitiveorigins(lua_State *L)
6230{
6231    lua_createtable(L, 2, 1);
6232    lua_push_key_at_index(L, tex,    tex_command);
6233    lua_push_key_at_index(L, etex,   etex_command);
6234    lua_push_key_at_index(L, luatex, luatex_command);
6235    return 1;
6236}
6237
6238static int texlib_getmissingcharactervalues(lua_State *L)
6239{
6240    lua_createtable(L, 2, 1);
6241    lua_set_string_by_index(L,missing_character_text_glyph,  "textglyph");
6242    lua_set_string_by_index(L,missing_character_math_glyph,  "mathglyph");
6243    lua_set_string_by_index(L,missing_character_math_kernel, "mathkernel");
6244    return 1;
6245}
6246
6247static int texlib_getinteractionmodes(lua_State *L)
6248{
6249    lua_createtable(L, 4, 0);
6250    lua_set_string_by_index(L, batch_mode,      "batch");
6251    lua_set_string_by_index(L, nonstop_mode,    "nonstop");
6252    lua_set_string_by_index(L, scroll_mode,     "scroll");
6253    lua_set_string_by_index(L, error_stop_mode, "errorstop");
6254    return 1;
6255}
6256
6257static int texlib_getiftypes(lua_State *L)
6258{
6259    lua_createtable(L, 70, 0);
6260    lua_set_string_by_index(L, if_char_code           - first_real_if_test_code, "char");
6261    lua_set_string_by_index(L, if_cat_code            - first_real_if_test_code, "cat");
6262    lua_set_string_by_index(L, if_int_code            - first_real_if_test_code, "num");
6263    lua_set_string_by_index(L, if_abs_int_code        - first_real_if_test_code, "absnum");
6264    lua_set_string_by_index(L, if_zero_int_code       - first_real_if_test_code, "zeronum");
6265    lua_set_string_by_index(L, if_interval_int_code   - first_real_if_test_code, "intervalnum");
6266    lua_set_string_by_index(L, if_posit_code          - first_real_if_test_code, "float");
6267    lua_set_string_by_index(L, if_abs_posit_code      - first_real_if_test_code, "absfloat");
6268    lua_set_string_by_index(L, if_zero_posit_code     - first_real_if_test_code, "zerofloat");
6269    lua_set_string_by_index(L, if_interval_posit_code - first_real_if_test_code, "intervalfloat");
6270    lua_set_string_by_index(L, if_dim_code            - first_real_if_test_code, "dim");
6271    lua_set_string_by_index(L, if_abs_dim_code        - first_real_if_test_code, "absdim");
6272    lua_set_string_by_index(L, if_zero_dim_code       - first_real_if_test_code, "zerodim");
6273    lua_set_string_by_index(L, if_interval_dim_code   - first_real_if_test_code, "intervaldim");
6274    lua_set_string_by_index(L, if_odd_code            - first_real_if_test_code, "odd");
6275    lua_set_string_by_index(L, if_vmode_code          - first_real_if_test_code, "vmode");
6276    lua_set_string_by_index(L, if_hmode_code          - first_real_if_test_code, "hmode");
6277    lua_set_string_by_index(L, if_mmode_code          - first_real_if_test_code, "mmode");
6278    lua_set_string_by_index(L, if_inner_code          - first_real_if_test_code, "inner");
6279    lua_set_string_by_index(L, if_void_code           - first_real_if_test_code, "void");
6280    lua_set_string_by_index(L, if_hbox_code           - first_real_if_test_code, "hbox");
6281    lua_set_string_by_index(L, if_vbox_code           - first_real_if_test_code, "vbox");
6282    lua_set_string_by_index(L, if_cstok_code          - first_real_if_test_code, "cstoken");
6283    lua_set_string_by_index(L, if_tok_code            - first_real_if_test_code, "tok");
6284    lua_set_string_by_index(L, if_x_code              - first_real_if_test_code, "x");
6285 /* lua_set_string_by_index(L, if_eof_code            - first_real_if_test_code, "eof"); */ /*not used */
6286    lua_set_string_by_index(L, if_true_code           - first_real_if_test_code, "true");
6287    lua_set_string_by_index(L, if_false_code          - first_real_if_test_code, "false");
6288    lua_set_string_by_index(L, if_chk_int_code        - first_real_if_test_code, "chknum");
6289    lua_set_string_by_index(L, if_chk_integer_code    - first_real_if_test_code, "chknumber");
6290    lua_set_string_by_index(L, if_chk_intexpr_code    - first_real_if_test_code, "chknumexpr");
6291    lua_set_string_by_index(L, if_val_int_code        - first_real_if_test_code, "numval");
6292    lua_set_string_by_index(L, if_cmp_int_code        - first_real_if_test_code, "cmpnum");
6293    lua_set_string_by_index(L, if_chk_dim_code        - first_real_if_test_code, "chkdim");
6294    lua_set_string_by_index(L, if_chk_dimension_code  - first_real_if_test_code, "chkdimension");
6295    lua_set_string_by_index(L, if_chk_dimexpr_code    - first_real_if_test_code, "chkdimexpr");
6296    lua_set_string_by_index(L, if_val_dim_code        - first_real_if_test_code, "dimval");
6297    lua_set_string_by_index(L, if_cmp_dim_code        - first_real_if_test_code, "cmpdim");
6298    lua_set_string_by_index(L, if_case_code           - first_real_if_test_code, "case");
6299    lua_set_string_by_index(L, if_defined_code        - first_real_if_test_code, "defined");
6300    lua_set_string_by_index(L, if_csname_code         - first_real_if_test_code, "csname");
6301    lua_set_string_by_index(L, if_font_char_code      - first_real_if_test_code, "fontchar");
6302    lua_set_string_by_index(L, if_posit_code          - first_real_if_test_code, "float");
6303    lua_set_string_by_index(L, if_tok_code            - first_real_if_test_code, "tok");
6304    lua_set_string_by_index(L, if_in_csname_code      - first_real_if_test_code, "incsname");
6305    lua_set_string_by_index(L, if_font_char_code      - first_real_if_test_code, "fontchar");
6306    lua_set_string_by_index(L, if_condition_code      - first_real_if_test_code, "condition");
6307    lua_set_string_by_index(L, if_flags_code          - first_real_if_test_code, "flags");
6308    lua_set_string_by_index(L, if_empty_code          - first_real_if_test_code, "empty");
6309    lua_set_string_by_index(L, if_relax_code          - first_real_if_test_code, "relax");
6310    lua_set_string_by_index(L, if_boolean_code        - first_real_if_test_code, "boolean");
6311    lua_set_string_by_index(L, if_numexpression_code  - first_real_if_test_code, "numexpression");
6312    lua_set_string_by_index(L, if_dimexpression_code  - first_real_if_test_code, "dimexpression");
6313    lua_set_string_by_index(L, if_math_parameter_code - first_real_if_test_code, "mathparameter");
6314    lua_set_string_by_index(L, if_math_style_code     - first_real_if_test_code, "mathstyle");
6315    lua_set_string_by_index(L, if_arguments_code      - first_real_if_test_code, "arguments");
6316    lua_set_string_by_index(L, if_parameters_code     - first_real_if_test_code, "parameters");
6317    lua_set_string_by_index(L, if_parameter_code      - first_real_if_test_code, "parameter");
6318    lua_set_string_by_index(L, if_has_tok_code        - first_real_if_test_code, "hastok");
6319    lua_set_string_by_index(L, if_has_toks_code       - first_real_if_test_code, "hastoks");
6320    lua_set_string_by_index(L, if_has_xtoks_code      - first_real_if_test_code, "hasxtoks");
6321    lua_set_string_by_index(L, if_has_char_code       - first_real_if_test_code, "haschar");
6322    lua_set_string_by_index(L, if_insert_code         - first_real_if_test_code, "insert");
6323    lua_set_string_by_index(L, if_in_alignment_code   - first_real_if_test_code, "inalignment");
6324    lua_set_string_by_index(L, if_cramped_code        - first_real_if_test_code, "ifcramped");
6325    lua_set_string_by_index(L, if_list_code           - first_real_if_test_code, "iflist");
6326 /* lua_set_string_by_index(L, if_bitwise_and_code    - first_real_if_test_code, "bitwiseand"); */ /* not (yet) used */
6327    return 1;
6328}
6329
6330static int texlib_getlargestusedmark(lua_State* L)
6331{
6332    lua_pushinteger(L, lmt_mark_state.mark_data.ptr);
6333    return 1;
6334}
6335
6336static int texlib_getusedmarks(lua_State *L)
6337{
6338    if (lmt_mark_state.min_used >= 0) {
6339        lua_Integer index = 0;
6340        for (halfword m = lmt_mark_state.min_used; m <= lmt_mark_state.max_used; m++) {
6341            if (lmt_mark_state.data[m].state) { 
6342                if (! index) { 
6343                    lua_createtable(L, 0, 0);
6344                }
6345                lua_pushinteger(L, m);
6346                lua_rawseti(L, -2, ++index);
6347            }
6348        }
6349        if (index) { 
6350            return 1;
6351        }
6352    }
6353    lua_pushnil(L);
6354    return 1;
6355}
6356
6357static int texlib_getoutputactive(lua_State* L)
6358{
6359    lua_pushboolean(L, lmt_page_builder_state.output_active);
6360    return 1;
6361}
6362
6363/*tex Moved from lmtnodelib to here. */
6364
6365int lmt_push_info_keys(lua_State *L, value_info *values)
6366{
6367    lua_newtable(L);
6368    for (int i = 0; values[i].name; i++) {
6369        lua_rawgeti(L, LUA_REGISTRYINDEX, values[i].lua);
6370        lua_rawseti(L, -2, i);
6371    }
6372    return 1;
6373}
6374
6375int lmt_push_info_values(lua_State *L, value_info *values)
6376{
6377    lua_newtable(L);
6378    for (int i = 0; values[i].name; i++) {
6379        lua_rawgeti(L, LUA_REGISTRYINDEX, values[i].lua);
6380        lua_rawseti(L, -2, values[i].value);
6381    }
6382    return 1;
6383}
6384
6385static int texlib_getgroupvalues(lua_State *L)
6386{
6387    return lmt_push_info_values(L, lmt_interface.group_code_values);
6388}
6389
6390static int texlib_getmathparametervalues(lua_State *L)
6391{
6392    return lmt_push_info_keys(L, lmt_interface.math_parameter_values);
6393}
6394
6395static int texlib_getmathstylevalues(lua_State* L)
6396{
6397    return lmt_push_info_values(L, lmt_interface.math_style_values);
6398}
6399
6400static int texlib_getpacktypevalues(lua_State *L)
6401{
6402    return lmt_push_info_values(L, lmt_interface.pack_type_values);
6403}
6404
6405static int texlib_getparcontextvalues(lua_State *L)
6406{
6407    return lmt_push_info_values(L, lmt_interface.par_context_values);
6408}
6409
6410static int texlib_getpagecontextvalues(lua_State *L)
6411{
6412    return lmt_push_info_values(L, lmt_interface.page_context_values);
6413}
6414
6415static int texlib_getappendlinecontextvalues(lua_State *L)
6416{
6417    return lmt_push_info_values(L, lmt_interface.append_line_context_values);
6418}
6419
6420static int texlib_getalignmentcontextvalues(lua_State *L)
6421{
6422    return lmt_push_info_values(L, lmt_interface.alignment_context_values);
6423}
6424
6425static int texlib_getbreakcontextvalues(lua_State *L)
6426{
6427    return lmt_push_info_values(L, lmt_interface.line_break_context_values);
6428}
6429
6430static int texlib_getbuildcontextvalues(lua_State *L)
6431{
6432    return lmt_push_info_values(L, lmt_interface.build_context_values);
6433}
6434
6435static int texlib_getvsplitcontextvalues(lua_State *L)
6436{
6437    return lmt_push_info_values(L, lmt_interface.vsplit_context_values);
6438}
6439
6440static int texlib_getpartriggervalues(lua_State *L)
6441{
6442    return lmt_push_info_values(L, lmt_interface.par_trigger_values);
6443}
6444
6445static int texlib_getparmodevalues(lua_State *L)
6446{
6447    return lmt_push_info_values(L, lmt_interface.par_mode_values);
6448}
6449
6450static int texlib_getmathstylenamevalues(lua_State *L)
6451{
6452    return lmt_push_info_values(L, lmt_interface.math_style_name_values);
6453}
6454
6455static int texlib_getmathvariantvalues(lua_State *L)
6456{
6457    return lmt_push_info_values(L, lmt_interface.math_style_variant_values);
6458}
6459
6460static int texlib_getmathvariantpresets(lua_State *L)
6461{
6462    if (lua_type(L, 1) == LUA_TNUMBER) {
6463        lua_pushinteger(L, tex_get_math_variant_preset(lmt_tointeger(L, 1)));
6464    } else {
6465        lua_createtable(L, 8, 1);
6466        for (int i = 0; i <= last_math_style_variant; i++) {
6467            lua_set_integer_by_index(L, i, tex_get_math_variant_preset(i));
6468        }
6469    }
6470    return 1;
6471}
6472
6473static int texlib_getspecificationoptionvalues(lua_State *L)
6474{
6475    lua_createtable(L, 2, 7);
6476    lua_set_string_by_index(L, specification_option_repeat,  "repeat");
6477    lua_set_string_by_index(L, specification_option_double,  "double");
6478    lua_set_string_by_index(L, specification_option_largest, "largest");
6479    lua_set_string_by_index(L, specification_option_presets, "presets");
6480    lua_set_string_by_index(L, specification_option_integer, "integer");
6481    lua_set_string_by_index(L, specification_option_final,   "final");
6482    lua_set_string_by_index(L, specification_option_default, "default");
6483    lua_set_string_by_index(L, specification_option_ignore,  "ignore");
6484    lua_set_string_by_index(L, specification_option_rotate,  "rotate");
6485    return 1;
6486}
6487
6488static int texlib_getglyphprotectionvalues(lua_State *L)
6489{
6490    lua_createtable(L, 2, 1);
6491    lua_set_string_by_index(L, glyph_unprotected_code,    "unset");
6492    lua_set_string_by_index(L, glyph_protected_text_code, "text");
6493    lua_set_string_by_index(L, glyph_protected_math_code, "math");
6494    return 1;
6495}
6496
6497static int texlib_getdiscpartvalues(lua_State *L)
6498{
6499    lua_createtable(L, 4, 1);
6500    lua_set_string_by_index(L, glyph_discpart_unset,   "unset");
6501    lua_set_string_by_index(L, glyph_discpart_pre,     "pre");
6502    lua_set_string_by_index(L, glyph_discpart_post,    "post");
6503    lua_set_string_by_index(L, glyph_discpart_replace, "replace");
6504    lua_set_string_by_index(L, glyph_discpart_always,  "always");
6505    return 1;
6506}
6507
6508static int texlib_getglyphdiscvalues(lua_State *L)
6509{
6510    lua_createtable(L, 5, 0);
6511    lua_set_string_by_index(L, glyph_disc_normal,      "normal");
6512    lua_set_string_by_index(L, glyph_disc_explicit,    "explicit");
6513    lua_set_string_by_index(L, glyph_disc_automatic,   "automatic");
6514    lua_set_string_by_index(L, glyph_disc_mathematics, "mathematics");
6515    lua_set_string_by_index(L, glyph_disc_syllable,    "syllable");
6516    return 1;
6517}
6518
6519static int texlib_getmathcontrolvalues(lua_State *L)
6520{
6521    lua_createtable(L, 2, 24);
6522    lua_set_string_by_index(L, math_control_use_font_control,            "usefontcontrol");
6523    lua_set_string_by_index(L, math_control_over_rule,                   "overrule");
6524    lua_set_string_by_index(L, math_control_under_rule,                  "underrule");
6525    lua_set_string_by_index(L, math_control_radical_rule,                "radicalrule");
6526    lua_set_string_by_index(L, math_control_fraction_rule,               "fractionrule");
6527    lua_set_string_by_index(L, math_control_accent_skew_half,            "accentskewhalf");
6528    lua_set_string_by_index(L, math_control_accent_skew_apply,           "accentskewapply");
6529    lua_set_string_by_index(L, math_control_apply_ordinary_kern_pair,    "applyordinarykernpair");
6530    lua_set_string_by_index(L, math_control_apply_vertical_italic_kern,  "applyverticalitalickern");
6531    lua_set_string_by_index(L, math_control_apply_ordinary_italic_kern,  "applyordinaryitalickern");
6532    lua_set_string_by_index(L, math_control_apply_char_italic_kern,      "applycharitalickern");
6533    lua_set_string_by_index(L, math_control_rebox_char_italic_kern,      "reboxcharitalickern");
6534    lua_set_string_by_index(L, math_control_apply_boxed_italic_kern,     "applyboxeditalickern");
6535    lua_set_string_by_index(L, math_control_staircase_kern,              "staircasekern");
6536    lua_set_string_by_index(L, math_control_apply_text_italic_kern,      "applytextitalickern");
6537    lua_set_string_by_index(L, math_control_check_text_italic_kern,      "checktextitalickern");
6538    lua_set_string_by_index(L, math_control_check_space_italic_kern,     "checkspaceitalickern");
6539    lua_set_string_by_index(L, math_control_apply_script_italic_kern,    "applyscriptitalickern");
6540    lua_set_string_by_index(L, math_control_analyze_script_nucleus_char, "analyzescriptnucleuschar");
6541    lua_set_string_by_index(L, math_control_analyze_script_nucleus_list, "analyzescriptnucleuslist");
6542    lua_set_string_by_index(L, math_control_analyze_script_nucleus_box,  "analyzescriptnucleusbox");
6543    lua_set_string_by_index(L, math_control_accent_top_skew_with_offset, "accenttopskewwithoffset");
6544    lua_set_string_by_index(L, math_control_ignore_kern_dimensions,      "ignorekerndimensions");
6545    lua_set_string_by_index(L, math_control_ignore_flat_accents,         "ignoreflataccents");
6546    lua_set_string_by_index(L, math_control_extend_accents,              "extendaccents");
6547    lua_set_string_by_index(L, math_control_extend_delimiters,           "extenddelimiters");
6548    return 1;
6549}
6550
6551static int texlib_gettextcontrolvalues(lua_State *L)
6552{
6553    lua_createtable(L, 2, 5);
6554    lua_set_string_by_index(L, text_control_collapse_hyphens,   "collapsehyphens");
6555    lua_set_string_by_index(L, text_control_base_ligaturing,    "baseligaturing");
6556    lua_set_string_by_index(L, text_control_base_kerning,       "basekerning");
6557    lua_set_string_by_index(L, text_control_none_protected,     "noneprotected");
6558    lua_set_string_by_index(L, text_control_has_italics,        "hasitalics");
6559    lua_set_string_by_index(L, text_control_auto_italics,       "autoitalics");
6560    lua_set_string_by_index(L, text_control_replace_apostrophe, "replaceapostrophe");
6561    return 1;
6562}
6563
6564static int texlib_getprepoststatevalues(lua_State *L)
6565{
6566    lua_createtable(L, 2, 2);
6567    lua_set_string_by_index(L, has_pre_adjust,    "preadjust");
6568    lua_set_string_by_index(L, has_post_adjust,   "postadjust");
6569    lua_set_string_by_index(L, has_pre_migrated,  "premigrated");
6570    lua_set_string_by_index(L, has_post_migrated, "postmigrated");
6571    return 1;
6572}
6573
6574static int texlib_getadjustoptionvalues(lua_State *L)
6575{
6576    lua_createtable(L, 2, 6);
6577    lua_set_string_by_index(L, adjust_option_none,         "none");
6578    lua_set_string_by_index(L, adjust_option_before,       "before");
6579    lua_set_string_by_index(L, adjust_option_baseline,     "baseline");
6580    lua_set_string_by_index(L, adjust_option_depth_before, "depthbefore");
6581    lua_set_string_by_index(L, adjust_option_depth_after,  "depthafter");
6582    lua_set_string_by_index(L, adjust_option_depth_check,  "depthcheck");
6583    lua_set_string_by_index(L, adjust_option_depth_last,   "depthlast");
6584    lua_set_string_by_index(L, adjust_option_except,       "except");
6585    return 1;
6586}
6587
6588static int texlib_getemptyparagraphmodevalues(lua_State *L)
6589{
6590    lua_createtable(L, 2, 2);
6591    lua_set_string_by_index(L, effective_empty_option_par,         "par");
6592    lua_set_string_by_index(L, effective_empty_option_dir,         "dir");
6593    lua_set_string_by_index(L, effective_empty_option_indent_list, "indentlist");
6594    lua_set_string_by_index(L, effective_empty_option_indent_glue, "indentglue");
6595    return 1;
6596}
6597
6598static int texlib_getfillvalues(lua_State *L)
6599{
6600    return lmt_push_info_values(L, lmt_interface.node_fill_values);
6601}
6602
6603static int texlib_getdirectionvalues(lua_State *L)
6604{
6605    return lmt_push_info_values(L, lmt_interface.direction_values);
6606}
6607
6608static int texlib_getparametermodevalues(lua_State *L)
6609{
6610    lua_createtable(L, 1, 0);
6611    lua_set_string_by_index(L, parameter_escape_mode, "escape");
6612    return 1;
6613}
6614
6615static int texlib_getcharactercontrolvalues(lua_State *L)
6616{
6617    lua_createtable(L, 1, 0);
6618    lua_set_string_by_index(L, ignore_twin_character_control_code, "ignoretwin");
6619    return 1;
6620}
6621
6622/* relatively new */
6623
6624static int texlib_getinsertdistance(lua_State *L)
6625{
6626    return texlib_aux_push_glue(L, tex_get_insert_distance(lmt_tointeger(L, 1)));
6627}
6628
6629static int texlib_getinsertmultiplier(lua_State *L)
6630{
6631    lua_pushinteger(L, tex_get_insert_multiplier(lmt_tointeger(L, 1)));
6632    return 1;
6633}
6634
6635static int texlib_getinsertlimit(lua_State *L)
6636{
6637    tex_set_insert_limit(lmt_tointeger(L, 1), lmt_opthalfword(L, 2, 0));
6638    return 0;
6639}
6640
6641static int texlib_setinsertdistance(lua_State *L)
6642{
6643    tex_set_insert_distance(lmt_tointeger(L, 1), texlib_aux_make_glue(L, lua_gettop(L), 2));
6644    return 0;
6645}
6646
6647static int texlib_setinsertmultiplier(lua_State *L)
6648{
6649    tex_set_insert_multiplier(lmt_tointeger(L, 1), lmt_tohalfword(L, 2));
6650    return 0;
6651}
6652
6653static int texlib_setinsertlimit(lua_State *L)
6654{
6655    lua_pushinteger(L, tex_get_insert_limit(lmt_tointeger(L, 1)));
6656    return 1;
6657}
6658
6659static int texlib_getinsertheight(lua_State *L)
6660{
6661    lua_pushinteger(L, tex_get_insert_height(lmt_tointeger(L, 1)));
6662    return 1;
6663}
6664
6665static int texlib_getinsertdepth(lua_State *L)
6666{
6667    lua_pushinteger(L, tex_get_insert_depth(lmt_tointeger(L, 1)));
6668    return 1;
6669}
6670
6671static int texlib_getinsertwidth(lua_State *L)
6672{
6673    lua_pushinteger(L, tex_get_insert_width(lmt_tointeger(L, 1)));
6674    return 1;
6675}
6676
6677static int texlib_getinsertcontent(lua_State *L)
6678{
6679    halfword index = lmt_tointeger(L, 1);
6680    lmt_node_list_to_lua(L, tex_get_insert_content(index));
6681    tex_set_insert_content(index, null);
6682    return 1;
6683}
6684
6685static int texlib_setinsertcontent(lua_State *L)
6686{
6687    halfword index = lmt_tointeger(L, 1);
6688    tex_flush_node(tex_get_insert_content(index));
6689    tex_set_insert_content(index, lmt_node_list_from_lua(L, 2));
6690    return 0;
6691}
6692
6693static int texlib_getmvloptionvalues(lua_State *L)
6694{
6695    lua_createtable(L, 2, 2);
6696    lua_set_string_by_index(L, mvl_ignore_prev_depth, "ignoreprevdepth");
6697    lua_set_string_by_index(L, mvl_no_prev_depth,     "noprevdepth");
6698    lua_set_string_by_index(L, mvl_discard_top,       "discardtop");
6699    lua_set_string_by_index(L, mvl_discard_bottom,    "discardbottom");
6700    return 1;
6701}
6702
6703static int texlib_getbalancecallbackvalues(lua_State *L)
6704{
6705    lua_createtable(L, 2, 1);
6706    lua_set_string_by_index(L, balance_callback_nothing,    "nothing");
6707    lua_set_string_by_index(L, balance_callback_try_break,  "trybreak"); 
6708    lua_set_string_by_index(L, balance_callback_skip_zeros, "skipzeros");
6709    return 1;
6710}
6711
6712static int texlib_getlocalboxlocations(lua_State *L)
6713{
6714    lua_createtable(L, 2, 1);
6715    lua_push_key_at_index(L, left,   local_left_box_code);
6716    lua_push_key_at_index(L, right,  local_right_box_code);
6717    lua_push_key_at_index(L, middle, local_middle_box_code);
6718    return 1;
6719}
6720
6721static inline int tex_aux_local_box_location(lua_State *L)
6722{
6723    switch (lua_type(L, 1)) {
6724        case LUA_TNUMBER:
6725            {
6726                int location = lmt_tointeger(L, 1);
6727                if (is_valid_local_box_code(location)) {
6728                    return location;
6729                } else {
6730                    break;
6731                }
6732            }
6733        case LUA_TSTRING:
6734            {
6735                const char *str = lua_tostring(L, 1);
6736                if (lua_key_eq(str, left)) {
6737                    return local_left_box_code;
6738                } else if (lua_key_eq(str, right)) {
6739                    return local_right_box_code;
6740                } else if (lua_key_eq(str, middle)) {
6741                    return local_middle_box_code;
6742                } else {
6743                    break;
6744                }
6745            }
6746
6747    }
6748    return -1;
6749}
6750
6751static int texlib_getlocalbox(lua_State *L)
6752{
6753    int location = tex_aux_local_box_location(L);
6754    if (location >= 0) {
6755        lmt_node_list_to_lua(L, tex_get_local_boxes(location));
6756    } else {
6757        lua_pushnil(L);
6758    }
6759    return 1;
6760}
6761
6762static int texlib_setlocalbox(lua_State *L)
6763{
6764    int location = tex_aux_local_box_location(L);
6765    if (location >= 0) {
6766        tex_set_local_boxes(lmt_node_list_from_lua(L, 2), location);
6767    }
6768    return 0;
6769}
6770
6771static int texlib_pushsavelevel(lua_State *L)
6772{
6773    (void) L;
6774    tex_new_save_level(lua_group);
6775    return 0;
6776}
6777
6778static int texlib_popsavelevel(lua_State *L)
6779{
6780    (void) L;
6781 // tex_off_save();
6782    tex_unsave();
6783    return 0;
6784}
6785static int texlib_getmathstylevariant(lua_State *L)
6786{
6787    halfword style = lmt_tointeger(L, 1);
6788    halfword parameter = lmt_tointeger(L, 2);
6789    if (is_valid_math_style(style) && math_parameter_valid_variant(parameter)) {
6790        lua_pushinteger(L, tex_get_math_parameter(style, parameter, NULL));
6791        lua_pushinteger(L, tex_math_style_variant(style, parameter));
6792        return 2;
6793    } else {
6794        return 0;
6795    }
6796}
6797
6798static int texlib_getpardataspecifications(lua_State *L)
6799{
6800    lua_createtable(L, par_n_of_codes, 0); /* one too many */
6801    for (int i = 1 ; i < par_n_of_codes; i++) {
6802        int cmd = lmt_interface.par_data[i].cmd;
6803        int chr = lmt_interface.par_data[i].chr;
6804        size_t len;
6805        char *csname = tex_makeclstring(lmt_primitive_state.prim_data[cmd].names[chr], &len);
6806        lua_createtable(L, 4, 0);
6807        lua_set_integer_by_index(L, 1, cmd);
6808        lua_set_integer_by_index(L, 2, chr);
6809        lua_set_string_by_index (L, 3, csname);
6810        lua_set_integer_by_index(L, 4, lmt_interface.par_data[i].category);
6811        lua_rawseti(L, -2, (lua_Integer) i);
6812    }
6813    return 1;
6814}
6815
6816/*tex
6817    Experiment. We could enhance page_builder_state_info with a few state fields.
6818*/
6819
6820// typedef struct page_builder_state {
6821//     halfword                saved_page_head;
6822//     halfword                saved_page_insert_head;
6823//     halfword                saved_contribute_head;
6824//     halfword                saved_hold_head;
6825//     page_builder_state_info saved_page_builder_state;
6826// } page_builder_state;
6827//
6828// page_builder_state page_builder_states[8];
6829//
6830// static int texlib_savepagestate(lua_State *L)
6831// {
6832//     int n = lua_tointeger(L, 1);
6833//     if (n >= 1 && n <= 8) {
6834//         page_builder_states[n-1].saved_page_head          = node_next(page_head);
6835//         page_builder_states[n-1].saved_page_insert_head   = node_next(page_insert_head);
6836//         page_builder_states[n-1].saved_contribute_head    = node_next(contribute_head);
6837//         page_builder_states[n-1].saved_hold_head          = node_next(hold_head);
6838//         page_builder_states[n-1].saved_page_builder_state = lmt_page_builder_state;
6839//     }
6840//     return 0;
6841// }
6842//
6843// static int texlib_restorepagestate(lua_State *L)
6844// {
6845//     int n = lua_tointeger(L, 1);
6846//     if (n >= 1 && n <= 8) {
6847//         node_next(page_head)        = page_builder_states[n-1].saved_page_head;
6848//         node_next(page_insert_head) = page_builder_states[n-1].saved_page_insert_head;
6849//         node_next(contribute_head)  = page_builder_states[n-1].saved_contribute_head;
6850//         node_next(hold_head)        = page_builder_states[n-1].saved_hold_head;
6851//         lmt_page_builder_state      = page_builder_states[n-1].saved_page_builder_state;
6852//     }
6853//     return 0;
6854// }
6855//
6856// static void texlib_initializepagestates(void)
6857// {
6858//     tex_initialize_pagestate();
6859//     for (int i=0; i < 8; i++) {
6860//         page_builder_states[i].saved_page_head          = null;
6861//         page_builder_states[i].saved_page_insert_head   = null;
6862//         page_builder_states[i].saved_contribute_head    = null;
6863//         page_builder_states[i].saved_page_head          = null;
6864//         page_builder_states[i].saved_hold_head          = null;
6865//         page_builder_states[i].saved_page_builder_state = lmt_page_builder_state;
6866//     }
6867// }
6868
6869/*tex
6870    When testing all these math finetuning options we needed to typeset the box contents and
6871    instead of filtering from the log or piping the log to a file, this more ssd friendly
6872    feature was added. The first argument is a box (id) and the second an optional detail
6873    directive. This is (currently) the only case where we write to a \LUA\ buffer, but I
6874    might add variants for a macro and tokenlist at some point (less interesting).
6875*/
6876
6877/* till here */
6878
6879static const struct luaL_Reg texlib_function_list[] = {
6880    { "write",                        texlib_write                        },
6881    { "print",                        texlib_print                        },
6882    { "sprint",                       texlib_sprint                       },
6883    { "mprint",                       texlib_mprint                       },
6884    { "tprint",                       texlib_tprint                       },
6885    { "cprint",                       texlib_cprint                       },
6886    { "isprintable",                  texlib_isprintable                  },
6887    { "pushlocal",                    texlib_pushlocal                    },
6888    { "poplocal",                     texlib_poplocal                     },
6889    { "runlocal",                     texlib_runlocal                     },
6890    { "runstring",                    texlib_runstring                    },
6891    { "quitlocal",                    texlib_quitlocal                    },
6892    { "expandasvalue",                texlib_expandasvalue                }, /* experiment */
6893    { "error",                        texlib_error                        },
6894    { "set",                          texlib_set                          },
6895    { "get",                          texlib_get                          },
6896    { "getregisterindex",             texlib_getregisterindex             },
6897    { "isdimen",                      texlib_isdimen                      },
6898    { "setdimen",                     texlib_setdimen                     },
6899    { "getdimen",                     texlib_getdimen                     },
6900    { "isfloat",                      texlib_isfloat                      },
6901    { "setfloat",                     texlib_setfloat                     },
6902    { "getfloat",                     texlib_getfloat                     },
6903    { "isskip",                       texlib_isskip                       },
6904    { "setskip",                      texlib_setskip                      },
6905    { "getskip",                      texlib_getskip                      },
6906    { "isglue",                       texlib_isglue                       },
6907    { "setglue",                      texlib_setglue                      },
6908    { "getglue",                      texlib_getglue                      },
6909    { "ismuskip",                     texlib_ismuskip                     },
6910    { "setmuskip",                    texlib_setmuskip                    },
6911    { "getmuskip",                    texlib_getmuskip                    },
6912    { "ismuglue",                     texlib_ismuglue                     },
6913    { "setmuglue",                    texlib_setmuglue                    },
6914    { "getmuglue",                    texlib_getmuglue                    },
6915    { "isattribute",                  texlib_isattribute                  },
6916    { "setattribute",                 texlib_setattribute                 },
6917    { "getattribute",                 texlib_getattribute                 },
6918    { "iscount",                      texlib_iscount                      },
6919    { "setcount",                     texlib_setcount                     },
6920    { "getcount",                     texlib_getcount                     },
6921    { "istoks",                       texlib_istoks                       },
6922    { "settoks",                      texlib_settoks                      },
6923    { "scantoks",                     texlib_scantoks                     },
6924    { "gettoks",                      texlib_gettoks                      },
6925    { "getmark",                      texlib_getmark                      },
6926    { "getmarknames",                 texlib_getmarknames                 },
6927    { "isbox",                        texlib_isbox                        },
6928    { "setbox",                       texlib_setbox                       },
6929    { "getbox",                       texlib_getbox                       },
6930    { "splitbox",                     texlib_splitbox                     },
6931    { "setlist",                      texlib_setlist                      },
6932    { "getlist",                      texlib_getlist                      },
6933    { "getlistfields",                texlib_getlistfields                },
6934    { "setnest",                      texlib_setnest                      }, /* only a message */
6935    { "getnest",                      texlib_getnest                      },
6936    { "getnestlevel",                 texlib_getnestlevel                 },
6937    { "getnestfields",                texlib_getnestfields                },
6938    { "setcatcode",                   texlib_setcatcode                   },
6939    { "getcatcode",                   texlib_getcatcode                   },
6940    { "setcccode",                    texlib_setcccode                    },
6941    { "getcccode",                    texlib_getcccode                    },
6942    { "setdelcode",                   texlib_setdelcode                   },
6943    { "getdelcode",                   texlib_getdelcode                   },
6944    { "getdelcodes",                  texlib_getdelcodes                  },
6945    { "sethccode",                    texlib_sethccode                    },
6946    { "gethccode",                    texlib_gethccode                    },
6947    { "sethmcode",                    texlib_sethmcode                    },
6948    { "gethmcode",                    texlib_gethmcode                    },
6949    { "setamcode",                    texlib_setamcode                    },
6950    { "getamcode",                    texlib_getamcode                    },
6951    { "setlccode",                    texlib_setlccode                    },
6952    { "getlccode",                    texlib_getlccode                    },
6953    { "setmathcode",                  texlib_setmathcode                  },
6954    { "getmathcode",                  texlib_getmathcode                  },
6955    { "getmathcodes",                 texlib_getmathcodes                 },
6956    { "setsfcode",                    texlib_setsfcode                    },
6957    { "getsfcode",                    texlib_getsfcode                    },
6958    { "setuccode",                    texlib_setuccode                    },
6959    { "getuccode",                    texlib_getuccode                    },
6960    { "round",                        texlib_round                        },
6961    { "scale",                        texlib_scale                        },
6962    { "sp",                           texlib_toscaled                     },
6963    { "toscaled",                     texlib_toscaled                     },
6964    { "tonumber",                     texlib_tonumber                     },
6965    { "fontname",                     texlib_getfontname                  },
6966    { "fontidentifier",               texlib_getfontidentifier            },
6967    { "getfontoffamily",              texlib_getfontoffamily              },
6968    { "number",                       texlib_getnumber                    },
6969 // { "dimension",                    texlib_getdimension                 },
6970    { "romannumeral",                 texlib_getromannumeral              },
6971    { "definefont",                   texlib_definefont                   },
6972    { "hashtokens",                   texlib_hashtokens                   },
6973    { "primitives",                   texlib_primitives                   },
6974    { "extraprimitives",              texlib_extraprimitives              },
6975    { "enableprimitives",             texlib_enableprimitives             },
6976    { "shipout",                      texlib_shipout                      },
6977    { "badness",                      texlib_badness                      },
6978    { "setmath",                      texlib_setmath                      },
6979    { "getmath",                      texlib_getmath                      },
6980    { "linebreak",                    texlib_linebreak                    },
6981    { "getlinebreakparameterfields",  texlib_getlinebreakparameterfields  },
6982    { "getlinebreakresultfields",     texlib_getlinebreakresultfields     },
6983    { "preparelinebreak",             texlib_preparelinebreak             },
6984    { "getbalanceparameterfields",    texlib_getbalanceparameterfields    },
6985    { "getbalanceresultfields",       texlib_getbalanceresultfields       },
6986    { "balance",                      texlib_balance                      },
6987    { "resetparagraph",               texlib_resetparagraph               },
6988    { "showcontext",                  texlib_showcontext                  },
6989    { "triggerbuildpage",             texlib_triggerbuildpage             },
6990    { "gethelptext",                  texlib_gethelptext                  },
6991    { "getpagestate",                 texlib_getpagestate                 },
6992    { "getpagestatevalues",           texlib_getpagestatevalues           },
6993    { "getlocallevel",                texlib_getlocallevel                },
6994    { "setinputstatemode",            texlib_setinputstatemode            },
6995    { "getinputstatemode",            texlib_getinputstatemode            },
6996    { "setinputstatefile",            texlib_setinputstatefile            },
6997    { "getinputstatefile",            texlib_getinputstatefile            },
6998    { "forceinputstatefile",          texlib_forceinputstatefile          },
6999    { "forceinputstateline",          texlib_forceinputstateline          },
7000    { "setinputstateline",            texlib_setinputstateline            },
7001    { "getinputstateline",            texlib_getinputstateline            },
7002    { "forcehmode",                   texlib_forcehmode                   },
7003    { "gettextdir",                   texlib_gettextdir                   },
7004    { "settextdir",                   texlib_settextdir                   },
7005    { "getlinedir",                   texlib_gettextdir                   }, /* we're nice */
7006    { "setlinedir",                   texlib_setlinedir                   },
7007    { "getmathdir",                   texlib_getmathdir                   },
7008    { "setmathdir",                   texlib_setmathdir                   },
7009    { "getpardir",                    texlib_getpardir                    },
7010    { "setpardir",                    texlib_setpardir                    },
7011    { "getboxdir",                    texlib_getboxdir                    },
7012    { "setboxdir",                    texlib_setboxdir                    },
7013    { "getinteraction",               texlib_getinteraction               },
7014    { "setinteraction",               texlib_setinteraction               },
7015    { "getglyphdata",                 texlib_getglyphdata                 },
7016    { "setglyphdata",                 texlib_setglyphdata                 },
7017    { "getglyphstate",                texlib_getglyphstate                },
7018    { "setglyphstate",                texlib_setglyphstate                },
7019    { "getglyphscript",               texlib_getglyphscript               },
7020    { "setglyphscript",               texlib_setglyphscript               },
7021    { "getglyphscales",               texlib_getglyphscales               },
7022    { "fatalerror",                   texlib_fatalerror                   },
7023    { "lastnodetype",                 texlib_lastnodetype                 },
7024    { "chardef",                      texlib_chardef                      },
7025    { "mathchardef",                  texlib_mathchardef                  },
7026    { "integerdef",                   texlib_setintegervalue              },
7027    { "setintegervalue",              texlib_setintegervalue              },
7028    { "getintegervalue",              texlib_getintegervalue              },
7029    { "positdef",                     texlib_setfloatvalue                },
7030    { "setpositvalue",                texlib_setfloatvalue                },
7031    { "getpositvalue",                texlib_getfloatvalue                },
7032    { "setcardinalvalue",             texlib_setcardinalvalue             },
7033    { "getcardinalvalue",             texlib_getintegervalue              },
7034    { "dimensiondef",                 texlib_setdimensionvalue            },
7035    { "setdimensionvalue",            texlib_setdimensionvalue            },
7036    { "getdimensionvalue",            texlib_getdimensionvalue            },
7037    { "getspecification",             texlib_getspecification             },
7038    { "getmode",                      texlib_getmode                      },
7039    { "getmodevalues",                texlib_getmodevalues                },
7040    { "getrunstatevalues",            texlib_getrunstatevalues            },
7041    { "setrunstate",                  texlib_setrunstate                  },
7042    { "getrunstate",                  texlib_getrunstate                  },
7043    { "gethyphenationvalues",         texlib_gethyphenationvalues         },
7044    { "getglyphoptionvalues",         texlib_getglyphoptionvalues         },
7045    { "getglueoptionvalues",          texlib_getglueoptionvalues          },
7046    { "getkernoptionvalues",          texlib_getkernoptionvalues          },
7047    { "getmathoptionvalues",          texlib_getmathoptionvalues          },
7048    { "getpenaltyoptionvalues",       texlib_getpenaltyoptionvalues       },
7049    { "getuleaderlocationvalues",     texlib_getuleaderlocationvalues     },
7050    { "getnoadoptionvalues",          texlib_getnoadoptionvalues          },
7051    { "getbalancestepoptionvalues",   texlib_getbalancestepoptionvalues   },
7052    { "getdiscoptionvalues",          texlib_getdiscoptionvalues          },
7053    { "getruleoptionvalues",          texlib_getruleoptionvalues          },
7054    { "getboxoptionvalues",           texlib_getboxoptionvalues           },
7055    { "getmathsurroundvalues",        texlib_getmathsurroundvalues        },
7056    { "getlistanchorvalues",          texlib_getlistanchorvalues          },
7057    { "getlistsignvalues",            texlib_getlistsignvalues            },
7058    { "getlistgeometryvalues",        texlib_getlistgeometryvalues        },
7059    { "getmathgluevalues",            texlib_getmathgluevalues            },
7060    { "getglyphprotectionvalues",     texlib_getglyphprotectionvalues     },
7061    { "getdiscpartvalues",            texlib_getdiscpartvalues            },
7062    { "getglyphdiscvalues",           texlib_getglyphdiscvalues           },
7063    { "getspecificationoptionvalues", texlib_getspecificationoptionvalues },
7064    { "getmathparametervalues",       texlib_getmathparametervalues       },
7065    { "getmathstylenamevalues",       texlib_getmathstylenamevalues       },
7066    { "getmathstylevalues",           texlib_getmathstylevalues           },
7067    { "getmathvariantvalues",         texlib_getmathvariantvalues         },
7068    { "getmathvariantpresets",        texlib_getmathvariantpresets        },
7069    { "getmathcontrolvalues",         texlib_getmathcontrolvalues         },
7070    { "gettextcontrolvalues",         texlib_gettextcontrolvalues         },
7071    { "getpardataspecifications",     texlib_getpardataspecifications     },
7072    { "getprepoststatevalues",        texlib_getprepoststatevalues        },
7073    { "getadjustoptionvalues",        texlib_getadjustoptionvalues        },
7074    { "getemptyparagraphmodevalues",  texlib_getemptyparagraphmodevalues  },
7075    { "getpacktypevalues",            texlib_getpacktypevalues            },
7076    { "getgroupvalues",               texlib_getgroupvalues               },
7077    { "getparcontextvalues",          texlib_getparcontextvalues          },
7078    { "getpagecontextvalues",         texlib_getpagecontextvalues         },
7079    { "getappendlinecontextvalues",   texlib_getappendlinecontextvalues   },
7080    { "getalignmentcontextvalues",    texlib_getalignmentcontextvalues    },
7081    { "getbreakcontextvalues",        texlib_getbreakcontextvalues        },
7082    { "getbuildcontextvalues",        texlib_getbuildcontextvalues        },
7083    { "getvsplitcontextvalues",       texlib_getvsplitcontextvalues       },
7084    { "getpartriggervalues",          texlib_getpartriggervalues          },
7085    { "getparmodevalues",             texlib_getparmodevalues             },
7086    { "getautomigrationvalues",       texlib_getautomigrationvalues       },
7087    { "getflagvalues",                texlib_getflagvalues                },
7088    { "getprotrusionboundaryvalues",  texlib_getprotrusionboundaryvalues  },
7089    { "getlinebreakstatevalues",      texlib_getlinebreakstatevalues      },
7090    { "getmathclassoptionvalues",     texlib_getmathclassoptionvalues     },
7091    { "getnormalizelinevalues",       texlib_getnormalizelinevalues       },
7092    { "getnormalizeparvalues",        texlib_getnormalizeparvalues        },
7093    { "getdirectionvalues",           texlib_getdirectionvalues           },
7094    { "getparametermodevalues",       texlib_getparametermodevalues       },
7095    { "getcharactercontrolvalues",    texlib_getcharactercontrolvalues    },
7096    { "getfillvalues",                texlib_getfillvalues                },
7097    { "getunitclassvalues",           texlib_getunitclassvalues           },
7098    { "geterrorvalues",               texlib_geterrorvalues               },
7099    { "getinteractionmodes",          texlib_getinteractionmodes          },
7100    { "getbadnessmodevalues",         texlib_getbadnessmodevalues         },
7101    { "getiftypes",                   texlib_getiftypes                   },
7102    { "getiovalues",                  texlib_getiovalues                  },
7103    { "getprimitiveorigins",          texlib_getprimitiveorigins          },
7104    { "getmissingcharactervalues",    texlib_getmissingcharactervalues    }, 
7105    { "getfrozenparvalues",           texlib_getfrozenparvalues           },
7106    { "getshapingpenaltiesvalues",    texlib_getshapingpenaltiesvalues    },
7107    { "getautoparagraphvalues",       texlib_getautoparagraphvalues       },
7108    { "getcharactertagvalues",        texlib_getcharactertagvalues        },
7109    { "getkerneloptionvalues",        texlib_getkerneloptionvalues        },
7110    { "getdoublescriptoptionvalues",  texlib_getdoublescriptoptionvalues  },
7111    { "getspecialmathclassvalues",    texlib_getspecialmathclassvalues    },
7112    { "getmathscriptordervalues",     texlib_getmathscriptordervalues     },
7113    { "getmathscriptsmodevalues",     texlib_getmathscriptsmodevalues     },
7114    { "getlargestusedmark",           texlib_getlargestusedmark           },
7115    { "getusedmarks",                 texlib_getusedmarks                 },
7116    { "getoutputactive",              texlib_getoutputactive              },
7117    { "getmvloptionvalues",           texlib_getmvloptionvalues           },
7118    { "getbalancecallbackvalues",     texlib_getbalancecallbackvalues     },
7119    /* experiment (metafun update) */
7120    { "shiftparshape",                texlib_shiftparshape                },
7121    { "snapshotpar",                  texlib_snapshotpar                  },
7122    { "getparstate",                  texlib_getparstate                  },
7123    { "getparstatefields",            texlib_getparstatefields            },
7124    /* */
7125    { "getmathstylevariant",          texlib_getmathstylevariant          },
7126    /* */
7127    { "getinsertdistance",            texlib_getinsertdistance            },
7128    { "getinsertmultiplier",          texlib_getinsertmultiplier          },
7129    { "getinsertlimit",               texlib_getinsertlimit               },
7130    { "getinsertheight",              texlib_getinsertheight              },
7131    { "getinsertdepth",               texlib_getinsertdepth               },
7132    { "getinsertwidth",               texlib_getinsertwidth               },
7133    { "getinsertcontent",             texlib_getinsertcontent             },
7134    { "setinsertdistance",            texlib_setinsertdistance            },
7135    { "setinsertmultiplier",          texlib_setinsertmultiplier          },
7136    { "setinsertlimit",               texlib_setinsertlimit               },
7137    { "setinsertcontent",             texlib_setinsertcontent             },
7138    { "getlocalbox",                  texlib_getlocalbox                  },
7139    { "setlocalbox",                  texlib_setlocalbox                  },
7140    { "getlocalboxlocations",         texlib_getlocalboxlocations         },
7141    /* */
7142    { "getcurrentmvl",                texlib_getcurrentmvl                },
7143    { "setbalanceshape",              texlib_setbalanceshape              },
7144    /* */
7145    { "pushsavelevel",                texlib_pushsavelevel                },
7146    { "popsavelevel",                 texlib_popsavelevel                 },
7147    /* */
7148 // { "savepagestate",                texlib_savepagestate                },
7149 // { "restorepagestate",             texlib_restorepagestate             },
7150    /* */
7151    { NULL,                           NULL                                },
7152};
7153
7154# define defineindexers(name) \
7155    static int texlib_index_##name   (lua_State *L) { lua_remove(L, 1);  return texlib_get##name(L); } \
7156    static int texlib_newindex_##name(lua_State *L) { lua_remove(L, 1);  return texlib_set##name(L); }
7157
7158defineindexers(attribute)
7159defineindexers(skip)
7160defineindexers(glue)
7161defineindexers(muskip)
7162defineindexers(muglue)
7163defineindexers(dimen)
7164defineindexers(count)
7165defineindexers(float)
7166defineindexers(toks)
7167defineindexers(box)
7168defineindexers(sfcode)
7169defineindexers(lccode)
7170defineindexers(uccode)
7171defineindexers(hccode)
7172defineindexers(hmcode)
7173defineindexers(amcode)
7174defineindexers(cccode)
7175defineindexers(catcode)
7176defineindexers(mathcode)
7177defineindexers(delcode)
7178defineindexers(list)
7179defineindexers(nest)
7180
7181/*tex
7182    At some point the |__index| and |__newindex |below will go away so that we no longer get
7183    interferences when we extedn the |tex| table.
7184*/
7185
7186int luaopen_tex(lua_State *L)
7187{
7188 // texlib_initializepagestates();
7189    /* */
7190    texlib_aux_initialize();
7191    /* */
7192    lua_newtable(L);
7193    luaL_setfuncs(L, texlib_function_list, 0);
7194    lmt_make_table(L, "attribute", TEX_METATABLE_ATTRIBUTE, texlib_index_attribute, texlib_newindex_attribute);
7195    lmt_make_table(L, "skip",      TEX_METATABLE_SKIP,      texlib_index_skip,      texlib_newindex_skip);
7196    lmt_make_table(L, "glue",      TEX_METATABLE_GLUE,      texlib_index_glue,      texlib_newindex_glue);
7197    lmt_make_table(L, "muskip",    TEX_METATABLE_MUSKIP,    texlib_index_muskip,    texlib_newindex_muskip);
7198    lmt_make_table(L, "muglue",    TEX_METATABLE_MUGLUE,    texlib_index_muglue,    texlib_newindex_muglue);
7199    lmt_make_table(L, "dimen",     TEX_METATABLE_DIMEN,     texlib_index_dimen,     texlib_newindex_dimen);
7200    lmt_make_table(L, "count",     TEX_METATABLE_COUNT,     texlib_index_count,     texlib_newindex_count);
7201    lmt_make_table(L, "posit",     TEX_METATABLE_FLOAT,     texlib_index_float,     texlib_newindex_float);
7202    lmt_make_table(L, "toks",      TEX_METATABLE_TOKS,      texlib_index_toks,      texlib_newindex_toks);
7203    lmt_make_table(L, "box",       TEX_METATABLE_BOX,       texlib_index_box,       texlib_newindex_box);
7204    lmt_make_table(L, "sfcode",    TEX_METATABLE_SFCODE,    texlib_index_sfcode,    texlib_newindex_sfcode);
7205    lmt_make_table(L, "lccode",    TEX_METATABLE_LCCODE,    texlib_index_lccode,    texlib_newindex_lccode);
7206    lmt_make_table(L, "uccode",    TEX_METATABLE_UCCODE,    texlib_index_uccode,    texlib_newindex_uccode);
7207    lmt_make_table(L, "hccode",    TEX_METATABLE_HCCODE,    texlib_index_hccode,    texlib_newindex_hccode);
7208    lmt_make_table(L, "hmcode",    TEX_METATABLE_HMCODE,    texlib_index_hmcode,    texlib_newindex_hmcode);
7209    lmt_make_table(L, "amcode",    TEX_METATABLE_AMCODE,    texlib_index_amcode,    texlib_newindex_amcode);
7210    lmt_make_table(L, "cccode",    TEX_METATABLE_CCCODE,    texlib_index_cccode,    texlib_newindex_cccode);
7211    lmt_make_table(L, "catcode",   TEX_METATABLE_CATCODE,   texlib_index_catcode,   texlib_newindex_catcode);
7212    lmt_make_table(L, "mathcode",  TEX_METATABLE_MATHCODE,  texlib_index_mathcode,  texlib_newindex_mathcode);
7213    lmt_make_table(L, "delcode",   TEX_METATABLE_DELCODE,   texlib_index_delcode,   texlib_newindex_delcode);
7214    lmt_make_table(L, "lists",     TEX_METATABLE_LISTS,     texlib_index_list,      texlib_newindex_list);
7215    lmt_make_table(L, "nest",      TEX_METATABLE_NEST,      texlib_index_nest,      texlib_newindex_nest);
7216    texlib_aux_init_nest_lib(L);
7217    /*tex make the meta entries and fetch it back */
7218    luaL_newmetatable(L, TEX_METATABLE_TEX);
7219    lua_pushstring(L, "__index");
7220    lua_pushcfunction(L, texlib_index);
7221    lua_settable(L, -3);
7222    lua_pushstring(L, "__newindex");
7223    lua_pushcfunction(L, texlib_newindex);
7224    lua_settable(L, -3);
7225    lua_setmetatable(L, -2);
7226    return 1;
7227}
7228