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