texmaincontrol.c /size: 282 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    We come now to the |main_control| routine, which contains the master switch that causes all the
10    various pieces of \TEX\ to do their things, in the right order.
11
12    In a sense, this is the grand climax of the program: It applies all the tools that we have
13    worked so hard to construct. In another sense, this is the messiest part of the program: It
14    necessarily refers to other pieces of code all over the place, so that a person can't fully
15    understand what is going on without paging back and forth to be reminded of conventions that
16    are defined elsewhere. We are now at the hub of the web, the central nervous system that
17    touches most of the other parts and ties them together.
18
19    The structure of |main_control| itself is quite simple. There's a label called |big_switch|,
20    at which point the next token of input is fetched using |get_x_token|. Then the program
21    branches at high speed into one of about 100 possible directions, based on the value of the
22    current mode and the newly fetched command code; the sum |abs(mode) + cur_cmd| indicates what
23    to do next. For example, the case |vmode + letter| arises when a letter occurs in  vertical
24    mode (or internal vertical mode); this case leads to instructions that initialize a new
25    paragraph and enter horizontal mode.p
26
27    The big |case| statement that contains this multiway switch has been labeled |reswitch|, so
28    that the program can |goto reswitch| when the next token has already been fetched. Most of
29    the cases are quite short; they call an \quote {action procedure} that does the work for that
30    case, and then they either |goto reswitch| or they \quote {fall through} to the end of the
31    |case| statement, which returns control back to |big_switch|. Thus, |main_control| is not an
32    extremely large procedure, in spite of the multiplicity of things it must do; it is small
33    enough to be handled by \PASCAL\ compilers that put severe restrictions on procedure size.
34
35    One case is singled out for special treatment, because it accounts for most of \TEX's
36    activities in typical applications. The process of reading simple text and converting it
37    into |char_node| records, while looking for ligatures and kerns, is part of \TEX's \quote
38    {inner loop}; the whole program runs efficiently when its inner loop is fast, so this part
39    has been written with particular care. (This is no longer true in \LUATEX.)
40
41    We leave the |space_factor| unchanged if |sf_code (cur_chr) = 0|; otherwise we set it equal
42    to |sf_code (cur_chr)|, except that it should never change from a value less than 1000 to a
43    value exceeding 1000. The most common case is |sf_code (cur_chr) = 1000|, so we want that case 
44    to be fast.
45
46    All action is done via runners in the function table. Some runners are implemented here,
47    others are spread over modules. In due time I will use more prefixes to indicate where they
48    belong. Also, more runners will move to their respective modules, a stepwise process. This
49    split up is not always consistent which relates to the fact that \TEX\ is a monolothic program
50    which in turn means that we keep all the smaller (and more dependen) bits here. There are
51    subsystems but they hook into each other, take inserts and adjusts that hook into the builders
52    and packagers.
53
54*/
55
56main_control_state_info lmt_main_control_state = {
57    .control_state    = goto_next_state,
58    .local_level      = 0,
59    .after_token      = null,
60    .after_tokens     = null,
61    .last_par_trigger = 0,
62    .last_par_context = 0,
63    .loop_iterator    = 0,
64    .loop_nesting     = 0,
65    .loop_stack_head  = null,
66    .loop_stack_tail  = null,
67    .quit_loop        = 0,
68    .padding          = 0,
69};
70
71static inline void tex_aux_big_switch       (int mode, int cmd);
72static        void tex_run_prefixed_command (void);
73
74/*tex
75    These two helpers, of which the second one is still experimental, actually belong in another
76    file so then might be moved. Watch how the first one has the |unsave| call!
77*/
78
79static void tex_aux_fixup_directions_and_unsave(void)
80{
81    int saved_par_state = internal_par_state_par;
82    int saved_dir_state = internal_dir_state_par;
83    int saved_direction = text_direction_par;
84    tex_pop_text_dir_ptr();
85    tex_unsave();
86    if (cur_mode == hmode) {
87        if (saved_dir_state) {
88            /* Add local dir node. */
89            tex_tail_append(tex_new_dir(cancel_dir_subtype, text_direction_par));
90            dir_direction(cur_list.tail) = saved_direction;
91        }
92        if (saved_par_state) {
93            /*tex Add local paragraph node. This resets after a group. */
94            tex_tail_append(tex_new_par_node(parameter_par_subtype)); // hmode_par_par_subtype needs checking 
95        }
96    }
97}
98
99static void tex_aux_fixup_directions_only(void)
100{
101    int saved_dir_state = internal_dir_state_par;
102    int saved_direction = text_direction_par;
103    tex_pop_text_dir_ptr();
104    if (saved_dir_state) {
105        /* Add local dir node. */
106        tex_tail_append(tex_new_dir(cancel_dir_subtype, saved_direction));
107    }
108}
109
110static void tex_aux_fixup_math_and_unsave(void)
111{
112    int saved_math_style = internal_math_style_par;
113    int saved_math_scale = internal_math_scale_par;
114    tex_unsave();
115    if (cur_mode == mmode) { 
116        if (saved_math_style >= 0 && saved_math_style != cur_list.math_style) {
117            halfword noad = tex_new_node(style_node, (quarterword) saved_math_style);
118            cur_list.math_style = saved_math_style;
119            tex_tail_append(noad);
120        }
121        if (saved_math_scale != cur_list.math_scale) {
122            halfword noad = tex_new_node(style_node, scaled_math_style);
123            style_scale(noad) = saved_math_scale;
124            cur_list.math_scale = saved_math_scale;
125            tex_tail_append(noad);
126        }
127    }
128}
129
130/*tex
131
132    The |run_| functions hook in the main control handler. Some immediately do something, others
133    trigger a follow up scan, driven by the cmd code. Here come some forward declarations; there
134    are more that the following |run_| functions. Some runners are defined in other modules. Some
135    runners finish what another started, for instance when we see a left brace, depending on state
136    another runner can kick in.
137
138*/
139
140/*tex
141
142    Experiment: 
143
144    (1) preceding glyph has option glyph_option_space_factor_overload set 
145    (2) factor is larger than 0 and below 1000
146    (3) space_factor_overload_par > 0 will force that value to be used 
147
148*/
149
150static inline int tex_aux_use_space_factor_overload(halfword tail, halfword space_factor)
151{
152    return 
153        (space_factor > 0 && space_factor < default_space_factor) &&
154        (node_type(tail) == glyph_node) &&
155        tex_has_glyph_option(tail, glyph_option_space_factor_overload)
156    ;
157}
158
159static inline int tex_aux_used_space_factor_overload(halfword space_factor)
160{
161    return default_space_factor - (space_factor_overload_par ? space_factor_overload_par :  - space_factor);
162}
163
164static void tex_aux_adjust_space_factor(halfword chr)
165{
166    halfword s = tex_get_sf_code(chr);
167    if (s == default_space_factor) {
168        cur_list.space_factor = default_space_factor;
169    } else if (s < default_space_factor) {
170        if (s > 0) {
171            cur_list.space_factor = s;
172        } else {
173            /* s <= 0 */
174        }
175    } else if (tex_aux_use_space_factor_overload(cur_list.tail, cur_list.space_factor)) {
176        /* keep it */
177    } else if (cur_list.space_factor < default_space_factor) {
178        cur_list.space_factor = default_space_factor;
179    } else {
180        cur_list.space_factor = s;
181    }
182}
183
184static void tex_aux_run_text_char_number(void)
185{
186    switch (cur_chr) {
187        case char_number_code:
188            {
189                halfword chr = tex_scan_char_number(0);
190                tex_aux_adjust_space_factor(chr);
191                tex_tail_append(tex_new_char_node(glyph_unset_subtype, cur_font_par, chr, 1));
192                break;
193            }
194        case glyph_number_code:
195            {
196                scaled xoffset = glyph_x_offset_par;
197                scaled yoffset = glyph_y_offset_par;
198                halfword xscale = glyph_x_scale_par;
199                halfword yscale = glyph_y_scale_par;
200                halfword scale = glyph_scale_par;
201                halfword slant = glyph_slant_par;
202                halfword weight = glyph_weight_par;
203                halfword options = glyph_options_par;
204                halfword font = cur_font_par;
205                scaled left = 0;
206                scaled right = 0;
207                scaled raise = 0;
208                halfword chr = 0;
209                halfword glyph;
210                while (1) {
211                    switch (tex_scan_character("xyofislrwXYOFISLRW", 0, 1, 0)) {
212                        case 0:
213                            goto DONE;
214                        case 'x': case 'X':
215                            switch (tex_scan_character("osOS", 0, 0, 0)) {
216                                case 'o': case 'O':
217                                    if (tex_scan_mandate_keyword("xoffset", 2)) {
218                                        xoffset = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
219                                    }
220                                    break;
221                                case 's': case 'S':
222                                    if (tex_scan_mandate_keyword("xscale", 2)) {
223                                        xscale = tex_scan_integer(0, NULL, NULL);
224                                    }
225                                    break;
226                                default:
227                                    tex_aux_show_keyword_error("xoffset|xscale");
228                                    goto DONE;
229                            }
230                            break;
231                        case 'y': case 'Y':
232                            switch (tex_scan_character("osOS", 0, 0, 0)) {
233                                case 'o': case 'O':
234                                    if (tex_scan_mandate_keyword("yoffset", 2)) {
235                                        yoffset = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
236                                    }
237                                    break;
238                                case 's': case 'S':
239                                    if (tex_scan_mandate_keyword("yscale", 2)) {
240                                        yscale = tex_scan_integer(0, NULL, NULL);
241                                    }
242                                    break;
243                                default:
244                                    tex_aux_show_keyword_error("yoffset|yscale");
245                                    goto DONE;
246                            }
247                            break;
248                        case 'o': case 'O':
249                            if (tex_scan_mandate_keyword("options", 1)) {
250                                options = tex_scan_integer(0, NULL, NULL) & glyph_option_valid;
251                            }
252                            break;
253                        case 'f': case 'F':
254                            if (tex_scan_mandate_keyword("font", 1)) {
255                                font = tex_scan_font_identifier(NULL);
256                            }
257                            break;
258                        case 'i': case 'I':
259                            if (tex_scan_mandate_keyword("id", 1)) {
260                                halfword f = tex_scan_integer(0, NULL, NULL);
261                                if (f > 0 && tex_is_valid_font(f)) {
262                                    font = f;
263                                }
264                            }
265                            break;
266                        case 's': case 'S':
267                            switch (tex_scan_character("clCL", 0, 0, 0)) {
268                                case 'c': case 'C':
269                                    if (tex_scan_mandate_keyword("scale", 2)) {
270                                        scale = tex_scan_integer(0, NULL, NULL);
271                                    }
272                                    break;
273                                case 'l': case 'L':
274                                    if (tex_scan_mandate_keyword("slant", 2)) {
275                                        slant = tex_scan_integer(0, NULL, NULL);
276                                    }
277                                    break;
278                                default:
279                                    tex_aux_show_keyword_error("scale|slant");
280                                    goto DONE;
281                            }
282                            break;
283                        case 'l': case 'L':
284                            if (tex_scan_mandate_keyword("left", 1)) {
285                                left = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
286                            }
287                            break;
288                        case 'r': case 'R':
289                            switch (tex_scan_character("aiAI", 0, 0, 0)) {
290                                case 'i': case 'I':
291                                    if (tex_scan_mandate_keyword("right", 2)) {
292                                        right = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
293                                    }
294                                    break;
295                                case 'a': case 'A':
296                                    if (tex_scan_mandate_keyword("raise", 2)) {
297                                        raise = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
298                                    }
299                                    break;
300                                default:
301                                    tex_aux_show_keyword_error("right|raise");
302                                    goto DONE;
303                            }
304                            break;
305                        case 'w': case 'W':
306                            if (tex_scan_mandate_keyword("weight", 1)) {
307                                weight = tex_scan_integer(0, NULL, NULL);
308                            }
309                            break;
310                        default:
311                            goto DONE;
312                    }
313                }
314              DONE:
315                chr = tex_scan_char_number(0);
316                tex_aux_adjust_space_factor(chr);
317                glyph = tex_new_char_node(glyph_unset_subtype, font, chr, 1);
318                set_glyph_options(glyph, options);
319                set_glyph_x_scale(glyph, xscale);
320                set_glyph_y_scale(glyph, yscale);
321                set_glyph_scale(glyph, scale);
322                set_glyph_left(glyph, left);
323                set_glyph_right(glyph, right);
324                set_glyph_raise(glyph, raise);
325                set_glyph_x_offset(glyph, xoffset);
326                set_glyph_y_offset(glyph, yoffset);
327                set_glyph_weight(glyph, weight);
328                set_glyph_slant(glyph, slant);
329                tex_tail_append(glyph);
330                break;
331            }
332    }
333}
334
335static void tex_aux_run_text_letter(void) 
336{
337    tex_aux_adjust_space_factor(cur_chr);
338    tex_tail_append(tex_new_char_node(glyph_unset_subtype, cur_font_par, cur_chr, 1));
339}
340
341/*tex
342
343    Here are all the functions that are called from |main_control| that are not already defined
344    elsewhere. For the moment, this list simply in the order that the appear in |init_main_control|,
345    below.
346
347*/
348
349static void tex_aux_run_node(void) 
350{
351    halfword n = cur_chr;
352    if (node_token_flagged(n)) {
353        tex_get_token();
354        n = node_token_sum(n,cur_chr);
355    }
356    if (copy_lua_input_nodes_par) {
357        n = tex_copy_node_list(n, null);
358    }
359    tex_tail_append(n);
360    if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
361        attach_current_attribute_list(n);
362    }
363    while (node_next(n)) {
364        n = node_next(n);
365        tex_tail_append(n);
366        if (tex_nodetype_has_attributes(node_type(n)) && ! node_attr(n)) {
367            attach_current_attribute_list(n);
368        }
369    }
370}
371
372/* */
373
374static inline void lmt_bytecode_run(int index)
375{
376    strnumber u = tex_save_cur_string();
377    lmt_token_state.luacstrings = 0;
378    lmt_bytecode_call(index);
379    tex_restore_cur_string(u);
380    if (lmt_token_state.luacstrings > 0) {
381        tex_lua_string_start();
382    }
383}
384
385static inline void lmt_lua_run(int reference, int prefix)
386{
387    strnumber u = tex_save_cur_string();
388    lmt_token_state.luacstrings = 0;
389    lmt_function_call(reference, prefix);
390    tex_restore_cur_string(u);
391    if (lmt_token_state.luacstrings > 0) {
392        tex_lua_string_start();
393    }
394}
395
396static void tex_aux_run_lua_protected_call(void) 
397{
398    if (cur_chr > 0) {
399        lmt_lua_run(cur_chr, 0);
400    } else {
401        tex_normal_error("luacall", "invalid number in protected call");
402    }
403}
404
405void tex_aux_lua_call(halfword cmd, halfword chr) 
406{
407    (void) cmd;
408    if (chr > 0) {
409        lmt_lua_run(chr, 0);
410    } else {
411        tex_normal_error("luacall", "invalid number in unprotected call");
412    }
413}
414
415static void tex_aux_set_lua_value(int a) 
416{
417    if (cur_chr > 0) {
418        lmt_lua_run(cur_chr, a);
419    } else {
420        tex_normal_error("luavalue", "invalid number");
421    }
422}
423
424/*tex
425
426    The occurrence of blank spaces is almost part of \TEX's inner loop, since we usually encounter
427    about one space for every five non-blank characters. Therefore |main_control| gives second
428    highest priority to ordinary spaces.
429
430    When a glue parameter like |\spaceskip| is set to |0pt|, we will see to it later that the
431    corresponding glue specification is precisely |zero_glue|, not merely a pointer to some
432    specification that happens to be full of zeroes. Therefore it is simple to test whether a glue
433    parameter is zero or~not.
434
435    There is a special treatment for spaces when |space_factor <> 1000|.
436
437*/
438
439static void tex_aux_run_math_space(void) 
440{
441    if (! no_spaces_par && node_type(cur_list.tail) == simple_noad) {
442        noad_options(cur_list.tail) |= noad_option_followed_by_space;
443    }
444}
445
446static void tex_aux_run_space(void) 
447{
448    switch (no_spaces_par) {
449        case 1:
450            /*tex Don't inject anything, not even zero skip. */
451            return;
452        case 2:
453            /*tex Inject nothing but zero glue. */
454            tex_tail_append(tex_new_glue_node(zero_glue, zero_space_skip_glue)); /* todo: subtype, zero_space_glue? */
455            glue_font(cur_list.tail) = cur_font_par;
456            break;
457        case 3:
458            tex_aux_adjust_space_factor(cur_chr);
459            tex_tail_append(tex_new_char_node(glyph_unset_subtype, cur_font_par, space_char_par, 1));
460            break;
461        default:
462            /*tex
463                The tradional treatment. A difference with other \TEX's is that we store the spacing
464                in the node instead of using the (end of) paragraph bound value.
465            */
466            {
467                halfword p;
468                if (cur_mode == hmode && cur_cmd == spacer_cmd && cur_list.space_factor != default_space_factor) {
469                    if ((cur_list.space_factor >= space_factor_threshold) && (! tex_glue_is_zero(xspace_skip_par))) {
470                        p = tex_get_scaled_parameter_glue(xspace_skip_code, xspace_skip_glue);
471                    } else {
472                        halfword font = cur_font_par; 
473                        if (tex_glue_is_zero(space_skip_par)) {
474                            p = tex_get_scaled_glue(font);
475                        } else {
476                            p = tex_get_parameter_glue(space_skip_code, space_skip_glue); /* not scaled */
477                        }
478                        /* Modify the glue specification in |q| according to the space factor */
479                        if (cur_list.space_factor >= space_factor_threshold) {
480                            glue_amount(p) += tex_get_scaled_extra_space(font);
481                        } else if (tex_aux_use_space_factor_overload(cur_list.tail, cur_list.space_factor)) {
482                            /* what with stretch and shrink */
483                            cur_list.space_factor = tex_aux_used_space_factor_overload(cur_list.space_factor);
484                            glue_amount(p) = tex_xn_over_d(glue_amount(p), cur_list.space_factor, scaling_factor);
485                        }
486                        glue_options(p) |= glue_option_has_factor;
487                        if (space_factor_stretch_limit_par >= scaling_factor && cur_list.space_factor > scaling_factor) {
488                            glue_options(p) |= glue_option_is_limited;
489                            glue_stretch(p) = tex_xn_over_d(glue_stretch(p), space_factor_stretch_limit_par, scaling_factor);
490                        } else {                   
491                            glue_stretch(p) = tex_xn_over_d(glue_stretch(p), cur_list.space_factor, scaling_factor);
492                        }
493                        if (space_factor_shrink_limit_par >= scaling_factor && cur_list.space_factor > scaling_factor) {
494                            glue_options(p) |= glue_option_is_limited;
495                            switch (space_factor_mode_par) { 
496                                case 1: 
497                                    glue_shrink(p) = tex_xn_over_d(glue_shrink(p), space_factor_shrink_limit_par, scaling_factor);
498                                    break;
499                                case 2 :
500                                    glue_shrink(p) = tex_xn_over_d(glue_shrink(p), 2*scaling_factor, space_factor_shrink_limit_par);
501                                    break;
502                                default:
503                                    glue_shrink(p) = tex_xn_over_d(glue_shrink(p), scaling_factor, space_factor_shrink_limit_par);
504                                    break;
505                            }
506                        } else {                   
507                            switch (space_factor_mode_par) { 
508                                case 1: 
509                                    glue_shrink(p) = tex_xn_over_d(glue_shrink(p), cur_list.space_factor, scaling_factor);
510                                    break;
511                                case 2 :
512                                    glue_shrink(p) = tex_xn_over_d(glue_shrink(p), 2*scaling_factor, cur_list.space_factor);
513                                    break;
514                                default:
515                                    glue_shrink(p) = tex_xn_over_d(glue_shrink(p), scaling_factor, cur_list.space_factor);
516                                    break;
517                            }
518                        }
519                    }
520                } else if (tex_glue_is_zero(space_skip_par)) {
521                    /*tex Find the glue specification for text spaces in the current font. */
522                    p = tex_get_scaled_glue(cur_font_par);
523                } else {
524                    /*tex Append a normal inter-word space to the current list. */
525                    p = tex_get_parameter_glue(space_skip_code, space_skip_glue); /* not scaled */
526                }
527                glue_font(p) = cur_font_par;
528                tex_tail_append(p);
529            }
530            break;
531        }
532}
533
534/*tex A fast one, also used to silently ignore |\par|s in a math formula. */
535
536static void tex_aux_run_relax(void) 
537{
538    return;
539}
540
541static void tex_aux_run_active(void) 
542{
543    if ((cur_mode == mmode || lmt_nest_state.math_mode) && tex_check_active_math_char(cur_chr)) {
544        /*tex We have an intercept. */
545        tex_back_input(cur_tok);
546    } else {
547        cur_cs = tex_active_to_cs(cur_chr, ! lmt_hash_state.no_new_cs);
548        cur_cmd = eq_type(cur_cs);
549        cur_chr = eq_value(cur_cs);
550        tex_x_token();
551        tex_back_input(cur_tok);
552    }
553}
554
555/*tex
556
557    |ignore_spaces| is a special case: after it has acted, |get_x_token| has already fetched the
558    next token from the input, so that operation in |main_control| should be skipped.
559
560*/
561
562static void tex_aux_run_ignore_something(void) 
563{
564    switch (cur_chr) {
565        case ignore_space_code:
566            /*tex Get the next non-blank call. */
567            do {
568                tex_get_x_token();
569            } while (cur_cmd == spacer_cmd);
570            lmt_main_control_state.control_state = goto_skip_token_state;
571            break;
572        case ignore_par_code:
573            /*tex Get the next non-blank/par call. */
574            do {
575                tex_get_x_token();
576            } while (cur_cmd == spacer_cmd || cur_cmd == end_paragraph_cmd);
577            lmt_main_control_state.control_state = goto_skip_token_state;
578            break;
579        case ignore_argument_code:
580            /*tex There is nothing to show here. */
581            break;
582        case ignore_upto_code:
583            {
584                halfword token = tex_get_token();
585                do {
586                    tex_get_token();
587                } while (cur_tok != token);
588                break;
589            }
590        case ignore_nested_upto_code:
591            {
592                halfword starttoken = tex_get_token();
593                halfword stoptoken = tex_get_token();
594                int level = 1;
595                do {
596                    tex_get_token();
597                    if (cur_tok == starttoken) { 
598                        ++level;
599                    } else if (cur_tok == stoptoken) { 
600                        --level;
601                    }
602                } while (level);
603                break;
604            }
605        case ignore_rest_code:
606            tex_quit_token_list();
607            break;
608        default:
609            break;
610    }
611}
612
613/* */
614
615static void tex_aux_run_math_non_math(void)
616{
617    if (tracing_commands_par >= 4) {
618        tex_begin_diagnostic();
619        tex_print_format("[math: pushing back %C]", cur_cmd, cur_chr);
620        tex_end_diagnostic();
621    }
622    tex_back_input(cur_tok);
623    tex_begin_paragraph(1, math_char_par_trigger);
624}
625
626/*tex
627
628    The most important parts of |main_control| are concerned with \TEX's chief mission of box
629    making. We need to control the activities that put entries on vlists and hlists, as well as
630    the activities that convert those lists into boxes. All of the necessary machinery has already
631    been developed; it remains for us to \quote {push the buttons} at the right times.
632
633    As an introduction to these routines, let's consider one of the simplest cases: What happens
634    when |\hrule| occurs in vertical mode, or |\vrule| in horizontal mode or math mode? The code
635    in |main_control| is short, since the |scan_rule_spec| routine already does most of what is
636    required; thus, there is no need for a special action procedure.
637
638    Note that baselineskip calculations are disabled after a rule in vertical mode, by setting
639    |prev_depth := ignore_depth|.
640
641    First we define a procedure that returns a pointer to a rule node. This routine is called just
642    after \TEX\ has seen |\hrule| or |\vrule|; therefore |cur_cmd| will be either |hrule| or
643    |vrule|. The idea is to store the default rule dimensions in the node, then to override them if
644    |height| or |width| or |depth| specifications are found (in any order).
645
646    For a moment I considered this:
647
648    \starttyping
649    if (scan_keyword("to")) {
650        scan_dimension(0, 0, 0, 0); rule_width(q)  = cur_val;
651        scan_dimension(0, 0, 0, 0); rule_height(q) = cur_val;
652        scan_dimension(0, 0, 0, 0); rule_depth(q)  = cur_val;
653        return q;
654    }
655    \stoptyping
656
657*/
658
659
660/*tex
661
662    Many of the actions related to box-making are triggered by the appearance of braces in the
663    input. For example, when the user says |\hbox to 100pt {<hlist>}| in vertical mode, the
664    information about the box size (100pt, |exactly|) is put onto |save_stack| with a level
665    boundary word just above it, and |cur_group:=adjusted_hbox_group|; \TEX\ enters restricted
666    horizontal mode to process the hlist. The right brace eventually causes |save_stack| to be
667    restored to its former state, at which time the information about the box size (100pt,
668    |exactly|) is available once again; a box is packaged and we leave restricted horizontal mode,
669    appending the new box to the current list of the enclosing mode (in this case to the current
670    list of vertical mode), followed by any vertical adjustments that were removed from the box by
671    |hpack|.
672
673    The next few sections of the program are therefore concerned with the treatment of left and
674    right curly braces.
675
676    If a left brace occurs in the middle of a page or paragraph, it simply introduces a new level
677    of grouping, and the matching right brace will not have such a drastic effect. Such grouping
678    affects neither the mode nor the current list.
679
680*/
681
682static void tex_aux_run_left_brace(void) 
683{
684    tex_new_save_level(simple_group);
685    update_tex_internal_par_state(0);
686    update_tex_internal_dir_state(0);
687}
688
689/*tex
690
691    The |also_simple_group| variant is triggered by |\beginsimplegroup|. It permits a mixed group
692    ending model:
693
694    \starttyping
695    \def\foo{\beginsimplegroup\bf\let\next}  \foo{text}
696    \stoptyping
697
698    So, such a group can end with |\endgroup| as well as |\egroup| or equivalents. This trick is
699    mostly meant for math where a complex group produces a list which in turn influences spacing.
700
701*/
702
703static void tex_aux_run_begin_group(void) 
704{
705    switch (cur_chr) {
706        case semi_simple_group_code:
707        case also_simple_group_code:
708            tex_new_save_level(cur_chr ? also_simple_group : semi_simple_group);
709            update_tex_internal_par_state(0);
710            update_tex_internal_dir_state(0);
711            break;
712        case math_simple_group_code:
713            tex_new_save_level(math_simple_group);
714            update_tex_internal_math_style(cur_mode == mmode ? cur_list.math_style : -1);
715            update_tex_internal_math_scale(cur_mode == mmode ? cur_list.math_scale : 0);
716            break;
717    }
718}
719
720static void tex_aux_run_end_group(void) 
721{
722    switch (cur_group) {
723        case semi_simple_group:
724        case also_simple_group:
725            tex_aux_fixup_directions_and_unsave(); /*tex Includes the |save()| call! */
726            break;
727        case math_simple_group:
728            tex_aux_fixup_math_and_unsave(); /*tex Includes the |save()| call! */
729            break;
730        default:
731            tex_off_save(); /*tex Recover with error. */
732            break;
733    }
734}
735
736/*tex
737
738    Constructions that require a box are started by calling |scan_box| with a specified context
739    code. The |scan_box| routine verifies that a |make_box| command comes next and then it calls
740    |begin_box|.
741
742    Maybe we should just have three variants as sharing this makes it messy: |cur_cmd| combined
743    with |cur_chr| and funny flags for leaders. Due to grouping we have a shared |box_end| so
744    it doesn't become much prettier anyway.
745
746 */
747
748static void tex_aux_scan_box(int boxcontext, int optional_equal, scaled shift, halfword slot, halfword callback, halfword leaders)
749{
750    /*tex Get the next non-blank non-relax... and optionally skip an equal sign */
751    while (1) {
752        tex_get_x_token();
753        if (cur_cmd == spacer_cmd) {
754            /*tex Go on. */
755        } else if (cur_cmd == relax_cmd) {
756            optional_equal = 0;
757        } else if (optional_equal && cur_tok == equal_token) {
758            optional_equal = 0;
759        } else {
760            break;
761        }
762    }
763    switch (cur_cmd) {
764        case make_box_cmd:
765            {
766                tex_begin_box(boxcontext, shift, slot, callback, leaders);
767                return;
768            }
769        case vcenter_cmd:
770            {
771                tex_run_vcenter();
772                return;
773            }
774        case lua_call_cmd:
775        case lua_protected_call_cmd:
776        case lua_semi_protected_call_cmd:
777            {
778                if (box_leaders_flag(boxcontext)) {
779                    tex_aux_run_lua_protected_call();
780                    tex_get_next();
781                    if (cur_cmd == node_cmd) {
782                        /*tex So we only fetch the tail; the rest can mess up in the current list! */
783                        halfword boxnode = null;
784                        tex_aux_run_node();
785                        boxnode = tex_pop_tail();
786                        if (boxnode) {
787                            switch (node_type(boxnode)) {
788                                case hlist_node:
789                                case vlist_node:
790                                case rule_node:
791                                case glyph_node:
792                                    tex_box_end(boxcontext, boxnode, shift, unset_noad_class, slot, callback, leaders);
793                                    return;
794                            }
795                        }
796                    }
797                    tex_formatted_error("lua", "invalid function call, proper leader content expected");
798                    return;
799                } else {
800                    break;
801                }
802            }
803        case lua_value_cmd:
804            {
805                halfword v = tex_scan_lua_value(cur_chr);
806                switch (v) {
807                    case no_val_level:
808                        tex_box_end(boxcontext, null, shift, unset_noad_class, slot, callback, leaders);
809                        return;
810                    case list_val_level:
811                        if (box_leaders_flag(boxcontext)) {
812                            switch (node_type(cur_val)) {
813                                case hlist_node:
814                                case vlist_node:
815                                case rule_node:
816                             // case glyph_node:
817                                    tex_box_end(boxcontext, cur_val, shift, unset_noad_class, slot, callback, leaders);
818                                    return;
819                            }
820                        } else {
821                            switch (node_type(cur_val)) {
822                                case hlist_node:
823                                case vlist_node:
824                                    tex_box_end(boxcontext, cur_val, shift, unset_noad_class, slot, callback, leaders);
825                                    return;
826                            }
827                        }
828                }
829                tex_formatted_error("lua", "invalid function call, return type %i instead of %i", v, list_val_level);
830                return;
831            }
832        case hrule_cmd:
833        case vrule_cmd:
834            {
835                if (box_leaders_flag(boxcontext)) {
836                    halfword rulenode = tex_aux_scan_rule_spec(cur_cmd == hrule_cmd ? h_rule_type : (cur_cmd == vrule_cmd ? v_rule_type : m_rule_type), cur_chr);
837                    tex_box_end(boxcontext, rulenode, shift, unset_noad_class, slot, callback, leaders);
838                    return;
839                } else {
840                    break;
841                }
842            }
843        case char_number_cmd:
844            {
845                if (cur_mode == hmode && box_leaders_flag(boxcontext)) {
846                    /*tex We cheat by just appending to the current list. */
847                    halfword boxnode = null;
848                    tex_aux_run_text_char_number();
849                    boxnode = tex_pop_tail();
850                    tex_box_end(boxcontext, boxnode, shift, unset_noad_class, slot, callback, leaders);
851                    return;
852                } else {
853                    break;
854                }
855            }
856    }
857    tex_handle_error(
858        back_error_type,
859        "A <box> was supposed to be here",
860        "I was expecting to see \\hbox or \\vbox or \\copy or \\box or something like\n"
861        "that. So you might find something missing in your output. But keep trying; you\n"
862        "can fix this later."
863    );
864    if (boxcontext == lua_scan_flag) { /* hm, why after error */
865        tex_box_end(boxcontext, null, shift, unset_noad_class, slot, callback, leaders);
866    }
867}
868
869/*tex
870    The |tex_aux_scan_box| call takes a |context| parameter and that is is somewhat weird: it
871    can be a box number, a flag signaling a special kind of box like a leader, or it can be the
872    shift in a move. It all relates to passing something in a way that make it possible to pick
873    it up later.
874*/
875
876static void tex_aux_run_move(void) 
877{
878    int code = cur_chr;
879    halfword val = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
880    tex_aux_scan_box(direct_box_flag, 0, code == move_forward_code ? val : - val, -1, 0, 0);
881}
882
883static int leader_flags[] = {
884    a_leaders_flag,
885    c_leaders_flag,
886    x_leaders_flag,
887    g_leaders_flag,
888    u_leaders_flag,
889};
890
891static void tex_aux_run_leader(void) 
892{
893    halfword code = cur_chr; 
894    halfword callback = 0; 
895    halfword options = 0;
896    if (code == u_leaders_code) { 
897        while (1) {
898            switch (tex_scan_character("clnCLN", 0, 1, 0)) {
899                case 0:
900                    goto DONE;
901                case 'l': case 'L':
902                    if (tex_scan_mandate_keyword("line", 1)) {
903                        options |= glue_option_u_leaders_line;
904                    }
905                    break;
906                case 'n': case 'N':
907                    if (tex_scan_mandate_keyword("nobreak", 1)) {
908                        options |= glue_option_no_auto_break;
909                    }
910                    break;
911                case 'c': case 'C':
912                    if (tex_scan_mandate_keyword("callback", 1)) {
913                        callback = tex_scan_integer(0, NULL, NULL);
914                    }
915                    break;
916                default:
917                    tex_aux_show_keyword_error("callback|line|nobreak");
918                    goto DONE;
919            }
920        }
921      DONE: ;
922    }
923    tex_aux_scan_box(leader_flags[code], 0, null_flag, -1, callback, options);
924}
925
926static void tex_aux_run_legacy(void) 
927{
928    switch (cur_chr) {
929        case shipout_code:
930            tex_aux_scan_box(shipout_flag, 0, null_flag, -1, 0, 0);
931            break;
932        default:
933            /* cant_happen */
934            break;
935    }
936}
937
938static void tex_aux_run_local_box(void) 
939{
940    switch (cur_chr) { 
941    case local_reset_boxes_code:
942            tex_reset_local_box(local_reset_boxes_code);
943            break;
944        default:
945            tex_aux_scan_local_box(cur_chr);
946            break;
947    }
948}
949
950static void tex_aux_run_make_box(void) 
951{
952    tex_begin_box(direct_box_flag, null_flag, -1, 0, 0);
953}
954
955/*tex
956
957    There is a really small patch to add a new primitive called |\quitvmode|. In vertical modes, it
958    is identical to |\indent|, but in horizontal and math modes it is really a no-op (as opposed to
959    |\indent|, which executes the |indent_in_hmode| procedure).
960
961    A paragraph begins when horizontal-mode material occurs in vertical mode, or when the paragraph
962    is explicitly started by |\quitvmode|, |\indent| or |\noindent|. We can revert this to zero
963    while at the same time keeping the node.
964
965    To be considered: delay (as with parfilskip), skip + boundary, pre/post anchor etc.
966
967*/
968
969static void tex_aux_insert_parindent(int indented)
970{
971    if (normalize_line_mode_option(parindent_skip_mode)) {
972        /*tex We cannot use |new_param_glue| yet, because it's a dimen */
973        halfword glue = tex_new_glue_node(zero_glue, indent_skip_glue);
974        if (indented) {
975            glue_amount(glue) = par_indent_par;
976        }
977        tex_tail_append(glue);
978    } else if (indented) {
979        halfword box = tex_new_null_box_node(hlist_node, indent_list);
980        box_dir(box) = (singleword) par_direction_par;
981        box_width(box) = par_indent_par;
982        tex_tail_append(box);
983    }
984}
985
986static void tex_aux_remove_parindent(void)
987{
988    halfword tail = cur_list.tail;
989    switch (node_type(tail)) {
990        case glue_node:
991            if (tex_is_par_init_glue(tail)) {
992                glue_amount(tail) = 0;
993            }
994            break;
995        case hlist_node:
996            if (node_subtype(tail) == indent_list) {
997                box_width(tail) = 0;
998            }
999            break;
1000    }
1001}
1002
1003static void tex_aux_run_begin_paragraph_vmode(void) 
1004{
1005    switch (cur_chr) {
1006        case noindent_par_code:
1007            tex_begin_paragraph(0, no_indent_par_trigger);
1008            break;
1009        case indent_par_code:
1010            tex_begin_paragraph(1, indent_par_trigger);
1011            break;
1012        case quitvmode_par_code:
1013            tex_begin_paragraph(1, force_par_trigger);
1014            break;
1015        case snapshot_par_code:
1016            /* silently ignore */
1017            tex_scan_integer(0, NULL, NULL);
1018            break;
1019        case attribute_par_code:
1020            /* silently ignore */
1021            tex_scan_attribute_register_number();
1022            tex_scan_integer(1, NULL, NULL);
1023            break;
1024        case options_par_code:
1025            /* silently ignore */
1026            tex_scan_integer(1, NULL, NULL);
1027            break;
1028        case wrapup_par_code:
1029            tex_you_cant_error(NULL);
1030            break;
1031    }
1032}
1033
1034static bool tex_aux_scan_more_toks(halfword *h)
1035{
1036    int reverse = tex_scan_optional_keyword("reverse"); /* maybe not _optional_ */
1037    do {
1038        tex_get_x_token();
1039    } while (cur_cmd == spacer_cmd);
1040    if (cur_cmd == left_brace_cmd) {
1041        halfword source = tex_scan_toks_normal(1, NULL);
1042        if (source) {
1043            if (! *h) {
1044                *h = source;
1045            } else if (reverse) {
1046                token_link(tex_tail_of_token_list(*h)) = token_link(source);
1047                tex_put_available_token(source);
1048            } else { 
1049                token_link(tex_tail_of_token_list(source)) = token_link(*h);
1050                tex_put_available_token(*h);
1051                *h = source;
1052            }
1053        }
1054        return true;
1055    } else { 
1056        return false;
1057    }
1058}
1059
1060static void tex_aux_run_begin_paragraph_hmode(void) 
1061{
1062    switch (cur_chr) {
1063        case noindent_par_code:
1064            /*tex We do as traditional \TEX, so no zero skip either when normalizing */
1065            break;
1066        case indent_par_code:
1067            /*tex We can have |\hbox {\indent x\indent x\indent}| */
1068            tex_aux_insert_parindent(1);
1069            break;
1070        case undent_par_code:
1071            tex_aux_remove_parindent();
1072            break;
1073        case snapshot_par_code:
1074            {
1075                halfword tag = tex_scan_integer(0, NULL, NULL);
1076                halfword par = tex_find_par_par(cur_list.head);
1077                if (par) {
1078                    tex_snapshot_par(par, tag);
1079                }
1080                break;
1081            }
1082        case attribute_par_code:
1083            {
1084                halfword att = tex_scan_attribute_register_number();
1085                halfword val = tex_scan_integer(1, NULL, NULL);
1086                halfword par = tex_find_par_par(cur_list.head);
1087                if (par) {
1088                    if (val == unused_attribute_value) {
1089                        tex_unset_attribute(par, att, val);
1090                    } else {
1091                        tex_set_attribute(par, att, val);
1092                    }
1093                }
1094                break;
1095            }
1096        case options_par_code:
1097            {
1098                halfword val = tex_scan_integer(1, NULL, NULL);
1099                halfword par = tex_find_par_par(cur_list.head);
1100                if (par && val >= 0) {
1101                    par_options(par) |= (singleword) val;
1102                }
1103                break;
1104            }
1105        case wrapup_par_code:
1106            {
1107                halfword par = tex_find_par_par(cur_list.head);
1108                if (par) {
1109                    halfword h = par_end_par_tokens(par);
1110                    if (tex_aux_scan_more_toks(&h)) {
1111                        par_end_par_tokens(par) = h;
1112                    } else {
1113                        tex_handle_error(
1114                            normal_error_type,
1115                            "I expected a {",
1116                            "The '\\wrapuppar' command only accepts an explicit token list."
1117                        );
1118                    }
1119                }
1120                break;
1121            }
1122    }
1123}
1124
1125static void tex_aux_run_begin_paragraph_mmode(void) 
1126{
1127    switch (cur_chr) {
1128        case indent_par_code:
1129            {
1130                /*tex This is kind of weird, we could also support a skip here. */
1131                halfword p = tex_new_null_box_node(hlist_node, indent_list);
1132                box_width(p) = par_indent_par;
1133                p = tex_new_sub_box(p);
1134                tex_tail_append(p);
1135                break;
1136            }
1137        case snapshot_par_code:
1138            /* silently ignore */
1139            tex_scan_integer(0, NULL, NULL);
1140            break;
1141        case attribute_par_code:
1142            /* silently ignore */
1143            tex_scan_attribute_register_number();
1144            tex_scan_integer(1, NULL, NULL);
1145            break;
1146        case wrapup_par_code:
1147            tex_you_cant_error(NULL);
1148            break;
1149    }
1150}
1151
1152static void tex_aux_run_new_paragraph(void) 
1153{
1154    int context;
1155    switch (cur_cmd) {
1156        case char_given_cmd:
1157        case other_char_cmd:
1158        case letter_cmd:
1159        case accent_cmd:
1160        case char_number_cmd:
1161        case discretionary_cmd:
1162            context = char_par_trigger;
1163            break;
1164        case boundary_cmd:
1165            context = boundary_par_trigger;
1166            break;
1167        case explicit_space_cmd:
1168            context = space_par_trigger;
1169            break;
1170        case math_shift_cmd:
1171        case math_shift_cs_cmd:
1172            context = math_par_trigger;
1173            break;
1174        case hskip_cmd:
1175            context = hskip_par_trigger;
1176            break;
1177        case kern_cmd:
1178            context = kern_par_trigger;
1179            break;
1180        case un_hbox_cmd:
1181            context = un_hbox_char_par_trigger;
1182            break;
1183        case valign_cmd:
1184            context = valign_char_par_trigger;
1185            break;
1186        case vrule_cmd:
1187            context = vrule_char_par_trigger;
1188            break;
1189        default:
1190            context = normal_par_trigger;
1191            break;
1192    }
1193    if (tracing_commands_par >= 4) {
1194        tex_begin_diagnostic();
1195        tex_print_format("[text: pushing back %C]", cur_cmd, cur_chr);
1196        tex_end_diagnostic();
1197    }
1198    tex_back_input(cur_tok);
1199    tex_begin_paragraph(1, context);
1200}
1201
1202/*tex
1203    Append a |boundary_node|. The |page_boundary| case is kind of special. It adds a node node to
1204    the list of contributions and triggers the page builder (that only kicks in when there is some
1205    contribution). That itself can result in firing up the output routine if the page is filled up.
1206    An alternative is to inject a penalty but we don't want anything to stay behind and using some
1207    special penalty would be incompatible.
1208
1209    In order to really trigger a check we change the boundary node into zero penalty in the builder
1210    when it still present (as the callback can decide to wipe it). It's a bit weird mechanism but
1211    it closely relates to triggering something that gets logged in the core engine. Anyway, we
1212    basically have a zero penalty equivalent (but one that doesn't register as last node).
1213*/
1214
1215void tex_page_boundary_message(const char *s, halfword n)
1216{
1217    if (tracing_pages_par > 0) {
1218        tex_begin_diagnostic();
1219        tex_print_format("[page: boundary, %s, trigger %i]", s, n);
1220        tex_end_diagnostic();
1221    }
1222}
1223
1224static void tex_aux_run_par_boundary(void) 
1225{
1226    switch (cur_chr) {
1227        case page_boundary:
1228            {   
1229                halfword n = tex_scan_integer(0, NULL, NULL);
1230                if (lmt_nest_state.nest_data.ptr == 0 && ! lmt_page_builder_state.output_active) {
1231                    halfword boundary = tex_new_node(boundary_node, page_boundary);
1232                    boundary_data(boundary) = n;
1233                    tex_tail_append(boundary);
1234                    if (cur_list.mode == vmode) {
1235                        tex_page_boundary_message("build triggered", n);
1236                        tex_build_page(boundary_page_context, n);
1237                    } else {
1238                        tex_page_boundary_message("appended", n);
1239                    }
1240                } else {
1241                    tex_page_boundary_message("ignored", n);
1242                }
1243                break;
1244            }
1245        case balance_boundary:
1246            {
1247                halfword n = tex_scan_integer(0, NULL, NULL);
1248                halfword m = tex_scan_integer(0, NULL, NULL);
1249                if (cur_list.mode == vmode && ! lmt_page_builder_state.output_active) {
1250                    halfword boundary = tex_new_node(boundary_node, balance_boundary);
1251                    boundary_data(boundary) = n;
1252                    boundary_reserved(boundary) = m;
1253                    tex_tail_append(boundary);
1254                }
1255                break;
1256            }
1257        /*tex Not yet, first I need a proper use case. */ /*
1258        case par_boundary:
1259            {
1260                halfword boundary = tex_new_node(boundary_node, par_boundary);
1261                boundary_data(boundary) = tex_scan_integer(0, NULL, NULL);
1262                tex_tail_append(boundary);
1263                break;
1264            }
1265        */
1266        default:
1267            /*tex Go into horizontal mode and try again (was already the modus operandi). */
1268            tex_aux_run_new_paragraph();
1269            break;
1270    }
1271}
1272
1273static void tex_aux_run_text_boundary(void) 
1274{
1275    halfword boundary = tex_new_node(boundary_node, (quarterword) cur_chr);
1276    switch (cur_chr) {
1277        case user_boundary:
1278        case protrusion_boundary:
1279        case optional_boundary:
1280            boundary_data(boundary) = tex_scan_integer(0, NULL, NULL);
1281            break;
1282        case lua_boundary:
1283            boundary_data(boundary) = tex_scan_integer(0, NULL, NULL);
1284            boundary_reserved(boundary) = tex_scan_integer(0, NULL, NULL);
1285            break;
1286        case balance_boundary:
1287            /*tex Maybe we should force vmode? For now we just ignore the value. */
1288            tex_scan_integer(0, NULL, NULL);
1289            /* fall through */
1290        case page_boundary:
1291            /*tex Maybe we should force vmode? For now we just ignore the value. */
1292            tex_scan_integer(0, NULL, NULL);
1293            break;
1294        default:
1295            break;
1296    }
1297    tex_tail_append(boundary);
1298}
1299
1300static void tex_aux_run_math_boundary(void) 
1301{
1302    switch (cur_chr) {
1303        case user_boundary:
1304            {
1305                halfword boundary = tex_new_node(boundary_node, (quarterword) cur_chr);
1306                boundary_data(boundary) = tex_scan_integer(0, NULL, NULL);
1307                tex_tail_append(boundary);
1308                break;
1309            }
1310        case lua_boundary:
1311            {
1312                halfword boundary = tex_new_node(boundary_node, (quarterword) cur_chr);
1313                boundary_data(boundary) = tex_scan_integer(0, NULL, NULL);
1314                boundary_reserved(boundary) = tex_scan_integer(0, NULL, NULL);
1315                tex_tail_append(boundary);
1316                break;
1317            }
1318        case math_boundary:
1319            {
1320                halfword boundary = tex_new_node(boundary_node, (quarterword) cur_chr);
1321                boundary_data(boundary) = tex_scan_integer(0, NULL, NULL);
1322                switch (boundary_data(boundary)) {
1323                    case begin_math_implicit_boundary: 
1324                    case end_math_implicit_boundary: 
1325                        /* valid */
1326                        break;
1327                    case begin_math_explicit_boundary: 
1328                    case end_math_explicit_boundary: 
1329                        /* valid, penalty to add */
1330                        boundary_reserved(boundary) = tex_scan_integer(0, NULL, NULL);
1331                        break;
1332                }
1333                tex_tail_append(boundary);
1334                break;
1335            }
1336        case protrusion_boundary:
1337        case page_boundary:
1338            /*tex We just ignore the values. */
1339            tex_scan_integer(0, NULL, NULL);
1340            break;
1341    }
1342}
1343
1344/*tex
1345
1346    A paragraph ends when a |par_end| command is sensed, or when we are in horizontal mode when
1347    reaching the right brace of vertical-mode routines like |\vbox|, |\insert|, or |\output|.
1348
1349*/
1350
1351static void tex_aux_run_paragraph_end_vmode(void) 
1352{
1353 // tex_normal_paragraph(normal_par_context);
1354    tex_normal_paragraph(vmode_par_context);
1355    if (cur_list.mode > nomode) {
1356        tex_build_page(vmode_par_page_context, 0);
1357    }
1358}
1359
1360/*tex We could pass the group and context here if needed and set some parameter. */
1361
1362int tex_wrapped_up_paragraph(int context, int final) 
1363{
1364    halfword par = tex_find_par_par(cur_list.head);
1365    lmt_main_control_state.last_par_context = context;
1366    if (par) {
1367        int done = 0;
1368        if (par_end_par_tokens(par)) {
1369            halfword eop = par_end_par_tokens(par);
1370            par_end_par_tokens(par) = null;
1371            tex_back_input(cur_tok);
1372            /*tex We  inject the tokens, which increments the ref count; this one has tracing. */
1373            tex_begin_token_list(eop, end_paragraph_text);
1374            /*tex So we need to decrement the token ref here. */
1375            tex_delete_token_reference(eop);
1376            done = 1;
1377        }
1378        if (final && end_of_group_par) {
1379            if (! done) {
1380                tex_back_input(cur_tok);
1381            }
1382            tex_begin_token_list(end_of_group_par, end_paragraph_text);
1383            update_tex_end_of_group(null);
1384            done = 1;
1385        }
1386        return done;
1387    } else {
1388        return 0;
1389    }
1390}
1391
1392static void tex_aux_run_paragraph_end_hmode(void) 
1393{
1394    if (cur_chr == local_break_end_paragraph_code) {
1395        /*tex Just for fun: an experiment. */
1396        tex_tail_append(tex_new_par_node(local_break_par_subtype));
1397    } else if (! tex_wrapped_up_paragraph(normal_par_context, 0)) {
1398        if (lmt_input_state.align_state < 0) {
1399            /*tex This tries to recover from an alignment that didn't end properly. */
1400            tex_off_save();
1401        }
1402        /*tex This takes us to the enclosing mode, if |mode > 0|. */
1403        tex_end_paragraph(bottom_level_group, normal_par_context);
1404        if (cur_list.mode == vmode) {
1405            tex_build_page(hmode_par_page_context, 0);
1406        }
1407    }
1408}
1409
1410/* */
1411
1412static void tex_aux_run_halign_mmode(void) 
1413{
1414    switch (cur_group) { 
1415        case math_inline_group:
1416        case math_display_group:
1417            tex_run_alignment_initialize();
1418            break;
1419        default:
1420            tex_off_save();
1421            break;
1422    }
1423}
1424
1425/*tex
1426
1427    The |\afterassignment| command puts a token into the global variable |after_token|. This global
1428    variable is examined just after every assignment has been performed. It's value is zero, or a
1429    saved token.
1430
1431*/
1432
1433static void tex_aux_run_after_something(void) 
1434{
1435    switch (cur_chr) {
1436        case after_group_code:
1437            {
1438                halfword t = tex_get_token(); /* avoid realloc issues */
1439                t = tex_get_available_token(t);
1440                tex_save_for_after_group(t);
1441                break;
1442            }
1443        case after_grouped_code:
1444            {
1445                do {
1446                    tex_get_x_token();
1447                } while (cur_cmd == spacer_cmd);
1448                if (cur_cmd == left_brace_cmd) {
1449                    halfword source = tex_scan_toks_normal(1, NULL);
1450                    if (source) { 
1451                        if (token_link(source)) {
1452                            tex_save_for_after_group(token_link(source));
1453                            token_link(source) = null;
1454                        }
1455                        tex_put_available_token(source);
1456                    }
1457                } else {
1458                    tex_handle_error(
1459                        normal_error_type,
1460                        "I expected a {",
1461                        "The '\\aftergrouped' command only accepts an explicit token list."
1462                    );
1463                }
1464                break;
1465            }
1466        case after_assignment_code:
1467            {
1468                lmt_main_control_state.after_token = tex_get_token();
1469                break;
1470            }
1471        case after_assigned_code:
1472            {
1473                do {
1474                    tex_get_x_token();
1475                } while (cur_cmd == spacer_cmd);
1476                if (cur_cmd == left_brace_cmd) {
1477                    halfword source = tex_scan_toks_normal(1, NULL);
1478                    if (source) {
1479                        /*tex Always, also when empty. */
1480                        lmt_main_control_state.after_tokens = token_link(source);
1481                        token_link(source) = null;
1482                        tex_put_available_token(source);
1483                    }
1484                } else {
1485                    tex_handle_error(
1486                        normal_error_type,
1487                        "I expected a {",
1488                        "The '\\afterassigned' command only accepts an explicit token list."
1489                    );
1490                }
1491                break;
1492            }
1493        case at_end_of_group_code:
1494            {
1495                halfword t = tex_get_token(); /* avoid realloc issues */
1496                switch (token_cmd(t)) {
1497                    case left_brace_cmd:
1498                    case right_brace_cmd:
1499                        /*tex We don't want to crash. */
1500                        tex_handle_error(
1501                            normal_error_type,
1502                            "I don't expected a { or }",
1503                            "The '\\atendofgroup' command won't handle this, maybe you want \\atendofgrouped instead."
1504                        );
1505                        break;
1506                    default: 
1507                        {
1508                            halfword r = tex_get_available_token(t);
1509                            if (end_of_group_par) {
1510                                halfword p = tex_tail_of_token_list(end_of_group_par);
1511                                token_link(p) = r;
1512                            } else {
1513                                halfword p = tex_get_available_token(null);
1514                                token_link(p) = r;
1515                                update_tex_end_of_group(p);
1516                            }
1517                        }
1518                        break;
1519                }
1520                break;
1521            }
1522        case at_end_of_grouped_code:
1523            {
1524                do {
1525                    tex_get_x_token();
1526                } while (cur_cmd == spacer_cmd);
1527                if (cur_cmd == left_brace_cmd) {
1528                    halfword source = tex_scan_toks_normal(1, NULL);
1529                    if (source) {
1530                        if (end_of_group_par) {
1531                            halfword p = tex_tail_of_token_list(end_of_group_par);
1532                            token_link(p) = token_link(source);
1533                            token_link(source) = null;
1534                            tex_put_available_token(source);
1535                        } else {
1536                            update_tex_end_of_group(source);
1537                        }
1538                    }
1539                } else {
1540                    tex_handle_error(
1541                        normal_error_type,
1542                        "I expected a {",
1543                        "The '\\endofgrouped' command only accepts an explicit token list."
1544                    );
1545                }
1546                break;
1547            }
1548        case at_end_of_file_code:
1549            {
1550                halfword t = tex_get_token(); /* avoid realloc issues */
1551                halfword r = tex_get_available_token(t);
1552                halfword h = tex_get_at_end_of_file();
1553                h = h ? tex_tail_of_token_list(h) : tex_get_available_token(null);
1554                token_link(h) = r;
1555                tex_set_at_end_of_file(h);
1556                break;
1557            }
1558        case at_end_of_filed_code:
1559            {
1560                halfword h = tex_get_at_end_of_file();
1561                if (tex_aux_scan_more_toks(&h)) {
1562                    tex_set_at_end_of_file(h);
1563                } else {
1564                    tex_handle_error(
1565                        normal_error_type,
1566                        "I expected a {",
1567                        "The '\\endoffiled' command only accepts an explicit token list."
1568                    );
1569                }
1570                break;
1571            }
1572    }
1573}
1574
1575static inline void tex_aux_finish_after_assignment(void)
1576{
1577    if (lmt_main_control_state.after_token) {
1578        tex_back_input(lmt_main_control_state.after_token);
1579        lmt_main_control_state.after_token = null;
1580    }
1581    if (lmt_main_control_state.after_tokens) {
1582        tex_begin_inserted_list(lmt_main_control_state.after_tokens);
1583        lmt_main_control_state.after_tokens = null;
1584    }
1585}
1586
1587static void tex_aux_invalid_catcode_table_error(void) 
1588{
1589    tex_handle_error(
1590        normal_error_type,
1591        "Invalid \\catcode table",
1592        "All \\catcode table ids must be between 0 and " LMT_TOSTRING(max_n_of_catcode_tables - 1)
1593    );
1594}
1595
1596static void tex_aux_overwrite_catcode_table_error(void) 
1597{
1598    tex_handle_error(
1599        normal_error_type,
1600        "Invalid \\catcode table",
1601        "You cannot overwrite the current \\catcode table"
1602    );
1603}
1604
1605static void tex_aux_run_catcode_table(void) 
1606{
1607    switch (cur_chr) {
1608        case save_cat_code_table_code:
1609            {
1610                halfword v = tex_scan_integer(0, NULL, NULL);
1611                if ((v < 0) || (v >= max_n_of_catcode_tables)) {
1612                    tex_aux_invalid_catcode_table_error();
1613                } else if (v == cat_code_table_par) {
1614                    tex_aux_overwrite_catcode_table_error();
1615                } else {
1616                    tex_copy_cat_codes(cat_code_table_par, v);
1617                }
1618                break;
1619            }
1620        case restore_cat_code_table_code:
1621            {
1622                halfword v = tex_scan_integer(0, NULL, NULL);
1623                if ((v < 0) || (v >= max_n_of_catcode_tables)) {
1624                    tex_aux_invalid_catcode_table_error();
1625                } else {
1626                    tex_restore_cat_codes(v, cur_level);
1627                }
1628                break;
1629            }
1630        case init_cat_code_table_code:
1631            {
1632                halfword v = tex_scan_integer(0, NULL, NULL);
1633                if ((v < 0) || (v >= max_n_of_catcode_tables)) {
1634                    tex_aux_invalid_catcode_table_error();
1635                } else if (v == cat_code_table_par) {
1636                    tex_aux_overwrite_catcode_table_error();
1637                } else {
1638                    tex_initialize_cat_codes(v);
1639                }
1640                break;
1641            }
1642            /*
1643        case dflt_cat_code_table_code:
1644            {
1645                halfword v = scan_int(1);
1646                if ((v < 0) || (v > CATCODE_MAX)) {
1647                    invalid_catcode_table_error();
1648                } else {
1649                    set_cat_code_table_default(cat_code_table_par, v);
1650                }
1651            }
1652            break;
1653            */
1654        default:
1655            break;
1656    }
1657}
1658
1659static void tex_aux_run_end_local(void)
1660{
1661    if (tracing_nesting_par > 2) {
1662        tex_local_control_message("leaving token scanner due to local end token");
1663    }
1664    tex_end_local_control();
1665}
1666
1667static void tex_aux_run_lua_function_call(void)
1668{
1669    switch (cur_chr) {
1670        case lua_function_call_code:
1671            {
1672                halfword v = tex_scan_function_reference(0);
1673                lmt_lua_run(v, 0);
1674                break;
1675            }
1676        case lua_bytecode_call_code:
1677            {
1678                halfword v = tex_scan_bytecode_reference(0);
1679                lmt_bytecode_run(v);
1680                break;
1681            }
1682        default:
1683            break;
1684    }
1685}
1686
1687/*tex
1688
1689    The |main_control| uses a jump table, and |init_main_control| sets that table up. We need to
1690    assign an entry for {\em each} of the three modes! The jump table is gone. 
1691
1692    For mode-independent commands, the following macro is useful. Also, there is a list of cases
1693    where the user has probably gotten into or out of math mode by mistake. \TEX\ will insert a
1694    dollar sign and rescan the current token, and it makes sense to have a macro for that as well.
1695
1696    Here is |main_control| itself. It is quite short nowadays.  The initializer is at the end of
1697    this file which saves a nunch of forward declarations.
1698
1699 */
1700
1701int tex_main_control(void)
1702{
1703    lmt_main_control_state.control_state = goto_next_state;
1704    if (every_job_par) {
1705        tex_begin_token_list(every_job_par, every_job_text);
1706    }
1707    while (1) {
1708        switch (lmt_main_control_state.control_state) { 
1709            case goto_next_state:
1710                tex_get_x_token();
1711                break;
1712            case goto_skip_token_state:
1713                lmt_main_control_state.control_state = goto_next_state;
1714                break;
1715            case goto_return_state:
1716                return lmt_main_state.run_state == initializing_state && cur_chr == dump_code;
1717        }
1718        /*tex
1719            Give diagnostic information, if requested. When a new token has just been fetched at
1720            |big_switch|, we have an ideal place to monitor \TEX's activity.
1721        */
1722        if (tracing_commands_par > 0) {
1723            tex_show_cmd_chr(cur_cmd, cur_chr);
1724        }
1725        /*tex Run the command: */
1726        tex_aux_big_switch(cur_mode, cur_cmd);
1727    }
1728    return 0; /* unreachable */
1729}
1730
1731/*tex
1732
1733    We assume a trailing |\relax|: |{...}\relax|, so we don't need a |back_input ()| here.
1734
1735*/
1736
1737void tex_local_control_message(const char *s)
1738{
1739    tex_begin_diagnostic();
1740    tex_print_format("[local control: level %i, %s]", lmt_main_control_state.local_level, s);
1741    tex_end_diagnostic();
1742}
1743
1744/*tex
1745
1746    We can save in two ways but when, for symmetry I want it to happen at the current level, we need
1747    to use the save stack. It depends a bit on how this will evolve.
1748
1749    This one is used in the runlocal \LUA\ helper. This local control is in fact like the main loop,
1750    so it can result in stuff being injected in for instance the main vertical list. I played with
1751    control over the mode but that gave weird side effects, so I dropped that immediately.
1752
1753    The implementation of local control in \LUAMETATEX\ is a bit different from \LUATEX\ because we
1754    use it in several ways.
1755
1756*/
1757
1758void tex_local_control(int obeymode)
1759{
1760    full_scanner_status saved_full_status = tex_save_full_scanner_status();
1761    int old_mode = cur_list.mode;
1762    int at_level = lmt_main_control_state.local_level;
1763    lmt_main_control_state.local_level += 1;
1764    lmt_main_control_state.control_state = goto_next_state;
1765    if (! obeymode) {
1766        cur_list.mode = restricted_hmode;
1767    }
1768    while (1) {
1769        if (lmt_main_control_state.control_state == goto_skip_token_state) {
1770            lmt_main_control_state.control_state = goto_next_state;
1771        } else {
1772            tex_get_x_token();
1773        }
1774        if (tracing_commands_par > 0) {
1775            tex_show_cmd_chr(cur_cmd, cur_chr);
1776        }
1777        tex_aux_big_switch(cur_mode, cur_cmd);
1778        if (lmt_main_control_state.local_level <= at_level) {
1779            lmt_main_control_state.control_state = goto_next_state;
1780            if (tracing_nesting_par > 2) {
1781                /*tex This is a kind of duplicate message, which can be confusing */
1782                tex_local_control_message("leaving local control due to level change");
1783            }
1784            break;
1785        } else if (lmt_main_control_state.control_state == goto_return_state) {
1786            if (tracing_nesting_par > 2) {
1787                tex_local_control_message("leaving local control due to triggering");
1788            }
1789            break;
1790        }
1791    }
1792    if (! obeymode) {
1793        cur_list.mode = old_mode;
1794    }
1795    tex_unsave_full_scanner_status(saved_full_status);
1796}
1797
1798static inline int tex_aux_is_iterator_value(halfword tokeninfo)
1799{
1800    if (tokeninfo >= cs_token_flag) {
1801        halfword cs = tokeninfo - cs_token_flag;
1802        return eq_type(cs) == some_item_cmd && eq_value(cs) == last_loop_iterator_code;
1803    } else {
1804        return 0;
1805    }
1806}
1807
1808static inline void tex_push_stack_entry(void)
1809{
1810    halfword state = tex_get_node(loop_state_node_size);
1811    node_type(state) = loop_state_node;
1812    loop_state_count(state) = 0;
1813    if (lmt_main_control_state.loop_stack_head) { 
1814        node_prev(lmt_main_control_state.loop_stack_head) = state;
1815        node_next(state) = lmt_main_control_state.loop_stack_head;
1816    } else { 
1817        lmt_main_control_state.loop_stack_tail = state;
1818    }
1819    lmt_main_control_state.loop_stack_head = state;
1820}
1821
1822static inline void tex_pop_stack_entry(void)
1823{
1824    halfword state = lmt_main_control_state.loop_stack_head;    
1825    lmt_main_control_state.loop_stack_head = node_next(state);
1826    if (! lmt_main_control_state.loop_stack_head) {
1827        lmt_main_control_state.loop_stack_tail = null;
1828    }
1829    tex_free_node(state, loop_state_node_size);
1830}
1831
1832halfword tex_nested_loop_iterator(void)
1833{
1834    halfword delta = tex_scan_integer(0, NULL, NULL);
1835    halfword state = lmt_main_control_state.loop_stack_tail;
1836    while (delta-- > 0 && state) {
1837        state = node_prev(state);
1838    }
1839    return state ? loop_state_count(state) : 0;
1840}
1841
1842halfword tex_previous_loop_iterator(void)
1843{
1844    halfword delta = tex_scan_integer(0, NULL, NULL);
1845    halfword state = lmt_main_control_state.loop_stack_head;
1846    while (delta-- && state) {
1847        state = node_next(state);
1848    }
1849    return state ? loop_state_count(state) : 0;
1850}
1851
1852static inline halfword tex_previous_loop_iterator_delta(int delta)
1853{
1854    halfword state = lmt_main_control_state.loop_stack_head;
1855    while (delta-- && state) {
1856        state = node_next(state);
1857    }
1858    return state ? loop_state_count(state) : 0;
1859}
1860
1861static inline void tex_update_stack_entry(halfword count)
1862{
1863    loop_state_count(lmt_main_control_state.loop_stack_head) = count;
1864}
1865
1866void tex_begin_local_control(void)
1867{
1868    halfword code = cur_chr;
1869    if (tracing_nesting_par > 2) {
1870        tex_local_control_message("entering token scanner via primitive");
1871    }
1872    switch (code) {
1873        case local_control_list_code:
1874            {
1875                halfword t;
1876                halfword h = tex_scan_toks_normal(0, &t);
1877                halfword r = tex_get_available_token(token_val(end_local_cmd, 0));
1878                tex_begin_inserted_list(r);
1879                tex_begin_token_list(h, local_text);
1880                break;
1881            }
1882        case local_control_token_code:
1883            {
1884                halfword t = tex_get_token(); /* avoid realloc issues */
1885                halfword h = get_reference_token();
1886                halfword r = tex_get_available_token(token_val(end_local_cmd, 0));
1887                tex_store_new_token(h, t);
1888                tex_begin_inserted_list(r);
1889                tex_begin_token_list(h, local_text);
1890                break;
1891            }
1892        /*tex
1893            For the moment al three are here because they share some code. At some point I might
1894            move the last two to the |convert_cmd| which is more natural spot but this is easier
1895            for debugging.
1896
1897            The align_state hack was tricky and took me a while to figure out because it only was
1898            an issue with +10K loops (where 10K is this magic state number).
1899
1900            We support a leading optional equal sign because that can help make robust macros that
1901            get |\the \dimexpr 1pt| etc fed which can lead to \TEX\ seeing one huge number.
1902
1903            The repeat variants are just there for convenience: it saves entering the initial value 
1904            and step. 
1905
1906        */
1907        case local_control_loop_code:
1908        case expanded_loop_code:
1909        case unexpanded_loop_code:
1910        case local_control_repeat_code:
1911        case expanded_repeat_code:
1912        case unexpanded_repeat_code:
1913        case local_control_endless_code:
1914        case expanded_endless_code:
1915        case unexpanded_endless_code:
1916            {
1917                halfword tail;
1918                int looping = code >= local_control_loop_code && code <= unexpanded_loop_code;
1919                int endless = code >= local_control_endless_code && code <= unexpanded_endless_code;
1920                halfword first = looping ? tex_scan_integer(1, NULL, NULL) : 1;
1921                halfword last = endless ? max_integer : tex_scan_integer(1, NULL, NULL);
1922                halfword step = looping ? tex_scan_integer(1, NULL, NULL) : 1;
1923                halfword head = tex_scan_toks_normal(0, &tail);
1924                if (token_link(head) && step) {
1925                    int savedloop = lmt_main_control_state.loop_iterator;
1926                    int savedquit = lmt_main_control_state.quit_loop;
1927                    lmt_main_control_state.loop_iterator = 0;
1928                    lmt_main_control_state.quit_loop = 0;
1929                    ++lmt_main_control_state.loop_nesting;
1930                    tex_push_stack_entry();
1931                    switch (code) {
1932                        case local_control_loop_code:
1933                        case local_control_repeat_code:
1934                        case local_control_endless_code:
1935                            {
1936                                /*tex:
1937                                    Appending to tail gives issues at the outer level, for instance
1938                                    |\dorecurse {3} {\startTEXpage \stopTEXpage}| without |\starttext
1939                                    \stoptext| wrapping. So, no:
1940                                */
1941                                /* tex_store_new_token(tail, token_val(end_local_cmd, 0)); */
1942                              LOCALCONTROL:
1943                                for (halfword i = first; step > 0 ? i <= last : i >= last; i += step) {
1944                                    lmt_main_control_state.loop_iterator = i;
1945                                    tex_update_stack_entry(i);
1946                                    lmt_main_control_state.quit_loop = 0;
1947                                    /*tex But this, so that we get a proper |\end message|: */
1948                                    tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
1949                                    /*tex ... maybe we need to enforce a level > 0 instead. */
1950                                    tex_begin_token_list(head, local_loop_text);
1951                                    tex_local_control(1);
1952                                    /*tex We need to avoid build-up. */
1953                                    tex_cleanup_input_state();
1954                                    if (lmt_main_control_state.quit_loop) {
1955                                        break;
1956                                    } else if (endless && i >= last) {
1957                                        goto LOCALCONTROL;
1958                                    }
1959                                }
1960                                tex_flush_token_list(head);
1961                                break;
1962                            }
1963                        case expanded_loop_code:
1964                        case expanded_repeat_code:
1965                        case expanded_endless_code:
1966                            {
1967                                halfword h = null;
1968                                halfword t = null;
1969                                full_scanner_status saved_full_status = tex_save_full_scanner_status();
1970                                strnumber u = tex_save_cur_string();
1971                                tex_store_new_token(tail, right_brace_token + '}');
1972                              EXPANDED:
1973                                for (halfword i = first; step > 0 ? i <= last : i >= last; i += step) {
1974                                    halfword lt = null;
1975                                    halfword lh = null;
1976                                    ++lmt_input_state.align_state; /* emulates the { for the } above */
1977                                    lmt_main_control_state.loop_iterator = i;
1978                                    tex_update_stack_entry(i);
1979                                    tex_begin_token_list(head, loop_text); /* ref counted */
1980                                    lh = tex_scan_toks_expand(1, &lt, 0, 0);
1981                                    if (token_link(lh)) {
1982                                        if (h) {
1983                                            token_link(t) = token_link(lh);
1984                                        } else {
1985                                            h = token_link(lh);
1986                                        }
1987                                        t = lt;
1988                                    }
1989                                    tex_put_available_token(lh);
1990                                    tex_cleanup_input_state();
1991                                    if (lmt_main_control_state.quit_loop) {
1992                                        break;
1993                                    } else if (endless && i >= last) {
1994                                        goto EXPANDED;
1995                                    }
1996                                }
1997                                tex_unsave_full_scanner_status(saved_full_status);
1998                                tex_restore_cur_string(u);
1999                                tex_flush_token_list(head);
2000                                tex_begin_inserted_list(h);
2001                                break;
2002                            }
2003                        case unexpanded_loop_code:
2004                        case unexpanded_repeat_code:
2005                        case unexpanded_endless_code:
2006                            {
2007                                /*
2008                                    A |\currentloopiterator| will not adapt itself in this kind of
2009                                    loop so we can as well replace it by the current one value which
2010                                    is what we do here. There is some overhead but I can live with
2011                                    that.
2012                                */
2013
2014                                halfword h = token_link(head);
2015                                halfword tt = null;
2016                                halfword t = h;
2017                                halfword b = 0; /* we can count and then break out */
2018                                while (token_link(t)) {
2019                                    t = token_link(t);
2020                                    if (! b && tex_aux_is_iterator_value(token_info(t))) {
2021                                        b = 1;
2022                                    }
2023                                }
2024                                tt = t;
2025                              UNEXPANDED:
2026                                for (halfword i = first + step; step > 0 ? i <= last : i >= last; i += step) {
2027                                    halfword hh = h;
2028                                    while (1) {
2029                                        t = tex_store_new_token(t, token_info(hh));
2030                                        if (b && tex_aux_is_iterator_value(token_info(t))) {
2031                                            halfword v = (i < min_iterator_value) ? min_iterator_value : (i > max_iterator_value ? max_iterator_value : i);
2032                                            token_info(t) = token_val(iterator_value_cmd, v < 0 ? 0x100000 - v : v);
2033                                        }
2034                                        if (hh == tt) {
2035                                            break;
2036                                        } else {
2037                                            hh = token_link(hh);
2038                                        }
2039                                    }
2040                                    if (endless && i >= last) {
2041                                        goto UNEXPANDED;
2042                                    }
2043                                }
2044                                if (b) {
2045                                    halfword hh = h;
2046                                    while (1) {
2047                                        if (tex_aux_is_iterator_value(token_info(hh))) {
2048                                            halfword v = (first < min_iterator_value) ? min_iterator_value : (first > max_iterator_value ? max_iterator_value : first);
2049                                            token_info(hh) = token_val(iterator_value_cmd, v < 0 ? 0x100000 - v : v);
2050                                        }
2051                                        if (hh == tt) {
2052                                            break;
2053                                        } else {
2054                                            hh = token_link(hh);
2055                                        }
2056                                    }
2057                                }
2058                                tex_put_available_token(head);
2059                                tex_begin_inserted_list(h);
2060                                break;
2061                            }
2062                    }
2063                    tex_pop_stack_entry();
2064                    --lmt_main_control_state.loop_nesting;
2065                    lmt_main_control_state.quit_loop = savedquit;
2066                    lmt_main_control_state.loop_iterator = savedloop;
2067                    return;
2068                } else {
2069                    tex_flush_token_list(head);
2070                }
2071                return;
2072            }
2073    }
2074    tex_local_control(1);      /*tex In this case nicer than 0. */
2075 // tex_cleanup_input_state(); /*tex Yes or no? */
2076}
2077
2078void tex_end_local_control(void )
2079{
2080    if (lmt_main_control_state.local_level > 0) {
2081        lmt_main_control_state.local_level -= 1;
2082    } else {
2083        tex_local_control_message("redundant end local control");
2084    }
2085}
2086
2087/*tex
2088
2089    We need to go back to the main loop. This is rather nasty and dirty and counterintuive code and
2090    there might be a cleaner way. Basically we trigger the main control state from here.
2091
2092    \starttyping
2093     0 0       \directlua{token.scan_box()}\hbox{!}
2094    -1 0       \setbox0\hbox{x}\directlua{token.scan_box()}\box0
2095     1 1       \toks0={\directlua{token.scan_box()}\hbox{x}}\directlua{tex.runtoks(0)}
2096     0 0  1 1  \directlua{tex.box[0]=token.scan_box()}\hbox{x\directlua{node.write(token.scan_box())}\hbox{x}}
2097     0 0  0 1  \setbox0\hbox{x}\directlua{tex.box[0]=token.scan_box()}\hbox{x\directlua{node.write(token.scan_box())}\box0}
2098    \stoptyping
2099
2100    It's rather fragile code so we added some tracing options.
2101
2102*/
2103
2104halfword tex_local_scan_box(void)
2105{
2106    int old_mode = cur_list.mode;
2107    int old_level = lmt_main_control_state.local_level;
2108    cur_list.mode = restricted_hmode;
2109    tex_aux_scan_box(lua_scan_flag, 0, null_flag, -1, 0, 0);
2110    if (lmt_main_control_state.local_level == old_level) {
2111        /*tex |\directlua{print(token.scan_list())}\hbox{!}| (n n) */
2112        if (tracing_nesting_par > 2) {
2113            tex_local_control_message("entering at end of box scanning");
2114        }
2115        tex_local_control(1);
2116// tex_cleanup_input_state();
2117    } else {
2118        /*tex |\directlua{print(token.scan_list())}\box0| (n-1 n) */
2119        /*
2120            if (tracing_nesting_par > 2) {
2121                local_control_message("setting level after box scanning");
2122            }
2123        */
2124        lmt_main_control_state.local_level = old_level;
2125    }
2126    cur_list.mode = old_mode;
2127    return cur_box;
2128}
2129
2130/*tex
2131
2132    We have an issue with modes when we quit here because we're coming from and still staying at
2133    the \LUA\ end. So, unless we're already nested, we trigger an end_local_level token (an
2134    extension code).
2135
2136*/
2137
2138static void tex_aux_wrapup_local_scan_box(void)
2139{
2140    /*
2141    if (tracing_nesting_par > 2) {
2142        local_control_message("leaving box scanner");
2143    }
2144    */
2145    lmt_main_control_state.local_level -= 1;
2146}
2147
2148static void tex_aux_run_insert_dollar_sign(void)
2149{
2150    tex_back_input(cur_tok);
2151    cur_tok = dollar_token_m;
2152    tex_handle_error(
2153        insert_error_type,
2154        "Missing $ inserted",
2155        "I've inserted a begin-math/end-math symbol since I think you left one out.\n"
2156        "Proceed, with fingers crossed."
2157    );
2158}
2159
2160/*tex
2161
2162    The |you_cant| procedure prints a line saying that the current command is illegal in the current
2163    mode; it identifies these things symbolically.
2164
2165*/
2166
2167void tex_you_cant_error(const char *helpinfo)
2168{
2169    tex_handle_error(
2170        normal_error_type,
2171        "You can't use '%C' in %M", cur_cmd, cur_chr, cur_list.mode,
2172        helpinfo
2173    );
2174}
2175
2176/*tex
2177
2178    When erroneous situations arise, \TEX\ usually issues an error message specific to the particular
2179    error. For example, |\noalign| should not appear in any mode, since it is recognized by the
2180    |align_peek| routine in all of its legitimate appearances; a special error message is given when
2181    |\noalign| occurs elsewhere. But sometimes the most appropriate error message is simply that the
2182    user is not allowed to do what he or she has attempted. For example, |\moveleft| is allowed only
2183    in vertical mode, and |\lower| only in non-vertical modes.
2184
2185*/
2186
2187static void tex_aux_run_illegal_case(void)
2188{
2189    tex_you_cant_error(
2190        "Sorry, but I'm not programmed to handle this case;\n"
2191        "I'll just pretend that you didn''t ask for it.\n"
2192        "If you're in the wrong mode, you might be able to\n"
2193        "return to the right one by typing 'I}' or 'I$' or 'I\\par'."
2194    );
2195}
2196
2197/*tex
2198
2199    Some operations are allowed only in privileged modes, i.e., in cases that |mode > 0|. The
2200    |privileged| function is used to detect violations of this rule; it issues an error message and
2201    returns |false| if the current |mode| is negative.
2202
2203*/
2204
2205int tex_in_privileged_mode(void)
2206{
2207    if (cur_list.mode > nomode) {
2208        return 1;
2209    } else {
2210        tex_aux_run_illegal_case();
2211        return 0;
2212    }
2213}
2214
2215/*tex
2216
2217    We don't want to leave |main_control| immediately when a |stop| command is sensed, because it
2218    may be necessary to invoke an |\output| routine several times before things really grind to a
2219    halt. (The output routine might even say |\gdef \end {...}|, to prolong the life of the job.)
2220    Therefore |its_all_over| is |true| only when the current page and contribution list are empty,
2221    and when the last output was not a \quote {dead cycle}. We do this when |\end| or |\dump|
2222    occurs. This |stop| is a special case as we want |main_control| to return to its caller if there
2223    is nothing left to do.
2224
2225*/
2226
2227static void tex_aux_run_end_job(void)
2228{
2229    if (tex_in_privileged_mode()) {
2230        if ((page_head == lmt_page_builder_state.page_tail)
2231         && (cur_list.head == cur_list.tail)
2232         && (lmt_page_builder_state.dead_cycles == 0)) {
2233            /*tex This is the only way out. */
2234            lmt_main_control_state.control_state = goto_return_state;
2235        } else {
2236            /*tex 
2237                We will try to end again after ejecting residual material and append |\hbox to \hsize
2238                {}\vfill\penalty-'10000000000|.
2239            */
2240            tex_back_input(cur_tok);
2241            tex_tail_append(tex_new_null_box_node(hlist_node, unknown_list));
2242            box_width(cur_list.tail) = hsize_par;
2243            tex_tail_append(tex_new_glue_node(fi_ll_glue, user_skip_glue)); /* todo: subtype, final_skip_glue? */
2244            tex_tail_append(tex_new_penalty_node(final_penalty, final_penalty_subtype));
2245            tex_build_page(end_page_context, 0);
2246        }
2247    }
2248}
2249
2250/*tex
2251
2252    The |hskip| and |vskip| command codes are used for control sequences like |\hss| and |\vfil| as
2253    well as for |\hskip| and |\vskip|. The difference is in the value of |cur_chr|.
2254
2255    All the work relating to glue creation has been delegated to the following subroutine. It does
2256    not call |build_page|, because it is used in at least one place where that would be a mistake.
2257
2258    The preset glue primitives are an efficiency feature in a traditional engine but becuase we 
2259    don't use shared glue in \LUAMETATEX, there is no real need for them except maybe less tracing
2260    and avoiding lookahead issues. Adding the triple |l| and additional |neg| variants makes no 
2261    sense because these are seldom used. 
2262
2263*/
2264
2265static const int glue_filler_codes[] = { 
2266    fi_l_glue,
2267    fi_ll_glue,
2268    fi_ss_glue,
2269    fi_l_neg_glue,
2270};
2271
2272static void tex_aux_run_glue(void)
2273{
2274    halfword code = cur_chr;
2275    switch (code) {
2276        case fi_l_code:
2277        case fi_ll_code:
2278        case fi_ss_code:
2279        case fi_l_neg_code:
2280            tex_tail_append(tex_new_glue_node(glue_filler_codes[code], user_skip_glue));
2281            break;
2282        case skip_code:
2283            {
2284                halfword v = tex_scan_glue(glue_val_level, 0, 1);
2285                halfword g = tex_new_glue_node(v, user_skip_glue);
2286             /* glue_data(g) = glue_data_par; */
2287                if (cur_mode == mmode) {
2288                   /*tex This could be an option. */
2289                   glue_options(g) |= glue_option_no_auto_break;
2290                }
2291                tex_tail_append(g);
2292                tex_flush_node(v);
2293                break;
2294            }
2295        default:
2296            break;
2297    }
2298}
2299
2300static void tex_aux_run_mglue(void)
2301{
2302    switch (cur_chr) {
2303        case normal_mskip_code:
2304            {
2305                halfword v = tex_scan_glue(muglue_val_level, 0, 0);
2306                tex_tail_append(tex_new_glue_node(v, mu_glue));
2307                tex_flush_node(v);
2308                break;
2309            }
2310        case atom_mskip_code:
2311            {
2312                halfword left = tex_scan_math_class_number(0);
2313                halfword right = tex_scan_math_class_number(0);
2314                halfword style = tex_scan_math_style_identifier(0, 0);
2315                halfword node = tex_math_spacing_glue(left, right, style);
2316                if (node) {
2317                    tex_tail_append(node);
2318                } else { 
2319                    /*tex This could be an option: */
2320                    tex_tail_append(tex_new_glue_node(zero_glue, mu_glue));
2321                }
2322                break;
2323            }
2324    }
2325}
2326
2327/*tex
2328
2329    We have to deal with errors in which braces and such things are not properly nested. Sometimes
2330    the user makes an error of commission by inserting an extra symbol, but sometimes the user makes
2331    an error of omission. \TEX\ can't always tell one from the other, so it makes a guess and tries
2332    to avoid getting into a loop.
2333
2334    The |off_save| routine is called when the current group code is wrong. It tries to insert
2335    something into the user's input that will help clean off the top level.
2336
2337*/
2338
2339void tex_off_save(void)
2340{
2341    if (cur_group == bottom_level_group) {
2342        /*tex Drop current token and complain that it was unmatched */
2343        tex_handle_error(normal_error_type, "Extra %C", cur_cmd, cur_chr,
2344            "Things are pretty mixed up, but I think the worst is over."
2345        );
2346    } else {
2347        const char * helpinfo =
2348            "I've inserted something that you may have forgotten. (See the <inserted text>\n"
2349            "above.) With luck, this will get me unwedged.";
2350        halfword h = tex_get_available_token(null);
2351        tex_back_input(cur_tok);
2352        /*tex
2353            Prepare to insert a token that matches |cur_group|, and print what it is. At this point,
2354            |link (temp_token_head) = p|, a pointer to an empty one-word node.
2355        */
2356        switch (cur_group) {
2357            case also_simple_group:
2358            case semi_simple_group:
2359            case math_simple_group:
2360                {
2361                    set_token_info(h, deep_frozen_end_group_token);
2362                    tex_handle_error(
2363                        normal_error_type,
2364                        "Missing \\endgroup inserted",
2365                        helpinfo
2366                    );
2367                    break;
2368                }
2369            case math_inline_group:
2370            case math_display_group:
2371            case math_number_group:
2372                {
2373                    set_token_info(h, math_shift_token + '$');
2374                    tex_handle_error(
2375                        normal_error_type,
2376                        "Missing $ inserted",
2377                        helpinfo
2378                    );
2379                    break;
2380                }
2381            case math_fence_group:
2382                {
2383                    /*tex
2384                        We feed the right dummy fence back into the input which is cheaper then 
2385                        finishing the fence here. We don't use the hard coded period approach 
2386                        because, even if that is kind of convention and likely configured in 
2387                        a macro package, one can never be sure. That means that using zero as 
2388                        signal is just as good.
2389                    */
2390                
2391                 // halfword q = tex_get_available_token(period_token); /* or char 0 */
2392                    halfword q = tex_get_available_token(token_val(math_char_number_cmd, math_char_ignore_code));
2393                    halfword f = node_next(cur_list.head);
2394                    set_token_info(h, deep_frozen_right_token);
2395                    set_token_link(h, q);
2396                 // tex_add_noad_option(f, noad_option_ignore);
2397                    if (! (f && node_type(f) == fence_noad && has_noad_option_nocheck(f))) {
2398                        tex_handle_error(
2399                            normal_error_type,
2400                            "Missing \\right\\nomathchar inserted",
2401                            helpinfo
2402                        );
2403                    }
2404                    break;
2405                }
2406            default:
2407                {
2408                    set_token_info(h, right_brace_token + '}');
2409                    tex_handle_error(
2410                        normal_error_type,
2411                        "Missing } inserted",
2412                        helpinfo
2413                     );
2414                    break;
2415                }
2416        }
2417        tex_begin_inserted_list(h);
2418    }
2419}
2420
2421/*tex
2422
2423    Discretionary nodes are easy in the common case |\-|, but in the general case we must process
2424    three braces full of items.
2425
2426    The space factor does not change when we append a discretionary node, but it starts out as 1000
2427    in the subsidiary lists.
2428
2429*/
2430
2431/* maybe move this to texlanguage */
2432
2433typedef enum saved_discretionary_entries {
2434    saved_discretionary_component_entry = 0, /* value_1 */
2435    saved_discretionary_n_of_records    = 1,
2436} saved_discretionary_entries;
2437
2438# define saved_discretionary_component saved_value_1(saved_discretionary_component_entry)
2439
2440static inline void saved_discretionary_initialize(void)
2441{
2442    saved_type(0) = saved_record_0;
2443    saved_record(0) = discretionary_save_type;
2444}
2445
2446static inline int saved_discretionary_current_component(void)
2447{
2448    return saved_type(saved_discretionary_component_entry - saved_discretionary_n_of_records) == saved_record_0 
2449        ? saved_value_1(saved_discretionary_component_entry - saved_discretionary_n_of_records) : -1 ;
2450}
2451
2452static inline void saved_discretionary_update_component(void)
2453{
2454    saved_value_1(saved_discretionary_component_entry - saved_discretionary_n_of_records) += 1;
2455}
2456
2457void tex_show_discretionary_group(void)
2458{
2459    tex_print_str_esc("discretionary");
2460    tex_aux_show_group_count(saved_discretionary_component);
2461}
2462
2463int tex_show_discretionary_record(void)
2464{
2465    tex_print_str("discretionary ");
2466    switch (save_type(lmt_save_state.save_stack_data.ptr)) { 
2467       case saved_record_0:
2468            tex_print_format("component %i", saved_discretionary_component);
2469            break;
2470        default: 
2471            return 0;
2472    }
2473    return 1;
2474}
2475
2476static void tex_aux_run_discretionary(void)
2477{
2478    switch (cur_chr) {
2479        case normal_discretionary_code:
2480            /*tex |\discretionary| */
2481            {
2482                halfword d = tex_new_disc_node(normal_discretionary_code);
2483                tex_tail_append(d);
2484                while (1) {
2485                    switch (tex_scan_character("pocbnPOCBN", 0, 1, 0)) {
2486                        case 0:
2487                            goto DONE;
2488                        case 'p': case 'P':
2489                            switch (tex_scan_character("eorEOR", 0, 0, 0)) {
2490                                case 'e': case 'E':
2491                                    if (tex_scan_mandate_keyword("penalty", 2)) {
2492                                        set_disc_penalty(d, tex_scan_integer(0, NULL, NULL));
2493                                    }
2494                                    break;
2495                                case 'o': case 'O':
2496                                    if (tex_scan_mandate_keyword("postword", 2)) {
2497                                        set_disc_option(d, disc_option_post_word);
2498                                    }
2499                                    break;
2500                                case 'r': case 'R':
2501                                    if (tex_scan_mandate_keyword("preword", 2)) {
2502                                        set_disc_option(d, disc_option_pre_word);
2503                                    }
2504                                    break;
2505                                default:
2506                                    tex_aux_show_keyword_error("penalty|postword|preword");
2507                                    goto DONE;
2508                            }
2509                            break;
2510                        case 'b': case 'B':
2511                            if (tex_scan_mandate_keyword("break", 1)) {
2512                                set_disc_option(d, disc_option_prefer_break);
2513                            }
2514                            break;
2515                        case 'n': case 'N':
2516                            if (tex_scan_mandate_keyword("nobreak", 1)) {
2517                                set_disc_option(d, disc_option_prefer_nobreak);
2518                            }
2519                            break;
2520                        case 'o': case 'O':
2521                            if (tex_scan_mandate_keyword("options", 1)) {
2522                                set_disc_options(d, tex_scan_integer(0, NULL, NULL));
2523                            }
2524                            break;
2525                        case 'c': case 'C':
2526                            if (tex_scan_mandate_keyword("class", 1)) {
2527                                set_disc_class(d, tex_scan_math_class_number(0));
2528                            }
2529                            break;
2530                        default:
2531                            goto DONE;
2532                    }
2533                }
2534            DONE:
2535                saved_discretionary_initialize();
2536                saved_discretionary_component = 0;
2537                lmt_save_state.save_stack_data.ptr += saved_discretionary_n_of_records;
2538                tex_new_save_level(discretionary_group);
2539                tex_scan_left_brace();
2540                tex_push_nest();
2541                cur_list.mode = restricted_hmode;
2542                cur_list.space_factor = default_space_factor; /* hm, quite hard coded */
2543            }
2544            break;
2545        case explicit_discretionary_code:
2546            /*tex |\-| */
2547            if (hyphenation_permitted(hyphenation_mode_par, explicit_hyphenation_mode)) {
2548                int c = tex_get_pre_hyphen_char(cur_lang_par);
2549                halfword d = tex_new_disc_node(explicit_discretionary_code);
2550                tex_tail_append(d);
2551                if (c > 0) {
2552                    halfword g = tex_new_char_node(glyph_unset_subtype, cur_font_par, c, 1);
2553                    set_glyph_disccode(g, glyph_disc_explicit);
2554                    tex_set_disc_field(d, pre_break_code, g);
2555                }
2556                c = tex_get_post_hyphen_char(cur_lang_par);
2557                if (c > 0) {
2558                    halfword g = tex_new_char_node(glyph_unset_subtype, cur_font_par, c, 1);
2559                    set_glyph_disccode(g, glyph_disc_explicit);
2560                    tex_set_disc_field(d, post_break_code, g);
2561                }
2562                disc_penalty(d) = tex_explicit_disc_penalty(hyphenation_mode_par);
2563            }
2564            break;
2565        case automatic_discretionary_code:
2566        case mathematics_discretionary_code:
2567            /*tex |-| */
2568            if (hyphenation_permitted(hyphenation_mode_par, automatic_hyphenation_mode)) {
2569                halfword c = tex_get_pre_exhyphen_char(cur_lang_par);
2570                halfword d = tex_new_disc_node(automatic_discretionary_code);
2571                halfword f = cur_chr == mathematics_discretionary_code ? glyph_disc_mathematics : glyph_disc_automatic;
2572                tex_tail_append(d);
2573                /*tex As done in hyphenator: */
2574                if (c <= 0) {
2575                    c = ex_hyphen_char_par;
2576                }
2577                if (c > 0) {
2578                    halfword g = tex_new_char_node(glyph_unset_subtype, cur_font_par, c, 1);
2579                    set_glyph_disccode(g, f);
2580                    tex_set_disc_field(d, pre_break_code, g);
2581                }
2582                c = tex_get_post_exhyphen_char(cur_lang_par);
2583                if (c > 0) {
2584                    halfword g = tex_new_char_node(glyph_unset_subtype, cur_font_par, c, 1);
2585                    set_glyph_disccode(g, f);
2586                    tex_set_disc_field(d, post_break_code, g);
2587                }
2588                c = ex_hyphen_char_par;
2589                if (c > 0) {
2590                    halfword g = tex_new_char_node(glyph_unset_subtype, cur_font_par, c, 1);
2591                    set_glyph_disccode(g, f);
2592                    tex_set_disc_field(d, no_break_code, g);
2593                }
2594                disc_penalty(d) = tex_automatic_disc_penalty(hyphenation_mode_par);
2595            } else {
2596                halfword c = ex_hyphen_char_par;
2597                if (c > 0) {
2598                    halfword g = tex_new_char_node(glyph_unset_subtype, cur_font_par, c, 1);
2599                    set_glyph_discpart(g, glyph_discpart_always);
2600                    set_glyph_disccode(g, glyph_disc_normal);
2601                    tex_tail_append(g);
2602                }
2603            }
2604            break;
2605    }
2606}
2607
2608/*tex
2609
2610    The three discretionary lists are constructed somewhat as if they were hboxes. A subroutine
2611    called |finish_discretionary| handles the transitions. (This is sort of fun.)
2612
2613*/
2614
2615static void tex_aux_finish_discretionary(void)
2616{
2617    halfword current, next;
2618    int length = 0;
2619    tex_unsave();
2620    /*tex
2621        Prune the current list, if necessary, until it contains only |char_node|, |kern_node|,
2622        |hlist_node|, |vlist_node| and |rule_node| items; set |n| to the length of the list, and
2623        set |q| to the lists tail. During this loop, |p = node_next(q)| and there are |n| items
2624        preceding |p|.
2625    */
2626    current = cur_list.head;
2627    next = node_next(current);
2628    while (next) {
2629        switch (node_type(next)) {
2630            case glyph_node:
2631            case hlist_node:
2632            case vlist_node:
2633            case rule_node:
2634            case kern_node:
2635                break;
2636            case glue_node:
2637                if (hyphenation_permitted(hyphenation_mode_par, permit_glue_hyphenation_mode)) {
2638                    if (glue_stretch_order(next)) {
2639                        glue_stretch(next) = 0;
2640                        glue_stretch_order(next) = 0;
2641                    }
2642                    if (glue_shrink_order(next)) {
2643                        glue_shrink(next) = 0;
2644                        glue_shrink_order(next) = 0;
2645                    }
2646                    break;
2647                } else {
2648                    // fall through
2649                }
2650            default:
2651                if (hyphenation_permitted(hyphenation_mode_par, permit_all_hyphenation_mode)) {
2652                    break;
2653                } else {
2654                    tex_handle_error(
2655                        normal_error_type,
2656                        "Improper discretionary list",
2657                        "Discretionary lists must contain only glyphs, boxes, rules and kerns."
2658                    );
2659                    tex_begin_diagnostic();
2660                    tex_print_str("The following discretionary sublist has been deleted:");
2661                    tex_print_levels();
2662                    tex_show_box(next);
2663                    tex_end_diagnostic();
2664                    tex_flush_node_list(next);
2665                    node_next(current) = null;
2666                    goto DONE;
2667                }
2668        }
2669        node_prev(next) = current;
2670        current = next;
2671        next = node_next(current);
2672        ++length;
2673    }
2674  DONE:
2675    next = node_next(cur_list.head);
2676    tex_pop_nest();
2677    {
2678        halfword discnode = cur_list.tail;
2679        switch (saved_discretionary_current_component()) {
2680            case 0:
2681                if (length > 0) {
2682                    tex_set_disc_field(discnode, pre_break_code, next);
2683                }
2684                break;
2685            case 1:
2686                if (length > 0) {
2687                    tex_set_disc_field(discnode, post_break_code, next);
2688                }
2689                break;
2690            case 2:
2691                /*tex
2692                    Attach list |p| to the current list, and record its length; then finish up and
2693                    |return|.
2694                */
2695                if (length > 0) {
2696                    if (cur_mode == mmode && ! hyphenation_permitted(hyphenation_mode_par, permit_math_replace_hyphenation_mode)) {
2697                        tex_handle_error(
2698                            normal_error_type,
2699                            "Illegal math \\discretionary",
2700                            "Sorry: The third part of a discretionary break must be empty, in math formulas. I\n"
2701                            "had to delete your third part."
2702                        );
2703                        tex_flush_node_list(next);
2704                    } else {
2705                        tex_set_disc_field(discnode, no_break_code, next);
2706                    }
2707                }
2708                if (! hyphenation_permitted(hyphenation_mode_par, normal_hyphenation_mode)) {
2709                    halfword replace = disc_no_break_head(discnode);
2710                    cur_list.tail = node_prev(cur_list.tail);
2711                    node_next(cur_list.tail) = null;
2712                    if (replace) {
2713                        tex_tail_append(replace);
2714                        cur_list.tail = disc_no_break_tail(discnode);
2715                        tex_set_disc_field(discnode, no_break_code, null);
2716                        tex_set_discpart(discnode, replace, disc_no_break_tail(discnode), glyph_discpart_replace);
2717                    }
2718                    tex_flush_node(discnode);
2719                } else if (cur_mode == mmode && disc_class(discnode) != unset_disc_class) {
2720                    halfword noad = null;
2721                    cur_list.tail = node_prev(discnode);
2722                    node_prev(discnode ) = null;
2723                    node_next(discnode ) = null;
2724                    noad = tex_math_make_disc(discnode);
2725                    tex_tail_append(noad);
2726                }
2727                /*tex There are no other cases. */
2728                lmt_save_state.save_stack_data.ptr -= saved_discretionary_n_of_records;
2729                return;
2730            default:
2731                tex_confusion("finish discretionary");
2732                return;
2733        }
2734        saved_discretionary_update_component();
2735        tex_new_save_level(discretionary_group);
2736        tex_scan_left_brace();
2737        tex_push_nest();
2738        cur_list.mode = restricted_hmode;
2739        cur_list.space_factor = default_space_factor;
2740    }
2741}
2742
2743/*tex
2744
2745    The routine for a |right_brace| character branches into many subcases, since a variety of things
2746    may happen, depending on |cur_group|. Some types of groups are not supposed to be ended by a
2747    right brace; error messages are given in hopes of pinpointing the problem. Most branches of this
2748    routine will be filled in later, when we are ready to understand them; meanwhile, we must prepare
2749    ourselves to deal with such errors.
2750
2751    When the right brace occurs at the end of an |\hbox| or |\vbox| or |\vtop| construction, the
2752    |package| routine comes into action. We might also have to finish a paragraph that hasn't ended.
2753*/
2754
2755static void tex_aux_extra_right_brace_error(void)
2756{
2757    const char *helpinfo =
2758        "I've deleted a group-closing symbol because it seems to be spurious, as in\n"
2759        "'$x}$'. But perhaps the } is legitimate and you forgot something else, as in\n"
2760        "'\\hbox{$x}'.";
2761    switch (cur_group) {
2762        case also_simple_group:
2763        case semi_simple_group:
2764            tex_handle_error(
2765                normal_error_type,
2766                "Extra }, or forgotten %eendgroup",
2767                helpinfo
2768            );
2769            break;
2770        case math_simple_group:
2771            tex_handle_error(
2772                normal_error_type,
2773                "Extra }, or forgotten %eendmathgroup",
2774                helpinfo
2775            );
2776            break;
2777        case math_inline_group:
2778        case math_display_group:
2779        case math_number_group:
2780            tex_handle_error(
2781                normal_error_type,
2782                "Extra }, or forgotten $",
2783                helpinfo
2784            );
2785            break;
2786        case math_fence_group:
2787            tex_handle_error(
2788                normal_error_type,
2789                "Extra }, or forgotten %eright",
2790                helpinfo
2791            );
2792            break;
2793    }
2794    ++lmt_input_state.align_state;
2795}
2796
2797static inline void tex_aux_finish_hbox(void)
2798{
2799    tex_aux_fixup_directions_only();
2800    tex_package(hbox_code);
2801}
2802
2803static inline void tex_aux_finish_adjusted_hbox(void)
2804{
2805    lmt_packaging_state.post_adjust_tail = post_adjust_head;
2806    lmt_packaging_state.pre_adjust_tail = pre_adjust_head;
2807    lmt_packaging_state.post_migrate_tail = post_migrate_head;
2808    lmt_packaging_state.pre_migrate_tail = pre_migrate_head;
2809    tex_package(hbox_code);
2810}
2811
2812static inline void tex_aux_finish_vbox(void)
2813{
2814
2815    if (! tex_wrapped_up_paragraph(vbox_par_context, 1)) {
2816        tex_end_paragraph(vbox_group, vbox_par_context);
2817        tex_package(vbox_code);
2818    }
2819}
2820
2821static inline void tex_aux_finish_vtop(void)
2822{
2823    if (! tex_wrapped_up_paragraph(vtop_par_context, 1)) {
2824        tex_end_paragraph(vtop_group, vtop_par_context);
2825        tex_package(vtop_code);
2826    }
2827}
2828
2829static inline void tex_aux_finish_dbox(void)
2830{
2831    if (! tex_wrapped_up_paragraph(dbox_par_context, 1)) {
2832        tex_end_paragraph(dbox_group, dbox_par_context);
2833        tex_package(dbox_code);
2834    }
2835}
2836
2837static inline void tex_aux_finish_simple_group(void)
2838{
2839    tex_aux_fixup_directions_and_unsave();
2840}
2841
2842static void tex_aux_finish_bottom_level_group(void)
2843{
2844    tex_handle_error(
2845        normal_error_type,
2846        "Too many }'s",
2847        "You've closed more groups than you opened. Such booboos are generally harmless,\n"
2848        "so keep going."
2849    );
2850}
2851
2852static inline void tex_aux_finish_output(void)
2853{
2854    tex_pop_text_dir_ptr();
2855    tex_resume_after_output();
2856}
2857
2858static void tex_aux_run_right_brace(void)
2859{
2860    switch (cur_group) {
2861        case bottom_level_group:
2862            tex_aux_finish_bottom_level_group();
2863            break;
2864        case simple_group:
2865            tex_aux_finish_simple_group();
2866            break;
2867        case hbox_group:
2868            tex_aux_finish_hbox();
2869            break;
2870        case adjusted_hbox_group:
2871            tex_aux_finish_adjusted_hbox();
2872            break;
2873        case vbox_group:
2874            tex_aux_finish_vbox();
2875            break;
2876        case vtop_group:
2877            tex_aux_finish_vtop();
2878            break;
2879        case dbox_group:
2880            tex_aux_finish_dbox();
2881            break;
2882        case align_group:
2883            tex_finish_alignment_group();
2884            break;
2885        case no_align_group:
2886            tex_finish_no_alignment_group();
2887            break;
2888        case output_group:
2889            tex_aux_finish_output();
2890            break;
2891        case math_group:
2892        case math_component_group:
2893        case math_stack_group:
2894            tex_finish_math_group();
2895            break;
2896        case discretionary_group:
2897            tex_aux_finish_discretionary();
2898            break;
2899        case insert_group:
2900            tex_finish_insert_group();
2901            break;
2902        case vadjust_group:
2903            tex_finish_vadjust_group();
2904            break;
2905        case vcenter_group:
2906            tex_finish_vcenter_group();
2907            break;
2908        case math_fraction_group:
2909            tex_finish_math_fraction();
2910            break;
2911        case math_radical_group:
2912            tex_finish_math_radical();
2913            break;
2914        case math_operator_group:
2915            tex_finish_math_operator();
2916            break;
2917        case math_choice_group:
2918            tex_finish_math_choice();
2919            break;
2920        case also_simple_group:
2921        case math_simple_group:
2922         // cur_group = semi_simple_group; /* probably not needed */
2923            tex_aux_run_end_group();
2924            break;
2925        case semi_simple_group:
2926        case math_inline_group:
2927        case math_display_group:
2928        case math_number_group:
2929        case math_fence_group: /*tex See above, let's see when we are supposed to end up here. */
2930            tex_aux_extra_right_brace_error();
2931            break;
2932        case local_box_group:
2933            tex_aux_finish_local_box();
2934            break;
2935        default:
2936            tex_confusion("right brace");
2937            break;
2938    }
2939}
2940
2941/*tex
2942
2943    Here is where we clear the parameters that are supposed to revert to their default values after
2944    every paragraph and when internal vertical mode is entered.
2945
2946    The multiple widow penalties work from the bottom and club penalties work from the top. Both are 
2947    persistent arrays while interline penalties acts like for instance looseness and is a more 
2948    temporary feature. One can argue about how much sense all this makes because it not only affects
2949    the next paragraph but nested ones too. 
2950
2951    One argument for the reset i.e. only set inside a paragraph is that  it then can't influence 
2952    a nested one. In that case resetting the widow and club penalties also makes sense but on the 
2953    other hand one often wants these to operate over more paragraphs. One solution is to make these 
2954    parameters current list specific, that is: wipe them as soon as they are bound to the par node, 
2955    although we can emulate that: frozen set and unfrozen reset. 
2956
2957*/
2958
2959void tex_normal_paragraph(int context)
2960{
2961    int ignore = 0;
2962    lmt_main_control_state.last_par_context = context;
2963    lmt_paragraph_context_callback(context, &ignore);
2964    if (! ignore) {
2965        /*tex Why only this subset, maybe we have to check some more now. */
2966        if (looseness_par) {
2967            update_tex_looseness(0);
2968        }
2969        if (hang_indent_par) {
2970            update_tex_hang_indent(0);
2971        }
2972        if (hang_after_par != 1) {
2973            update_tex_hang_after(1);
2974        }
2975        if (par_shape_par) {
2976            update_tex_par_shape(null);
2977        }
2978        if (inter_line_penalties_par && ! normalize_par_mode_option(keep_interline_penalties_mode)) { 
2979            update_tex_inter_line_penalties(null);
2980        }
2981        if (emergency_left_skip_par) {
2982            update_tex_emergency_left_skip(null);
2983        }
2984        if (emergency_right_skip_par) {
2985            update_tex_emergency_right_skip(null);
2986        }
2987        if (local_interline_penalty_par) {
2988            update_tex_local_interline_penalty(0);
2989        }
2990        if (local_broken_penalty_par) {
2991            update_tex_local_broken_penalty(0);
2992        }
2993        if (local_tolerance_par) {
2994            update_tex_local_tolerance(0);
2995        }
2996        if (local_pre_tolerance_par) {
2997            update_tex_local_pre_tolerance(0);
2998        }
2999        if (single_line_penalty_par) {
3000            update_tex_single_line_penalty(0);
3001        }
3002        if (par_passes_exception_par) {
3003            update_tex_par_passes_exception(null);
3004        }
3005    }
3006}
3007
3008/*tex
3009
3010    The global variable |cur_box| will point to a newly-made box. If the box is void, we will have
3011    |cur_box = null|. Otherwise we will have |type(cur_box) = hlist_node| or |vlist_node| or
3012    |rule_node|; the |rule_node| case can occur only with leaders.
3013
3014    The |box_end| procedure does the right thing with |boxnode|, if |boxcontext| represents the
3015    context as explained above. The |boxnode| variable is either a list node or a register index.
3016    In some cases we communicate via a state variable.
3017
3018*/
3019
3020static void tex_aux_wrapup_leader_box(halfword boxcontext, halfword boxnode, halfword callback, halfword leaders)
3021{
3022    /*tex Append a new leader node that uses |box| and get the next non-blank non-relax. */
3023    do {
3024        tex_get_x_token();
3025    } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd);
3026    if ((cur_cmd == hskip_cmd && cur_mode != vmode) || (cur_cmd == vskip_cmd && cur_mode == vmode)) {
3027        tex_aux_run_glue(); /* uses cur_chr */
3028        switch (boxcontext) {
3029            case a_leaders_flag:
3030                node_subtype(cur_list.tail) = a_leaders;
3031                break;
3032            case c_leaders_flag:
3033                node_subtype(cur_list.tail) = c_leaders;
3034                break;
3035            case x_leaders_flag:
3036                node_subtype(cur_list.tail) = x_leaders;
3037                break;
3038            case g_leaders_flag:
3039                node_subtype(cur_list.tail) = g_leaders;
3040                break;
3041            case u_leaders_flag:
3042                switch (node_type(boxnode)) {
3043                    case hlist_node:
3044                        if (cur_mode != vmode) {
3045                            node_subtype(cur_list.tail) = u_leaders;
3046                            glue_amount(cur_list.tail) += box_width(boxnode);
3047                            glue_callback(cur_list.tail) = callback;
3048                            glue_options(cur_list.tail) = leaders;
3049                        } else {
3050                            node_subtype(cur_list.tail) = a_leaders;
3051                        }
3052                        break;
3053                    case vlist_node:
3054                        if (cur_mode == vmode) {
3055                            node_subtype(cur_list.tail) = u_leaders;
3056                            glue_amount(cur_list.tail) += box_total(boxnode);
3057                            glue_callback(cur_list.tail) = callback;
3058                            glue_options(cur_list.tail) = leaders;
3059                        } else {
3060                            node_subtype(cur_list.tail) = a_leaders;
3061                        }
3062                        break;
3063                    default:
3064                        /* yet unsupported */
3065                        node_subtype(cur_list.tail) = a_leaders;
3066                        break;
3067                }
3068                break;
3069        }
3070        glue_leader_ptr(cur_list.tail) = boxnode;
3071    } else {
3072        tex_handle_error(
3073            back_error_type,
3074            "Leaders not followed by proper glue",
3075            "You should say '\\leaders <box or rule><hskip or vskip>'. I found the <box or\n"
3076            "rule>, but there's no suitable <hskip or vskip>, so I'm ignoring these leaders."
3077        );
3078        tex_flush_node_list(boxnode);
3079    }
3080}
3081
3082void tex_box_end(int boxcontext, halfword boxnode, scaled shift, halfword mainclass, halfword slot, halfword callback, halfword leaders)
3083{
3084    cur_box = boxnode;
3085    switch (boxcontext) {
3086        case direct_box_flag:
3087            /*tex
3088
3089                Append box |boxnode| to the current list, shifted by |boxcontext|. The global variable
3090                |adjust_tail| will be non-null if and only if the current box might include adjustments
3091                that should be appended to the current vertical list.
3092
3093                Having shift in the box context is kind of strange but as long as we stay below maxdimen
3094                it works. We now pass the shift directly, so no boxcontext trick here.
3095
3096            */
3097            if (boxnode) {
3098                if (shift != null_flag) {
3099                    box_shift_amount(boxnode) = shift;
3100                }
3101                switch (cur_mode) {
3102                    case vmode:
3103                        if (lmt_packaging_state.pre_adjust_tail) {
3104                            if (pre_adjust_head != lmt_packaging_state.pre_adjust_tail) {
3105                                tex_inject_adjust_list(pre_adjust_head, 1, boxnode, NULL);
3106                            }
3107                            lmt_packaging_state.pre_adjust_tail = null;
3108                        }
3109                        if (lmt_packaging_state.pre_migrate_tail) {
3110                            if (pre_migrate_head != lmt_packaging_state.pre_migrate_tail) {
3111                                tex_append_list(pre_migrate_head, lmt_packaging_state.pre_migrate_tail);
3112                            }
3113                            lmt_packaging_state.pre_migrate_tail = null;
3114                        }
3115                        tex_append_to_vlist(boxnode, lua_key_index(box), NULL);
3116                        if (lmt_packaging_state.post_migrate_tail) {
3117                            if (post_migrate_head != lmt_packaging_state.post_migrate_tail) {
3118                                tex_append_list(post_migrate_head, lmt_packaging_state.post_migrate_tail);
3119                            }
3120                            lmt_packaging_state.post_migrate_tail = null;
3121                        }
3122                        if (lmt_packaging_state.post_adjust_tail) {
3123                            if (post_adjust_head != lmt_packaging_state.post_adjust_tail) {
3124                                tex_inject_adjust_list(post_adjust_head, 1, null, NULL);
3125                            }
3126                            lmt_packaging_state.post_adjust_tail = null;
3127                        }
3128                        /* what to do here */
3129                        lmt_packaging_state.except = 0;
3130                        if (cur_list.mode > nomode) {
3131                            tex_build_page(box_page_context, 0);
3132                        }
3133                        break;
3134                    case hmode:
3135                        cur_list.space_factor = default_space_factor;
3136                        tex_couple_nodes(cur_list.tail, boxnode);
3137                        cur_list.tail = boxnode;
3138                        break;
3139                    /* case mmode: */
3140                    default:
3141                        boxnode = tex_new_sub_box(boxnode);
3142                        tex_couple_nodes(cur_list.tail, boxnode);
3143                        cur_list.tail = boxnode;
3144                        if (mainclass != unset_noad_class) {
3145                            tex_set_noad_classes(boxnode, mainclass);
3146                        }
3147                        break;
3148                }
3149            } else {
3150                /* just scanning */
3151            }
3152            break;
3153        case box_flag:
3154            /*tex Store |box| in a local box register  */
3155            update_tex_box_local(slot, boxnode);
3156            break;
3157        case global_box_flag:
3158            /*tex Store |box| in a global box register  */
3159            update_tex_box_global(slot, boxnode);
3160            break;
3161        case shipout_flag:
3162            /*tex This normally can't happen as some backend code needs to kick in. */
3163            if (boxnode) {
3164                /*tex We just show the box ... */
3165                tex_begin_diagnostic();
3166                tex_show_node_list(boxnode, max_integer, max_integer);
3167                tex_end_diagnostic();
3168                /*tex ... and wipe it when it's a register ... */
3169                if (box_register(boxnode)) {
3170                    tex_flush_node_list(boxnode);
3171                    box_register(boxnode) = null;
3172                }
3173                /*tex ... so there is at least an indication that we flushed. */
3174            }
3175            break;
3176        case left_box_flag:
3177        case right_box_flag:
3178        case middle_box_flag:
3179            /*tex Actualy, this cannot happen ... will go away. */
3180            tex_aux_finish_local_box();
3181            break;
3182        case lua_scan_flag:
3183            /*tex We are done with scanning so let's return to the caller. */
3184            tex_aux_wrapup_local_scan_box();
3185            cur_box = boxnode;
3186            break;
3187        case a_leaders_flag:
3188        case c_leaders_flag:
3189        case x_leaders_flag:
3190        case g_leaders_flag:
3191        case u_leaders_flag:
3192            tex_aux_wrapup_leader_box(boxcontext, boxnode, callback, leaders);
3193            break;
3194        default:
3195            /* fatal error */
3196            break;
3197    }
3198}
3199
3200/*tex
3201
3202    The canonical \TEX\ engine(s) inject an indentation box, so there is always something at the beginning that
3203    also acts as a boundary. However, when snapshotting was introduced it made also sense to turn the parindent
3204    related hlist into a glue. We might need to adapt the parbuilder but it looks liek that is not needed. Of
3205    course, an |\unskip| will now also unskip the parindent but there are ways to prevent this. I'll test it for
3206    a while, which is why we have a way to enable it. The glue is {\em always} injected, also when it's zero.
3207
3208*/
3209
3210void tex_begin_paragraph(int doindent, int context)
3211{
3212    int indented = doindent;
3213    int isvmode = cur_list.mode == vmode;
3214    lmt_main_control_state.last_par_trigger = context; /* added */
3215    if (isvmode || cur_list.head != cur_list.tail) {
3216        /*tex
3217            Actually we could remove the callback and hook it into the |\everybeforepar| but that one
3218            started out as a |tex.expandmacro| itself and we don't want the callback overhead every
3219            time, so now we have both. However, in the end I decided to do this one {\em before} the
3220            parskip is injected.
3221        */
3222        if (every_before_par_par) {
3223            tex_begin_inserted_list(tex_get_available_token(token_val(end_local_cmd, 0)));
3224            tex_begin_token_list(every_before_par_par, every_before_par_text);
3225            if (tracing_nesting_par > 2) {
3226                tex_local_control_message("entering local control via \\everybeforepar");
3227            }
3228            tex_local_control(1);
3229// tex_cleanup_input_state();
3230        }
3231        tex_tail_append(tex_new_param_glue_node(par_skip_code, par_skip_glue));
3232    }
3233    lmt_begin_paragraph_callback(isvmode, &indented, context);
3234    /*tex We'd better not messed up things in the callback! */
3235    { 
3236        halfword prev_graf = cur_list.prev_graf;
3237        cur_list.prev_graf = 0;
3238        tex_push_nest();
3239        cur_list.mode = hmode;
3240        cur_list.space_factor = default_space_factor;
3241        /*tex Add local paragraph node */
3242        tex_tail_append(tex_new_par_node(vmode_par_par_subtype));
3243        par_prev_graf(cur_list.tail) = prev_graf;
3244        /*tex Dir nodes end up before the indent box. */
3245        tex_append_dir_state();
3246    }
3247    tex_aux_insert_parindent(indented);
3248    if (tracing_paragraph_lists) {
3249        tex_begin_diagnostic();       
3250     // tex_print_format("[paragraph: start, context %i]", context);
3251        tex_print_format("[paragraph: start, context %s]", lmt_interface.par_trigger_values[context].name);
3252        tex_show_box(node_next(cur_list.head));
3253        tex_end_diagnostic();
3254    }
3255    /*tex The |\everypar| tokens are injected after all these nodes have been added. */
3256    if (every_par_par) {
3257        tex_begin_token_list(every_par_par, every_par_text);
3258    }
3259    if (lmt_nest_state.nest_data.ptr == 1) {
3260        /*tex put |par_skip| glue on current page */
3261        tex_build_page(begin_paragraph_page_context, 0);
3262    }
3263}
3264
3265void tex_insert_paragraph_token(void)
3266{
3267    if (auto_paragraph_mode_par > 0) {
3268        cur_tok = token_val(end_paragraph_cmd, inserted_end_paragraph_code);
3269     // cur_tok = token_val(end_paragraph_cmd, normal_end_paragraph_code);
3270     // cur_cs  = null;
3271    } else {
3272        cur_tok = lmt_token_state.par_token;
3273    }
3274}
3275
3276static void tex_aux_run_head_for_vmode(void)
3277{
3278    if (cur_list.mode >= nomode) {
3279        tex_back_input(cur_tok);
3280        /*tex
3281            We could have a callback here but on the other hand, we really need to be in vmode
3282            afterwards! Also, a macro package can just test for the mode at that spot which is
3283            less hassle than making a callback identify what is needed. A return value would
3284            indicate to not inject a par when we're in vmode and only very dirty \LUA\ code can
3285            change modes here by messing with the list so far. So, unless I find a real use case
3286            we just continue.
3287        */
3288        tex_insert_paragraph_token();
3289        /* 
3290            An old cheat: we need to allocate a token which is what back_input does, but then we 
3291            mark it as inserted. 
3292        */
3293        tex_back_input(cur_tok);
3294        lmt_input_state.cur_input.token_type = inserted_text;
3295        /* 
3296            tex_insert_input(tex_get_available_token(cur_tok)); // cleaner 
3297        */
3298    } else if (cur_cmd != hrule_cmd) {
3299        tex_off_save();
3300    } else {
3301        tex_handle_error(
3302            normal_error_type,
3303            "You can't use '\\hrule' here except with leaders",
3304            "To put a horizontal rule in an hbox or an alignment, you should use \\leaders or\n"
3305            "\\hrulefill (see The TeXbook)."
3306        );
3307    }
3308}
3309
3310/*tex
3311
3312    We don't have |hkern_cmd| and |vkern_cmd| and it makes no sense to introduce them now so instead
3313    of handling modes in the big switch we do it here. Because we need to be compatible we would end
3314    up with three |cmd| codes anyway. The rationale for |\hkern| and |\vkern| is consistency of
3315    primitives, while |\nonzerowidthkern| can make node lists smaller which is nice for \LUA\ based
3316    juggling.
3317
3318*/
3319
3320/*
3321static void tex_aux_run_kern(void)
3322{
3323    halfword val = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3324    tex_tail_append(tex_new_kern_node(val, explicit_kern));
3325}
3326*/
3327
3328static void tex_aux_run_kern(void)
3329{
3330    halfword code = cur_chr;
3331    switch (code) {
3332        /*tex Finally enabled: */
3333        case h_kern_code:
3334            if (cur_mode == vmode) {
3335                tex_back_input(token_val(kern_cmd, normal_kern_code));
3336                tex_back_input(token_val(begin_paragraph_cmd, quitvmode_par_code));
3337                return;
3338            } else { 
3339                break;
3340            }
3341        case v_kern_code:
3342            if (cur_mode == hmode) {
3343                tex_back_input(token_val(kern_cmd, normal_kern_code));
3344                tex_back_input(token_val(end_paragraph_cmd, normal_end_paragraph_code));
3345                return;
3346            } else { 
3347                break;
3348            }
3349    }
3350    { 
3351        scaled val = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3352        if (code == non_zero_width_kern_code && ! val) { 
3353            return;
3354        } else { 
3355            tex_tail_append(tex_new_kern_node(val, explicit_kern_subtype));
3356        }
3357    }
3358}
3359
3360static void tex_aux_run_mkern(void)
3361{
3362    halfword val = tex_scan_dimension(1, 0, 0, 0, NULL, NULL);
3363    tex_tail_append(tex_new_kern_node(val, explicit_math_kern_subtype));
3364}
3365
3366/*tex
3367
3368    |cur_list.dirs| would have been set by |line_break| by means of |post_line_break|, but this is
3369    not done right now, as it introduces pretty heavy memory leaks. This means the current code
3370    might be wrong in some way that relates to in-paragraph displays.
3371
3372    Instead of |tex_aux_only_dirs| we now uyse a configurable |tex_is_effectively_empty| so that we
3373    can test cq .\ document this trickery. It also gives bit more control over hoe to deal with 
3374    indentation (old school \TEX). 
3375
3376*/
3377
3378void tex_end_paragraph(int group, int context)
3379{
3380    if (cur_list.mode == hmode) {
3381        if (cur_list.head == cur_list.tail) {
3382            /*tex |null| paragraphs are ignored, all contain a |par| node */
3383            tex_pop_nest();
3384        } else if (empty_paragraph_mode_par && tex_is_effectively_empty(node_next(cur_list.head), empty_paragraph_mode_par)) {
3385            tex_flush_node_list(node_next(cur_list.head));
3386         /* cur_list.tail = cur_list.head; */
3387            tex_pop_nest();
3388         // if (cur_list.head == cur_list.tail || node_next(cur_list.head) == cur_list.tail) {
3389         //     if (node_next(cur_list.head) == cur_list.tail) {
3390         //         tex_flush_node(node_next(cur_list.head));
3391         //      // cur_list.tail = cur_list.head;
3392         //     }
3393         //     tex_pop_nest();
3394        } else {
3395            halfword tail = cur_list.tail;
3396            while (tail != cur_list.head && node_type(tail) == par_node && node_subtype(tail) == local_break_par_subtype) { 
3397                cur_list.tail = node_prev(tail);
3398                node_next(cur_list.tail) = null;
3399                tex_flush_node(tail);
3400                tail = cur_list.tail;
3401            }
3402            tex_line_break(group, context, 0);
3403        }
3404        if (cur_list.direction_stack) {
3405            tex_flush_node_list(cur_list.direction_stack);
3406            cur_list.direction_stack = null;
3407        }
3408        tex_normal_paragraph(context);
3409        lmt_error_state.error_count = 0;
3410    }
3411}
3412
3413static void tex_aux_run_penalty(void)
3414{
3415    halfword code = cur_chr;
3416    switch (code) {
3417        /*tex Finally enabled: */
3418        case h_penalty_code:
3419            if (cur_mode == vmode) {
3420                tex_back_input(token_val(penalty_cmd, normal_penalty_code));
3421                tex_back_input(token_val(begin_paragraph_cmd, quitvmode_par_code));
3422                return;
3423            } else { 
3424                break;
3425            }
3426        case v_penalty_code:
3427            if (cur_mode == hmode) {
3428                tex_back_input(token_val(penalty_cmd, normal_penalty_code));
3429                tex_back_input(token_val(end_paragraph_cmd, normal_end_paragraph_code));
3430                return;
3431            } else { 
3432                break;
3433            }
3434    }
3435    {
3436        halfword value = tex_scan_integer(0, NULL, NULL);
3437        tex_tail_append(tex_new_penalty_node(value, user_penalty_subtype));
3438        if (cur_list.mode == vmode) {
3439            tex_build_page(penalty_page_context, 0);
3440        }
3441    }
3442}
3443
3444/*tex
3445
3446    When |delete_last| is called, |cur_chr| is the |type| of node that will be deleted, if present.
3447    The |remove_item| command removes a penalty, kern, or glue node if it appears at the tail of
3448    the current list, using a brute-force linear scan. Like |\lastbox|, this command is not allowed
3449    in vertical mode (except internal vertical mode), since the current list in vertical mode is
3450    sent to the page builder. But if we happen to be able to implement it in vertical mode, we do.
3451
3452*/
3453
3454static void tex_aux_run_remove_item(void)
3455{
3456    halfword code = cur_chr;
3457    halfword head = cur_list.head;
3458    halfword tail = cur_list.tail;
3459    if (cur_list.mode == vmode && tail == head) {
3460        /*tex
3461            Apologize for inability to do the operation now, unless |\unskip|
3462            follows non-glue. It's a bit weird test.
3463        */
3464        if ((code != skip_item_code) || (lmt_page_builder_state.last_glue != max_halfword)) {
3465            switch (code) {
3466                case kern_item_code:
3467                    tex_you_cant_error(
3468                        "Sorry...I usually can't take things from the current page.\n"
3469                        "Try '\\kern-\\lastkern' instead."
3470                    );
3471                    break;
3472                case penalty_item_code:
3473                case boundary_item_code:
3474                    tex_you_cant_error(
3475                        "Sorry...I usually can't take things from the current page.\n"
3476                        "Perhaps you can make the output routine do it."
3477                    );
3478                    break;
3479                case skip_item_code:
3480                    tex_you_cant_error(
3481                        "Sorry...I usually can't take things from the current page.\n"
3482                        "Try '\\vskip-\\lastskip' instead."
3483                    );
3484                    break;
3485            }
3486        }
3487//    } else if (node_type(tail) != glyph_node) {
3488//        /*tex
3489//            Officially we don't need to check what we remove because it can be only one of
3490//            three, unless one creates one indendently (in \LUA). So, we just do check and
3491//            silently ignore bad code.
3492//        */
3493//        halfword p;
3494//        switch (code) {
3495//            case kern_item_code    : if (node_type(tail) != kern_node   ) { return; } else { break; }
3496//            case penalty_item_code : if (node_type(tail) != penalty_node) { return; } else { break; }
3497//            case skip_item_code    : if (node_type(tail) != glue_node   ) { return; } else { break; }
3498//        }
3499//        /*tex
3500//            There is some magic testing here that makes sure we don't mess up any discretionary
3501//            nodes. But why do we care?
3502//        */
3503//        do {
3504//            p = head;
3505//            if (p == tail && node_type(head) == disc_node) {
3506//                return;
3507//            } else {
3508//                head = node_next(p);
3509//            }
3510//        } while (head != tail);
3511//        node_next(p) = null;
3512//        tex_flush_node_list(tail);
3513//        cur_list.tail = p;
3514//    }
3515    } else {
3516//  } else if (tail != head) {
3517        /*tex
3518            Officially we don't need to check what we remove because it can be only one of
3519            three, unless one creates one indendently (in \LUA). So, we just do check and
3520            silently ignore bad code.
3521        */
3522        switch (node_type(tail)) {
3523            case kern_node     :
3524                if (code == kern_item_code) {
3525                    break;
3526                } else {
3527                    return;
3528                }
3529            case penalty_node  :
3530                if (code == penalty_item_code) {
3531                    break;
3532                } else {
3533                    return;
3534                }
3535            case glue_node     :
3536                if (code == skip_item_code) {
3537                    break;
3538                } else {
3539                    return;
3540                }
3541            case boundary_node :
3542                if (code == boundary_item_code && node_subtype(tail) == user_boundary) {
3543                    break;
3544                } else {
3545                    return;
3546                }
3547            default:
3548                return;
3549        }
3550        tex_flush_node(tail);
3551        tex_pop_tail();
3552//        {
3553//            /*tex
3554//                There is some magic testing here that makes sure we don't mess up any discretionary
3555//                nodes. But why do we care?
3556//            */
3557//            halfword p;
3558//            do {
3559//                p = head;
3560//                if (p == tail && node_type(head) == disc_node) {
3561//                    return;
3562//                } else {
3563//                    head = node_next(p);
3564//                }
3565//            } while (head != tail);
3566//            node_next(p) = null;
3567//            tex_flush_node_list(tail);
3568//            cur_list.tail = p;
3569//        }
3570    }
3571}
3572
3573/*tex
3574
3575    Italic corrections are converted to kern nodes when the |italic_correction| command follows a
3576    character. In math mode the same effect is achieved by appending a kern of zero here, since
3577    italic corrections are supplied later.
3578
3579    The callback can take care of |\glyphslant| usage because it is up to the macro package to come 
3580    up with a decent heuristic. 
3581
3582*/
3583
3584static void tex_aux_run_text_italic_correction(void)
3585{
3586    halfword tail = cur_list.tail;
3587    scaled kern = 0;
3588    quarterword subtype = 0;
3589    switch (cur_chr) { 
3590        case italic_correction_code: 
3591            if (tail == cur_list.head) { 
3592                return; 
3593            } else {
3594                subtype = italic_kern_subtype;
3595                break;
3596            }
3597        case left_correction_code:
3598            subtype = left_correction_kern_subtype;
3599            break;
3600        case right_correction_code:
3601            subtype = right_correction_kern_subtype;
3602            break;
3603        default: 
3604            return;
3605    }
3606    if (subtype == italic_kern_subtype) {
3607        switch (node_type(tail)) { 
3608            case glyph_node: 
3609                kern = tex_char_italic_from_glyph(tail); /* scaled */
3610                if (cur_mode == mmode) { 
3611                    /*tex 
3612                        Here we are just compatible and in \CONTEXT\ we don't have italic 
3613                        corrections in math anyway. 
3614                    */
3615                    break;
3616                } else if (tex_has_glyph_option(tail, glyph_option_no_italic_correction)) { /* && cur_chr != right_correction_code */
3617                    break;
3618                } else {
3619                    /*tex
3620                        We pass the identified value but in the case of \OPENTYPE\ fonts or slant
3621                        control we have to cook up some value ourselves (if at all). 
3622                    */
3623                    kern = lmt_italic_correction_callback(tail, kern, subtype);
3624                    if (kern || ! tex_has_glyph_option(tail, glyph_option_no_zero_italic_correction)) { 
3625                        break;
3626                    } else { 
3627                        return;
3628                    }
3629                }
3630            case disc_node: 
3631                /*tex 
3632                    Just in case we want this but here checking has to be done in the callback 
3633                    unless we check replace and post here. 
3634                */
3635                kern = lmt_italic_correction_callback(tail, 0, subtype);
3636                if (kern) { 
3637                    break;
3638                } else { 
3639                    return;
3640                }
3641            default: 
3642                return; 
3643        }
3644    }
3645    /*tex 
3646        When we end up here the decision has been made to inject a kern, it being zero or 
3647        otherwise. What to do with multiple corrections. Best not mess with heuristics here. 
3648    */
3649    tex_tail_append(tex_new_kern_node(kern, subtype));
3650    /*tex 
3651        We might want to add some properties to the kern node or maybe register it as a todo. The 
3652        overhead of a callback can be neglected because we don't have them many of them. We could 
3653        assign a returned value but ... in the end these correction kerns are just signals. 
3654    */
3655    if (subtype != italic_kern_subtype) {
3656        lmt_italic_correction_callback(cur_list.tail, kern, subtype);
3657    }
3658}
3659
3660/*tex
3661
3662    The positioning of accents is straightforward but tedious. Given an accent of width |a|,
3663    designed for characters of height |x| and slant |s|; and given a character of width |w|,
3664    height |h|, and slant |t|: We will shift the accent down by |x - h|, and we will insert kern
3665    nodes that have the effect of centering the accent over the character and shifting the accent
3666    to the right by $\delta = {1 \over 2} (w-a) + h \cdot t - x \cdot s$. If either character is
3667    absent from the font, we will simply use the other, without shifting.
3668
3669    While much is delegated to builders this is one of the few places where the action happens
3670    directly. Of course, in a \UNICODE\ engine this command is not really relevant but here we
3671    even extended it with optional offsets!
3672
3673*/
3674
3675static void tex_aux_run_text_accent(void)
3676{
3677    halfword fnt = cur_font_par;
3678    halfword accent = null;
3679    halfword base = null;
3680    scaled xoffset = 0;
3681    scaled yoffset = 0;
3682    while (1) {
3683        switch (tex_scan_character("xyXY", 0, 1, 0)) {
3684            case 'x': case 'X':
3685                if (tex_scan_mandate_keyword("xoffset", 1)) {
3686                    xoffset = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3687                }
3688                break;
3689            case 'y': case 'Y':
3690                if (tex_scan_mandate_keyword("yoffset", 1)) {
3691                    yoffset = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
3692                }
3693                break;
3694            default:
3695                goto DONE;
3696        }
3697    }
3698  DONE:
3699    accent = tex_new_char_node(glyph_unset_subtype, fnt, tex_scan_char_number(0), 1);
3700    if (accent) {
3701        /*tex
3702            Create a character node |q| for the next character, but set |q := null| if problems
3703            arise.
3704        */
3705        scaled x = tex_get_scaled_ex_height(fnt);
3706        double s = (double) (tex_get_font_slant(fnt)) / (double) (65536);
3707        scaled a = tex_glyph_width(accent);
3708        /*tex
3709            Here we had |handle_assignments| which is a bit confusing one so we inlined it, probably
3710            at the cost of some error recovery compatibility, which we don't worry too much about.
3711            It looks like skipping spaces and relax is okay. The (original \TEX\ idea is that one
3712            can change a font in between which is why the |fnt| variable gets set again. Because in
3713            practice switching a font can involve more than assignments wd could be more tolerant
3714            and often wrapping in |\localcontrolled| is more robust then.
3715        */
3716     /* handle_assignments(); */
3717        fnt = cur_font_par;
3718      PICKUP:
3719        switch (cur_cmd) {
3720            case spacer_cmd:
3721            case relax_cmd:
3722                tex_get_x_token();
3723                goto PICKUP;
3724            case letter_cmd:
3725            case other_char_cmd:
3726            case char_given_cmd:
3727                base = tex_new_glyph_node(glyph_unset_subtype, fnt, cur_chr, accent);
3728                break;
3729            case char_number_cmd:
3730                /* We don't accept keywords for |\glyph|. */
3731                base = tex_new_glyph_node(glyph_unset_subtype, fnt, tex_scan_char_number(0), accent);
3732                break;
3733            default:
3734                /* compatibility hack, not that useful nowadays  */
3735                if (cur_cmd <= max_non_prefixed_cmd) {
3736                    tex_back_input(cur_tok);
3737                    break;
3738                } else {
3739                    lmt_error_state.set_box_allowed = 0;
3740                    tex_run_prefixed_command();
3741                    lmt_error_state.set_box_allowed = 0;
3742                    goto PICKUP;
3743                }
3744        }
3745        if (base) {
3746            /*tex
3747                Append the accent with appropriate kerns, then set |p := q|. The kern nodes
3748                appended here must be distinguished from other kerns, lest they be wiped away by
3749                the hyphenation algorithm or by a previous line break. The two kerns are computed
3750                with (machine dependent) |real| arithmetic, but their sum is machine independent;
3751                the net effect is machine independent, because the user cannot remove these nodes
3752                nor access them via |\lastkern|.
3753
3754                This goes away: not listening to scaled yet.
3755
3756             */
3757            double t = (double) (tex_get_font_slant(fnt)) / (double) (65536); /* amount of slant */
3758            scaled w = tex_glyph_width(base);
3759            scaled h = tex_glyph_height(base);
3760            scaled delta = glueround((double) (w - a) / (double) (2) + h * t - x * s);
3761            halfword left = tex_new_kern_node(delta, accent_kern_subtype);
3762            halfword right = tex_new_kern_node(- a - delta, accent_kern_subtype);
3763            glyph_x_offset(accent) = xoffset;
3764            glyph_y_offset(accent) = yoffset;
3765            if (h != x) {
3766                /*tex the accent must be shifted up or down */
3767             // accent = hpack(accent, 0, packing_additional, direction_unknown);
3768             // box_shift_amount(accent) = x - h;
3769                glyph_y_offset(accent) += x - h;
3770            }
3771            tex_couple_nodes(cur_list.tail, left);
3772            tex_couple_nodes(left, accent);
3773            tex_couple_nodes(accent, right);
3774            tex_couple_nodes(right,base);
3775            cur_list.tail = base;
3776        } else {
3777            tex_couple_nodes(cur_list.tail, accent);
3778            cur_list.tail = accent;
3779        }
3780        cur_list.space_factor = default_space_factor;
3781    }
3782}
3783
3784/*tex Finally, |\endcsname| is not supposed to get through to |main_control|. */
3785
3786static void tex_aux_run_cs_error(void)
3787{
3788    tex_handle_error(
3789        normal_error_type,
3790        "Extra \\endcsname",
3791        "I'm ignoring this, since I wasn't doing a \\csname."
3792    );
3793}
3794
3795/*tex
3796
3797    Assignments to values in |eqtb| can be global or local. Furthermore, a control sequence can
3798    be defined to be |\long|, |\protected|, or |\outer|, and it might or might not be expanded.
3799    The prefixes |\global|, |\long|, |\protected|, and |\outer| can occur in any order. Therefore
3800    we assign binary numeric codes, making it possible to accumulate the union of all specified
3801    prefixes by adding the corresponding codes. (\PASCAL's |set| operations could also have been
3802    used.)
3803
3804    Every prefix, and every command code that might or might not be prefixed, calls the action
3805    procedure |prefixed_command|. This routine accumulates a sequence of prefixes until coming to
3806    a non-prefix, then it carries out the command.
3807
3808*/
3809
3810void tex_inject_text_or_line_dir(int val, int check_glue)
3811{
3812    if (cur_mode == hmode && internal_dir_state_par > 0) {
3813        /*tex |tail| is non zero but we test anyway. */
3814        halfword dirn = tex_new_dir(cancel_dir_subtype, text_direction_par);
3815        halfword tail = cur_list.tail;
3816        if (check_glue && tail && node_type(tail) == glue_node) { // && node_subtype(tail) != indent_skip_glue
3817            halfword prev = node_prev(tail);
3818            tex_couple_nodes(prev, dirn);
3819            tex_couple_nodes(dirn, tail);
3820        } else {
3821            tex_tail_append(dirn);
3822        }
3823    }
3824    tex_push_text_dir_ptr(val);
3825    if (cur_mode == hmode) {
3826        halfword dir = tex_new_dir(normal_dir_subtype, val);
3827        dir_level(dir) = cur_level;
3828        tex_tail_append(dir);
3829    }
3830}
3831
3832static void tex_aux_show_frozen_error(halfword cs)
3833{
3834    if (cs) {
3835        tex_handle_error(
3836            normal_error_type,
3837            "You can't redefine the frozen macro %S.", cs,
3838            NULL
3839        );
3840    } else {
3841        tex_handle_error(
3842            normal_error_type,
3843            "You can't redefine a frozen macro.",
3844            NULL
3845        );
3846
3847    }
3848}
3849
3850/*tex
3851
3852    We use the fact that |register| $<$ |advance| $<$ |multiply| $<$ |divide| We compute the
3853    register location |l| and its type |p| but |return| if invalid. Here we use the fact that
3854    the consecutive codes |int_val .. mu_val| and |assign_int .. assign_mu_glue| correspond
3855    to each other nicely.
3856
3857*/
3858
3859static inline halfword tex_aux_get_register_index(int level)
3860{
3861    switch (level) {
3862        case integer_val_level:
3863            {
3864                halfword index = tex_scan_integer_register_number();
3865                return register_integer_location(index);
3866            }
3867        case dimension_val_level:
3868            {
3869                halfword index = tex_scan_dimension_register_number();
3870                return register_dimension_location(index);
3871            }
3872        case attribute_val_level:
3873            {
3874                halfword index = tex_scan_attribute_register_number();
3875                return register_attribute_location(index);
3876            }
3877        case posit_val_level:
3878            {
3879                halfword index = tex_scan_posit_register_number();
3880                return register_posit_location(index);
3881            }
3882        case glue_val_level:
3883            {
3884                halfword index = tex_scan_glue_register_number();
3885                return register_glue_location(index);
3886            }
3887        case muglue_val_level:
3888            {
3889                halfword index = tex_scan_muglue_register_number();
3890                return register_muglue_location(index);
3891            }
3892        case token_val_level:
3893            {
3894                halfword index = tex_scan_toks_register_number();
3895                return register_toks_location(index);
3896            }
3897        default:
3898            return 0;
3899    }
3900}
3901
3902static inline halfword tex_aux_get_register_value(int level, int optionalequal)
3903{
3904    switch (level) {
3905        case integer_val_level:
3906        case attribute_val_level:
3907            return tex_scan_integer(optionalequal, NULL, NULL);
3908        case posit_val_level:
3909            return tex_scan_posit(optionalequal);
3910        case dimension_val_level:
3911            return tex_scan_dimension(0, 0, 0, optionalequal, NULL, NULL);
3912        default:
3913            return tex_scan_glue(level, optionalequal, 1);
3914    }
3915}
3916
3917static int tex_aux_valid_arithmic(int cmd, int *index, int *level, int *varcmd, int *simple, int *original)
3918{
3919    /*tex So: |\multiply|, |\divide| or |\advance|. */
3920    tex_get_x_token();
3921    *varcmd = cur_cmd;
3922 /* *simple = 0; */
3923    switch (cur_cmd) {
3924        case register_integer_cmd:
3925        case internal_integer_cmd:
3926            *index = cur_chr;
3927            *level = integer_val_level;
3928            *original = eq_value(*index);
3929            return 1;
3930        case register_attribute_cmd:
3931        case internal_attribute_cmd:
3932            *index = cur_chr;
3933            *level = attribute_val_level;
3934            *original = eq_value(*index);
3935            return 1;
3936        case register_posit_cmd:
3937        case internal_posit_cmd:
3938            *index = cur_chr;
3939            *level = posit_val_level;
3940            *original = eq_value(*index);
3941            return 1;
3942        case register_dimension_cmd:
3943        case internal_dimension_cmd:
3944            *index = cur_chr;
3945            *level = dimension_val_level;
3946            *original = eq_value(*index);
3947            return 1;
3948        case register_glue_cmd:
3949        case internal_glue_cmd:
3950            *index = cur_chr;
3951            *level = glue_val_level;
3952            *original = eq_value(*index);
3953            return 1;
3954        case register_muglue_cmd:
3955        case internal_muglue_cmd:
3956            *index = cur_chr;
3957            *level = muglue_val_level;
3958            *original = eq_value(*index);
3959            return 1;
3960        case register_cmd:
3961            *level = cur_chr;
3962            *index = tex_aux_get_register_index(cur_chr);
3963            *original = eq_value(*index);
3964            return 1;
3965        case integer_cmd:
3966            *index = cur_cs;
3967            *level = integer_val_level;
3968            *original = cur_chr;
3969            *simple = integer_cmd;
3970            return 1;
3971     // case index_cmd:
3972     //     *index = cur_cs;
3973     //     *level = integer_val_level;
3974     //     *original = cur_chr;
3975     //     *simple = index_cmd;
3976     //     return 1;
3977        case dimension_cmd:
3978            *index = cur_cs;
3979            *level = dimension_val_level;
3980            *original = cur_chr;
3981            *simple = dimension_cmd;
3982            return 1;
3983        case posit_cmd:
3984            *index = cur_cs;
3985            *level = posit_val_level;
3986            *original = cur_chr;
3987            *simple = posit_cmd;
3988            return 1;
3989        case gluespec_cmd:
3990            *index = cur_cs;
3991            *level = glue_val_level;
3992            *original = cur_chr;
3993            *simple = gluespec_cmd;
3994            return 1;
3995        case mugluespec_cmd:
3996            *index = cur_cs;
3997            *level = muglue_val_level;
3998            *original = cur_chr;
3999            *simple = mugluespec_cmd;
4000            return 1;
4001        default:
4002            tex_handle_error(
4003                normal_error_type,
4004                "You can't use '%C' after %C",
4005                cur_cmd, cur_chr, cmd, 0,
4006                "I'm forgetting what you said and not changing anything."
4007            );
4008            return 0;
4009    }
4010}
4011
4012static void tex_aux_arithmic_overflow_error(int level, halfword value)
4013{
4014    if (level >= glue_val_level) {
4015        tex_flush_node(value);
4016    }
4017    tex_handle_error(
4018        normal_error_type,
4019        "Arithmetic overflow",
4020        "I can't carry out that multiplication or division, since the result is out of\n"
4021        "range."
4022    );
4023}
4024
4025static inline void tex_aux_update_register(int a, int level, halfword index, halfword value, halfword cmd)
4026{
4027    switch (level) {
4028        case integer_val_level:
4029            tex_word_define(a, index, value);
4030            if (is_frozen(a) && cmd == internal_integer_cmd && cur_mode == hmode) {
4031                tex_update_par_par(internal_integer_cmd, index - lmt_primitive_state.prim_data[cmd].offset);
4032            }
4033            break;
4034        case attribute_val_level:
4035            if ((register_attribute_number(index)) > lmt_node_memory_state.max_used_attribute) {
4036                lmt_node_memory_state.max_used_attribute = register_attribute_number(index);
4037            }
4038            tex_change_attribute_register(a, index, value);
4039            tex_word_define(a, index, value);
4040            break;
4041        case posit_val_level:
4042            tex_word_define(a, index, value);
4043            if (is_frozen(a) && cmd == internal_posit_cmd && cur_mode == hmode) {
4044                tex_update_par_par(internal_posit_cmd, index - lmt_primitive_state.prim_data[cmd].offset);
4045            }
4046            break;
4047        case dimension_val_level:
4048            tex_word_define(a, index, value);
4049            if (is_frozen(a) && cmd == internal_dimension_cmd && cur_mode == hmode) {
4050                tex_update_par_par(internal_dimension_cmd, index - lmt_primitive_state.prim_data[cmd].offset);
4051            }
4052            break;
4053        case glue_val_level:
4054            tex_define(a, index, cmd == internal_glue_cmd ? internal_glue_reference_cmd : register_glue_reference_cmd, value);
4055            if (is_frozen(a) && cmd == internal_glue_cmd && cur_mode == hmode) {
4056                tex_update_par_par(internal_glue_cmd,  index - lmt_primitive_state.prim_data[cmd].offset);
4057            }
4058            break;
4059        case muglue_val_level:
4060            tex_define(a, index, cmd == internal_glue_cmd ? internal_muglue_reference_cmd : register_muglue_reference_cmd, value);
4061            break;
4062        default:
4063            /* can't happen */
4064            tex_word_define(a, index, value);
4065            break;
4066    }
4067}
4068
4069static inline void tex_aux_set_register(int a)
4070{
4071    halfword level = cur_chr;
4072    halfword varcmd = cur_cmd;
4073    halfword index = tex_aux_get_register_index(level);
4074    halfword value = tex_aux_get_register_value(level, 1);
4075    tex_aux_update_register(a, level, index, value, varcmd);
4076}
4077
4078/*tex 
4079    The by-less variant is more efficient as there is no push back of the token when there is not 
4080    such keyword. It goes unnoticed on an average run but not on the total runtime of 15.000 
4081    (times 2) runs of a 30 page \CONTEXT\ document with plenty of complex tables. So this is one 
4082    of the few obscure optimizations I grand myself (if only to be able to discuss it). The same is
4083    true for the zero and one checks, it saves some save stack although we check for that later
4084    anyway. 
4085*/
4086
4087static void tex_aux_arithmic_register(int a, int code)
4088{
4089    halfword cmd = cur_cmd;
4090    halfword level = cur_chr;
4091    halfword index = 0;
4092    halfword varcmd = 0;
4093    halfword simple = 0;
4094    halfword original = 0;
4095    if (tex_aux_valid_arithmic(cmd, &index, &level, &varcmd, &simple, &original)) {
4096        halfword value = null;
4097     // lmt_scanner_state.arithmic_error = 0;
4098        switch (code) {
4099            case advance_code:
4100                tex_scan_optional_keyword("by");
4101            case advance_by_code:
4102                {
4103                    halfword amount = tex_aux_get_register_value(level, 0);
4104                    switch (level) {
4105                        case integer_val_level:
4106                        case attribute_val_level:
4107                        case dimension_val_level:
4108                            if (is_global(a) || amount) {
4109                                value = original + amount;
4110                                break;
4111                            } else { 
4112                                /* likely a dimension */
4113                                return;
4114                            }
4115                        case posit_val_level:
4116                            if (is_global(a) || tex_posit_ne_zero(amount)) {
4117                                value = tex_posit_add(original, amount); 
4118                                break;
4119                            } else { 
4120                                return;
4121                            }
4122                        case glue_val_level:
4123                        case muglue_val_level:
4124                            if (is_global(a) || ! tex_glue_is_zero(amount)) {
4125                                /* Compute the sum of two glue specs */
4126                                glue_amount(amount) += glue_amount(original);
4127                                if (glue_stretch(amount) == 0) {
4128                                    glue_stretch_order(amount) = normal_glue_order;
4129                                }
4130                                if (glue_stretch_order(amount) == glue_stretch_order(original)) {
4131                                    glue_stretch(amount) += glue_stretch(original);
4132                                } else if ((glue_stretch_order(amount) < glue_stretch_order(original)) && (glue_stretch(original))) {
4133                                    glue_stretch(amount) = glue_stretch(original);
4134                                    glue_stretch_order(amount) = glue_stretch_order(original);
4135                                }
4136                                if (glue_shrink(amount) == 0) {
4137                                    glue_shrink_order(amount) = normal_glue_order;
4138                                }
4139                                if (glue_shrink_order(amount) == glue_shrink_order(original)) {
4140                                    glue_shrink(amount) += glue_shrink(original);
4141                                } else if ((glue_shrink_order(amount) < glue_shrink_order(original)) && (glue_shrink(original))) {
4142                                    glue_shrink(amount) = glue_shrink(original);
4143                                    glue_shrink_order(amount) = glue_shrink_order(original);
4144                                }
4145                                value = amount;
4146                                break;
4147                            } else {
4148                                tex_flush_node(amount);
4149                                return;
4150                            }
4151                        default:
4152                            /* error */
4153                            break;
4154                    }
4155                    /*tex There is no overflow detection for addition, just wraparound. */
4156                    if (simple) {
4157                        tex_define(a, index, (singleword) simple, value);
4158                    } else {
4159                        tex_aux_update_register(a, level, index, value, varcmd);
4160                    }
4161                    break;
4162                }
4163            case multiply_code:
4164                tex_scan_optional_keyword("by");
4165            case multiply_by_code:
4166                {
4167                    halfword amount = tex_scan_integer(0, NULL, NULL);
4168                    halfword value = 0;
4169                    if (is_global(a) || amount != 1) {
4170                        lmt_scanner_state.arithmic_error = 0;
4171                        switch (level) {
4172                            case integer_val_level:
4173                            case attribute_val_level:
4174                                value = tex_multiply_integers(original, amount);
4175                                break;
4176                            case posit_val_level:
4177                                value = tex_posit_mul_by(original, amount);
4178                                break;
4179                            case dimension_val_level:
4180                                value = tex_nx_plus_y(original, amount, 0);
4181                                break;
4182                            case glue_val_level:
4183                            case muglue_val_level:
4184                                {
4185                                    halfword newvalue = tex_new_glue_spec_node(original);
4186                                    glue_amount(newvalue) = tex_nx_plus_y(glue_amount(original), amount, 0);
4187                                    glue_stretch(newvalue) = tex_nx_plus_y(glue_stretch(original), amount, 0);
4188                                    glue_shrink(newvalue) = tex_nx_plus_y(glue_shrink(original), amount, 0);
4189                                    value = newvalue;
4190                                    break;
4191                                }
4192                            default:
4193                                /* error */
4194                                break;
4195                        }
4196                        if (lmt_scanner_state.arithmic_error) {
4197                            tex_aux_arithmic_overflow_error(level, value);
4198                        } else if (simple) {
4199                            tex_define(a, index, (singleword) simple, value);
4200                        } else {
4201                            tex_aux_update_register(a, level, index, value, varcmd);
4202                        }
4203                        break;
4204                    } else { 
4205                        return;
4206                    }
4207                }
4208            case divide_code:
4209            case r_divide_code:
4210            case e_divide_code:
4211                tex_scan_optional_keyword("by");
4212            case divide_by_code:
4213            case r_divide_by_code:
4214            case e_divide_by_code:
4215                {
4216                    halfword amount = tex_scan_integer(0, NULL, NULL);
4217                    if (is_global(a) || amount != 1) {
4218                        bool rounded = code == r_divide_code || code == r_divide_by_code;
4219                        lmt_scanner_state.arithmic_error = 0;
4220                        switch (level) {
4221                            case dimension_val_level:
4222                                if (rounded) {
4223                                    value = tex_quotient(original >> 16, amount, 1) << 16;
4224                                    break;
4225                                }
4226                            case integer_val_level:
4227                            case attribute_val_level:
4228                                {
4229                                    bool asexpr = code == e_divide_code || code == e_divide_by_code;
4230                                    value = tex_quotient(original, amount, asexpr || rounded);
4231                                    break;
4232                                }
4233                            case posit_val_level:
4234                                value = tex_posit_div_by(original, amount); /* always rounded */
4235                                break;
4236                            case glue_val_level:
4237                            case muglue_val_level:
4238                                {
4239                                    halfword newvalue = tex_new_glue_spec_node(original);
4240                                    /* we could shift over 0 when not rounded but .. why bother */
4241                                    if (rounded) {
4242                                        glue_amount(newvalue) = tex_quotient(glue_amount(original) >> 16, amount, 1) << 16;
4243                                        glue_stretch(newvalue) = tex_quotient(glue_stretch(original) >> 16, amount, 1) << 16;
4244                                        glue_shrink(newvalue) = tex_quotient(glue_shrink(original) >> 16, amount, 1) << 16;
4245                                    } else {
4246                                        bool asexpr = code == e_divide_code || code == e_divide_by_code;
4247                                        glue_amount(newvalue) = tex_quotient(glue_amount(original), amount, asexpr);
4248                                        glue_stretch(newvalue) = tex_quotient(glue_stretch(original), amount, asexpr);
4249                                        glue_shrink(newvalue) = tex_quotient(glue_shrink(original), amount, asexpr);
4250                                    }
4251                                    value = newvalue;
4252                                    break;
4253                                }
4254                            default:
4255                                /* error */
4256                                break;
4257                        }
4258                        if (lmt_scanner_state.arithmic_error) {
4259                            tex_aux_arithmic_overflow_error(level, value);
4260                        } else if (simple) {
4261                            tex_define(a, index, (singleword) simple, value);
4262                        } else {
4263                            tex_aux_update_register(a, level, index, value, varcmd);
4264                        }
4265                        break;
4266                    } else { 
4267                        return;
4268                    }
4269                }
4270            /*
4271            case advance_by_plus_one_code:
4272            case advance_by_minus_one_code:
4273                {
4274                    switch (level) {
4275                        case integer_val_level:
4276                        case attribute_val_level:
4277                            original += code == advance_by_plus_one_code ? 1 : -1;
4278                            if (simple) {
4279                                tex_define(a, index, simple, original);
4280                            } else {
4281                                tex_aux_update_register(a, level, index, original, varcmd);
4282                            }
4283                            break;
4284                    }
4285                    break;
4286                }
4287            */
4288        }
4289    }
4290}
4291
4292/*tex
4293    The value of |c| is 0 for |\deadcycles|, 1 for |\insertpenalties|, etc. In traditional \TEX\
4294    the interaction mode is set by primitives so no checking is needed. However, in \ETEX\ the
4295    value can be set. As a consequence there is an error message for wrong values but here we
4296    just clip the values. After all, we can also set values from \LUA\ so either we bark or we
4297    just recover. So, gone is:
4298
4299    \starttyping
4300    handle_error_int(
4301        normal_error_type,
4302        "Bad interaction mode (", val, ")",
4303        "Modes are 0=batch, 1=nonstop, 2=scroll, and 3=errorstop. Proceed, and I'll ignore\n"
4304        "this case."
4305    );
4306    \stoptyping
4307
4308    I could have decided to ignore bad values but clipping is probably better.
4309
4310*/
4311
4312static inline void tex_aux_set_interaction(halfword mode)
4313{
4314    tex_print_ln();
4315    if (mode < batch_mode) {
4316        lmt_error_state.interaction = batch_mode;
4317    } else if (mode > error_stop_mode) {
4318        lmt_error_state.interaction = error_stop_mode;
4319    } else {
4320        lmt_error_state.interaction = mode;
4321    }
4322    tex_fixup_selector(lmt_fileio_state.log_opened);
4323}
4324
4325static void tex_aux_set_page_property(void)
4326{
4327    switch (cur_chr) {
4328        case page_goal_code:
4329            lmt_page_builder_state.goal = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4330            break;
4331        case page_vsize_code:
4332            lmt_page_builder_state.vsize = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4333            break;
4334        case page_total_code:
4335            lmt_page_builder_state.total = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4336            /*tex Otherwise we have to also set that at the \TEX\ end: */
4337            lmt_page_builder_state.last_height = lmt_page_builder_state.total;
4338            /*tex So when setting total and depth first total needs to be set! */
4339            lmt_page_builder_state.last_depth = 0;
4340            break;
4341        case page_depth_code:
4342            lmt_page_builder_state.depth = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4343            /*tex Otherwise we have to also set that at the \TEX\ end: */
4344            lmt_page_builder_state.last_depth = lmt_page_builder_state.depth;
4345            break;
4346        case page_excess_code:
4347            lmt_page_builder_state.excess = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4348            break;
4349        case page_last_height_code:
4350            lmt_page_builder_state.last_height = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4351            break;
4352        case page_last_depth_code:
4353            lmt_page_builder_state.last_depth = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4354            break;
4355        case dead_cycles_code:
4356            lmt_page_builder_state.dead_cycles = tex_scan_integer(1, NULL, NULL);
4357            break;
4358        case insert_penalties_code:
4359            lmt_page_builder_state.insert_penalties = tex_scan_integer(1, NULL, NULL);
4360            break;
4361        case insert_heights_code:
4362            lmt_page_builder_state.insert_heights = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4363            break;
4364        case insert_storing_code:
4365            lmt_insert_state.storing = tex_scan_integer(1, NULL, NULL);
4366            break;
4367        case insert_distance_code:
4368            {
4369                /*tex
4370                    We need to scan the index first because when we do that in the call we somehow
4371                    get an out-of-order issue (index too large). The same is true for teh rest.
4372                */
4373                int index = tex_scan_integer(0, NULL, NULL);
4374                tex_set_insert_distance(index, tex_scan_glue(glue_val_level, 1, 1));
4375            }
4376            break;
4377        case insert_multiplier_code:
4378            {
4379                int index = tex_scan_integer(0, NULL, NULL);
4380                tex_set_insert_multiplier(index, tex_scan_integer(1, NULL, NULL));
4381            }
4382            break;
4383        case insert_limit_code:
4384            {
4385                int index = tex_scan_integer(0, NULL, NULL);
4386                tex_set_insert_limit(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4387            }
4388            break;
4389        case insert_storage_code:
4390            {
4391                int index = tex_scan_integer(0, NULL, NULL);
4392                tex_set_insert_storage(index, tex_scan_integer(1, NULL, NULL));
4393            }
4394            break;
4395        case insert_penalty_code:
4396            {
4397                int index = tex_scan_integer(0, NULL, NULL);
4398                tex_set_insert_penalty(index, tex_scan_integer(1, NULL, NULL));
4399            }
4400            break;
4401        case insert_maxdepth_code:
4402            {
4403                int index = tex_scan_integer(0, NULL, NULL);
4404                tex_set_insert_maxdepth(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4405            }
4406            break;
4407        case insert_height_code:
4408            {
4409                int index = tex_scan_integer(0, NULL, NULL);
4410                tex_set_insert_height(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4411            }
4412            break;
4413        case insert_depth_code:
4414            {
4415                int index = tex_scan_integer(0, NULL, NULL);
4416                tex_set_insert_depth(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4417            }
4418            break;
4419        case insert_width_code:
4420            {
4421                int index = tex_scan_integer(0, NULL, NULL);
4422                tex_set_insert_width(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4423            }
4424            break;
4425        case insert_line_height_code:
4426            {
4427                int index = tex_scan_integer(0, NULL, NULL);
4428                tex_set_insert_line_height(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4429            }
4430            break;
4431        case insert_line_depth_code:
4432            {
4433                int index = tex_scan_integer(0, NULL, NULL);
4434                tex_set_insert_line_depth(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4435            }
4436            break;
4437        case insert_stretch_code:
4438            {
4439                int index = tex_scan_integer(0, NULL, NULL);
4440                tex_set_insert_stretch(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4441            }
4442            break;
4443        case insert_shrink_code:
4444            {
4445                int index = tex_scan_integer(0, NULL, NULL);
4446                tex_set_insert_shrink(index, tex_scan_dimension(0, 0, 0, 1, NULL, NULL));
4447            }
4448            break;
4449        case page_stretch_code:                        
4450            lmt_page_builder_state.stretch = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4451            break;
4452        case page_fistretch_code:                    
4453            lmt_page_builder_state.fistretch = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4454            break;
4455        case page_filstretch_code:                    
4456            lmt_page_builder_state.filstretch = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4457            break;
4458        case page_fillstretch_code:                   
4459            lmt_page_builder_state.fillstretch = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4460            break;
4461        case page_filllstretch_code:                  
4462            lmt_page_builder_state.filllstretch = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4463            break;
4464        case page_shrink_code:                        
4465            lmt_page_builder_state.shrink = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4466            break;
4467        default:
4468            tex_confusion("page property");
4469            break;
4470    }
4471}
4472
4473/*tex
4474    The |space_factor| or |prev_depth| settings are changed when a |set_aux| command is sensed.
4475    Similarly, |prev_graf| is changed in the presence of |set_prev_graf|, and |dead_cycles| or
4476    |insert_penalties| in the presence of |set_page_int|. These definitions are always global.
4477*/
4478
4479static void tex_aux_set_auxiliary(int a)
4480{
4481    (void) a;
4482    switch (cur_chr) {
4483        case space_factor_code:
4484            if (cur_mode == hmode) {
4485                cur_list.space_factor = tex_scan_space_factor(1);
4486            } else {
4487                tex_aux_run_illegal_case();
4488            }
4489            break;
4490        case prev_depth_code:
4491            if (cur_mode == vmode) {
4492                cur_list.prev_depth = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4493            } else {
4494                tex_aux_run_illegal_case();
4495            }
4496            break;
4497        case prev_graf_code:
4498            lmt_nest_state.nest[tex_vmode_nest_index()].prev_graf = tex_scan_positive_number(1);
4499            break;
4500        case interaction_mode_code:
4501            tex_aux_set_interaction(tex_scan_integer(1, NULL, NULL));
4502            break;
4503        case insert_mode_code:
4504            tex_set_insert_mode(tex_scan_integer(1, NULL, NULL));
4505            break;
4506    }
4507}
4508
4509/*tex
4510    When some dimension of a box register is changed, the change isn't exactly global; but \TEX\
4511    does not look at the |\global| switch.
4512*/
4513
4514static void tex_aux_set_box_property(void)
4515{
4516    halfword code = cur_chr;
4517    halfword n = tex_scan_box_register_number();
4518    halfword b = box_register(n);
4519    switch (code) {
4520        case box_width_code:
4521            {
4522                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4523                if (b) {
4524                    box_width(b) = v;
4525                }
4526                break;
4527            }
4528        case box_height_code:
4529            {
4530                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4531                if (b) {
4532                    box_height(b) = v;
4533                }
4534                break;
4535            }
4536        case box_depth_code:
4537            {
4538                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4539                if (b) {
4540                    box_depth(b) = v;
4541                }
4542                break;
4543            }
4544        case box_direction_code:
4545            {
4546                halfword v = tex_scan_direction(1);
4547                if (b) {
4548                    tex_set_box_direction(b, v);
4549                }
4550                break;
4551            }
4552        case box_geometry_code:
4553            {
4554                halfword v = tex_scan_geometry(1);
4555                if (b) {
4556                    box_geometry(b) = (singleword) v;
4557                }
4558                break;
4559            }
4560        case box_orientation_code:
4561            {
4562                halfword v = tex_scan_orientation(1);
4563                if (b) {
4564                    box_orientation(b) = v;
4565                    tex_set_box_geometry(b, orientation_geometry);
4566                }
4567                break;
4568            }
4569        case box_anchor_code:
4570        case box_anchors_code:
4571            {
4572                halfword v = code == box_anchor_code ? tex_scan_anchor(1) : tex_scan_anchors(1);
4573                if (b) {
4574                    box_anchor(b) = v;
4575                    tex_set_box_geometry(b, anchor_geometry);
4576                }
4577                break;
4578            }
4579        case box_source_code:
4580            {
4581                halfword v = tex_scan_integer(1, NULL, NULL);
4582                if (b) {
4583                    box_source_anchor(b) = v;
4584                    tex_set_box_geometry(b, anchor_geometry);
4585                }
4586                break;
4587            }
4588        case box_target_code:
4589            {
4590                halfword v = tex_scan_integer(1, NULL, NULL);
4591                if (b) {
4592                    box_target_anchor(b) = v;
4593                    tex_set_box_geometry(b, anchor_geometry);
4594                }
4595                break;
4596            }
4597        case box_xoffset_code:
4598            {
4599                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4600                if (b) {
4601                    box_x_offset(b) = v;
4602                    tex_set_box_geometry(b, offset_geometry);
4603                }
4604                break;
4605            }
4606        case box_yoffset_code:
4607            {
4608                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4609                if (b) {
4610                    box_y_offset(b) = v;
4611                    tex_set_box_geometry(b, offset_geometry);
4612                }
4613                break;
4614            }
4615        case box_xmove_code:
4616            {
4617                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4618                if (b) {
4619                    box_x_offset(b) = tex_aux_checked_dimension1(box_x_offset(b) + v);
4620                    box_width(b) = tex_aux_checked_dimension2(box_width(b) + v);
4621                    tex_set_box_geometry(b, offset_geometry);
4622                }
4623                break;
4624            }
4625        case box_ymove_code:
4626            {
4627                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4628                if (b) {
4629                    box_y_offset(b) = tex_aux_checked_dimension1(box_y_offset(b) + v);
4630                    box_height(b) = tex_aux_checked_dimension2(box_height(b) + v);
4631                    box_depth(b) = tex_aux_checked_dimension2(box_depth(b) - v);
4632                    tex_set_box_geometry(b, offset_geometry);
4633                }
4634                break;
4635            }
4636        case box_total_code:
4637            {
4638                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4639                if (b) {
4640                    box_height(b) = v / 2;
4641                    box_depth(b) = v - (v / 2);
4642                }
4643            }
4644            break;
4645        case box_shift_code:
4646            {
4647                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4648                if (b) {
4649                    box_shift_amount(b) = v;
4650                }
4651            }
4652            break;
4653        case box_adapt_code:
4654            {
4655                scaled v = tex_scan_limited_scale(1);
4656                if (b) {
4657                    tex_repack(b, v, packing_adapted);
4658                }
4659            }
4660            break;
4661        case box_repack_code:
4662            {
4663                scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4664                if (b) {
4665                    tex_repack(b, v, packing_additional);
4666                }
4667            }
4668            break;
4669        case box_freeze_code:
4670            {
4671                halfword recurse = tex_scan_integer(1, NULL, NULL);
4672                if (b) {
4673                    tex_freeze(b, recurse, -1, 0);
4674                }
4675            }
4676            break;
4677        case box_migrate_code:
4678            {
4679                halfword what = tex_scan_integer(1, NULL, NULL);
4680                if (b) {
4681                    /* maybe check for post */
4682                    halfword first = null; 
4683                    halfword last = null; 
4684                    tex_migrate(b, &first, &last, what & auto_migrate_insert, what & auto_migrate_mark);
4685                    if (first) { 
4686                        box_post_migrated(b) = first;
4687                    }
4688                }
4689            }
4690            break;
4691        case box_limitate_code:
4692            {
4693                halfword recurse = tex_scan_integer(1, NULL, NULL);
4694                if (b) {
4695                    tex_freeze(b, recurse, node_type(b), 0);
4696                }
4697            }
4698            break;
4699        case box_finalize_code:
4700            {
4701                halfword factor = tex_scan_integer(0, NULL, NULL);
4702                if (b) {
4703                    tex_freeze(b, 0, -1, factor); /* recurse makes no sense here */
4704                }
4705            }
4706            break;
4707        case box_limit_code:
4708            {
4709                if (b && box_list(b)) {
4710                    tex_limit(b);
4711                }
4712            }
4713            break;
4714        case box_stretch_code:
4715        case box_shrink_code:
4716            /* ignore: maybe apply some factor */
4717            break;
4718        case box_subtype_code:
4719            /* ignore: maybe set it (limited subset) */
4720            break;
4721        case box_attribute_code:
4722            {
4723                halfword att = tex_scan_attribute_register_number();
4724                halfword val = tex_scan_integer(1, NULL, NULL);
4725                if (b) {
4726                    if (val == unused_attribute_value) {
4727                        tex_unset_attribute(b, att, val);
4728                    } else {
4729                        tex_set_attribute(b, att, val);
4730                    }
4731                }
4732            }
4733            break;
4734        case box_vadjust_code: 
4735            if (b) { 
4736                tex_set_vadjust(b);
4737            } else { 
4738                tex_run_vadjust(); /* maybe error */
4739            }
4740            break;
4741        case box_inserts_code:
4742            break;
4743        default:
4744            break;
4745    }
4746}
4747
4748/*tex
4749    The processing of boxes is somewhat different, because we may need to scan and create an entire
4750    box before we actually change the value of the old one.
4751*/
4752
4753static void tex_aux_set_box(int a)
4754{
4755    halfword slot = tex_scan_box_register_number();
4756    if (lmt_error_state.set_box_allowed) {
4757        tex_aux_scan_box(is_global(a) ? global_box_flag : box_flag, 1, null_flag, slot, 0, 0);
4758    } else {
4759        tex_handle_error(
4760            normal_error_type,
4761            "Improper \\setbox",
4762            "Sorry, \\setbox is not allowed after \\halign in a display, between \\accent and\n"
4763            "an accented character, or in immediate assignments."
4764        );
4765    }
4766}
4767
4768/*tex
4769    We temporarily define |p| to be |relax|, so that an occurrence of |p| while scanning the
4770    definition will simply stop the scanning instead of producing an \quote {undefined control
4771    sequence} error or expanding the previous meaning. This allows, for instance, |\chardef
4772    \foo = 123\foo|.
4773*/
4774
4775static void tex_aux_set_shorthand_def(int a, int force)
4776{
4777    halfword code = cur_chr;
4778    /*
4779    switch (code) { 
4780        case integer_def_csname_code:
4781        case dimension_def_csname_code:
4782            cur_cs = tex_create_csname();
4783            break;
4784        default:           
4785            tex_get_r_token();
4786            break;
4787    }
4788    */
4789    tex_get_r_token();
4790    if (force || tex_define_permitted(cur_cs, a)) {
4791        /* can we optimize the dual define, like no need to destroy in second call */
4792        halfword p = cur_cs;
4793        tex_define(a, p, relax_cmd, relax_code);
4794        tex_scan_optional_equals();
4795        switch (code) {
4796            case char_def_code:
4797                {
4798                    halfword chr = tex_scan_char_number(0);
4799                    tex_define_again(a, p, char_given_cmd, chr);
4800                    break;
4801                }
4802            case math_char_def_code:
4803                {
4804                    mathcodeval mval = tex_scan_mathchar(tex_mathcode);
4805                    tex_define_again(a, p, mathspec_cmd, tex_new_math_spec(mval, tex_mathcode));
4806                    break;
4807                }
4808            case math_uchar_def_code:
4809                {
4810                    mathcodeval mval = tex_scan_mathchar(umath_mathcode);
4811                    tex_define_again(a, p, mathspec_cmd, tex_new_math_spec(mval, umath_mathcode));
4812                    break;
4813                }
4814            case math_dchar_def_code:
4815                {
4816                    mathdictval dval = tex_scan_mathdict();
4817                    mathcodeval mval = tex_scan_mathchar(umath_mathcode);
4818                    tex_define_again(a, p, mathspec_cmd, tex_new_math_dict_spec(dval, mval, umath_mathcode));
4819                    break;
4820                }
4821            case count_def_code:
4822                {
4823                    halfword n = tex_scan_integer_register_number();
4824                    tex_define_again(a, p, register_integer_cmd, register_integer_location(n));
4825                    break;
4826                }
4827            case attribute_def_code:
4828                {
4829                    halfword n = tex_scan_attribute_register_number();
4830                    tex_define_again(a, p, register_attribute_cmd, register_attribute_location(n));
4831                    break;
4832                }
4833            case dimen_def_code:
4834                {
4835                    scaled n = tex_scan_dimension_register_number();
4836                    tex_define_again(a, p, register_dimension_cmd, register_dimension_location(n));
4837                    break;
4838                }
4839            case skip_def_code:
4840                {
4841                    halfword n = tex_scan_glue_register_number();
4842                    tex_define_again(a, p, register_glue_cmd, register_glue_location(n));
4843                    break;
4844                }
4845            case muskip_def_code:
4846                {
4847                    halfword n = tex_scan_muglue_register_number();
4848                    tex_define_again(a, p, register_muglue_cmd, register_muglue_location(n));
4849                    break;
4850                }
4851            case toks_def_code:
4852                {
4853                    halfword n = tex_scan_toks_register_number();
4854                    tex_define_again(a, p, register_toks_cmd, register_toks_location(n));
4855                    break;
4856                }
4857            case float_def_code:
4858                {
4859                    scaled n = tex_scan_posit_register_number();
4860                    tex_define_again(a, p, register_posit_cmd, register_posit_location(n));
4861                    break;
4862                }
4863            case lua_def_code:
4864                {
4865                    halfword v = tex_scan_function_reference(1);
4866                    tex_define_again(a, p, is_protected(a) ? lua_protected_call_cmd : (is_semiprotected(a) ? lua_semi_protected_call_cmd : lua_call_cmd), v);
4867                    break;
4868                }
4869            case integer_def_code:
4870         /* case integer_def_csname_code: */
4871                {
4872                    halfword v = tex_scan_integer(1, NULL, NULL);
4873                    tex_define_again(a, p, integer_cmd, v);
4874                    break;
4875                }
4876            case dimension_def_code:
4877         /* case dimension_def_csname_code: */
4878                {
4879                    scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
4880                    tex_define_again(a, p, dimension_cmd, v);
4881                    break;
4882                }
4883            case gluespec_def_code:
4884                {
4885                    halfword v = tex_scan_glue(glue_val_level, 1, 1);
4886                    tex_define_again(a, p, gluespec_cmd, v);
4887                    break;
4888                }
4889            case mugluespec_def_code:
4890                {
4891                    halfword v = tex_scan_glue(muglue_val_level, 1, 0);
4892                    tex_define_again(a, p, mugluespec_cmd, v);
4893                    break;
4894                }
4895            case posit_def_code:
4896         /* case posit_def_csname_code: */
4897                {
4898                    scaled v = tex_scan_posit(1);
4899                    tex_define_again(a, p, posit_cmd, v);
4900                    break;
4901                }
4902            case parameter_def_code:
4903         /* case index_def_csname_code: */
4904                {
4905                    halfword v = tex_get_parameter_index(tex_scan_parameter_index());
4906                    tex_define_again(a, p, index_cmd, v);
4907                    break;
4908                }
4909            /*
4910            case mathspec_def_code:
4911                {
4912                    halfword v = tex_scan_math_spec(1);
4913                    tex_define(a, p, mathspec_cmd, v);
4914                    break;
4915                }
4916            */
4917            case fontspec_def_code:
4918                {
4919                    halfword v = tex_scan_font(1);
4920                    tex_define(a, p, fontspec_cmd, v);
4921                    break;
4922                }
4923            case specification_def_code:
4924                {
4925                    halfword v = tex_scan_specifier();
4926                    tex_define(a, p, specificationspec_cmd, v);
4927                    break;
4928                }
4929            default:
4930                tex_confusion("shorthand definition");
4931                break;
4932        }
4933    }
4934}
4935
4936static void tex_aux_set_association(int flags, int force)
4937{
4938    switch (cur_chr) { 
4939        case unit_association_code: 
4940            { 
4941                tex_get_r_token();
4942                if (tex_valid_userunit(cur_cmd, cur_chr, cur_cs)) {
4943                    halfword cs = cur_cs;
4944                    halfword index = tex_scan_unit_register_number(1);
4945                    if (tex_get_unit_class(index)) { 
4946                        tex_handle_error(
4947                            normal_error_type,
4948                            "Imvalid \\associateunit, unit %i is already taken", index, 
4949                            "Units can only be bound once and not overload built-in ones."
4950                        );
4951                    } else if (force || tex_define_permitted(cs, flags)) {
4952                        unit_parameter(index) = cs;
4953                    }
4954                } else { 
4955                    tex_handle_error(
4956                        normal_error_type,
4957                        "Invalid \\associateunit target",
4958                        "Only existing dimension equivalent commands are accepted."
4959                    );
4960                }
4961                break;
4962            }
4963    }
4964}
4965
4966/*tex
4967    All of \TEX's parameters are kept in |eqtb| except the font and language information, including
4968    the hyphenation tables; these are strictly global.
4969*/
4970
4971static void tex_aux_set_hyph_data(void)
4972{
4973    switch (cur_chr) {
4974        case hyphenation_code:
4975            { 
4976                halfword result = tex_scan_toks_expand(0, NULL, 0, 0);
4977                tex_load_tex_hyphenation(language_par, result);
4978                tex_flush_token_list(result);
4979                break;
4980            }
4981        case patterns_code:
4982            { 
4983                halfword result = tex_scan_toks_expand(0, NULL, 0, 0);
4984                tex_load_tex_patterns(language_par, result);
4985                tex_flush_token_list(result);
4986                break;
4987            }
4988        case prehyphenchar_code:
4989            tex_set_pre_hyphen_char(language_par, tex_scan_integer(1, NULL, NULL));
4990            break;
4991        case posthyphenchar_code:
4992            tex_set_post_hyphen_char(language_par, tex_scan_integer(1, NULL, NULL));
4993            break;
4994        case preexhyphenchar_code:
4995            tex_set_pre_exhyphen_char(language_par, tex_scan_integer(1, NULL, NULL));
4996            break;
4997        case postexhyphenchar_code:
4998            tex_set_post_exhyphen_char(language_par, tex_scan_integer(1, NULL, NULL));
4999            break;
5000        case hyphenationmin_code:
5001            tex_set_hyphenation_min(language_par, tex_scan_integer(1, NULL, NULL));
5002            break;
5003        case hjcode_code:
5004            {
5005                halfword lan = tex_scan_integer(0, NULL, NULL);
5006                halfword val = tex_scan_integer(1, NULL, NULL);
5007                tex_set_hj_code(language_par, lan, val, -1);
5008            }
5009            break;
5010        default:
5011            break;
5012    }
5013}
5014
5015/*tex move to font */
5016
5017static void tex_aux_set_font_property(void)
5018{
5019    halfword code = cur_chr;
5020    switch (code) {
5021        case font_hyphen_code:
5022            {
5023                halfword fnt = tex_scan_font_identifier(NULL);
5024                halfword val = tex_scan_integer(1, NULL, NULL);
5025                set_font_hyphen_char(fnt, val);
5026                break;
5027            }
5028        case font_skew_code:
5029            {
5030                halfword fnt = tex_scan_font_identifier(NULL);
5031                halfword val = tex_scan_integer(1, NULL, NULL);
5032                set_font_skew_char(fnt, val);
5033                break;
5034            }
5035        case font_lp_code:
5036            {
5037                halfword fnt = tex_scan_font_identifier(NULL);
5038                halfword chr = tex_scan_char_number(0);
5039                halfword val = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
5040                tex_set_lpcode_in_font(fnt, chr, val);
5041                break;
5042            }
5043        case font_rp_code:
5044            {
5045                halfword fnt = tex_scan_font_identifier(NULL);
5046                halfword chr = tex_scan_char_number(0);
5047                halfword val = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
5048                tex_set_rpcode_in_font(fnt, chr, val);
5049                break;
5050            }
5051        case font_ef_code:
5052            {
5053                halfword fnt = tex_scan_font_identifier(NULL);
5054                halfword chr = tex_scan_char_number(0);
5055                halfword val = tex_scan_integer(1, NULL, NULL);
5056                tex_set_efcode_in_font(fnt, chr, val);
5057                break;
5058            }
5059        case font_cf_code:
5060            {
5061                halfword fnt = tex_scan_font_identifier(NULL);
5062                halfword chr = tex_scan_char_number(0);
5063                halfword val = tex_scan_integer(1, NULL, NULL);
5064                tex_set_cfcode_in_font(fnt, chr, val);
5065                break;
5066            }
5067        case font_dimension_code:
5068            {
5069                tex_set_font_dimension();
5070                break;
5071            }
5072        case scaled_font_dimension_code:
5073            {
5074                tex_set_scaled_font_dimension();
5075                break;
5076            }
5077        default:
5078            break;
5079    }
5080}
5081
5082/*tex
5083    Here is where the information for a new font gets loaded. We start with fonts. Unfortunately,
5084    they aren't all as simple as this.
5085*/
5086
5087static void tex_aux_set_font(int a)
5088{
5089    tex_set_cur_font(a, cur_chr);
5090}
5091
5092static void tex_aux_set_define_font(int a)
5093{
5094    if (! tex_tex_def_font(a)) {
5095        tex_aux_show_frozen_error(cur_cs);
5096    }
5097}
5098
5099/*tex
5100    When a |def| command has been scanned, |cur_chr| is odd if the definition is supposed to be
5101    global, and |cur_chr >= 2| if the definition is supposed to be expanded. Remark: this is
5102    different in \LUAMETATEX.
5103*/
5104
5105static void tex_aux_set_def(int flags, int force)
5106{
5107    int expand = 0;
5108    switch (cur_chr) {
5109        case expanded_def_code:
5110            expand = 1;
5111            break;
5112        case def_code:
5113            break;
5114        case global_expanded_def_code:
5115            expand = 1;
5116            // fall through
5117        case global_def_code:
5118            flags = add_global_flag(flags);
5119            break;
5120        case expanded_def_csname_code:
5121            expand = 1;
5122            // fall through
5123        case def_csname_code:
5124            cur_cs = tex_create_csname();
5125            goto DONE;
5126        case global_expanded_def_csname_code:
5127            expand = 1;
5128            // fall through
5129        case global_def_csname_code:
5130            cur_cs = tex_create_csname();
5131            flags = add_global_flag(flags);
5132            goto DONE;
5133        case constant_def_code:
5134            expand = 2;
5135            flags = add_constant_flag(flags);
5136            break;
5137        case constant_def_csname_code:
5138            expand = 2;
5139            cur_cs = tex_create_csname();
5140            flags = add_constant_flag(flags);
5141            goto DONE;
5142    }
5143    tex_get_r_token();
5144    DONE:
5145# if 1
5146    if (global_defs_par) {
5147        flags = global_defs_par > 0 ? add_global_flag(flags) : remove_global_flag(flags);
5148    }
5149# else
5150    /*tex Tracing discussed @ tex-implementors but differently. It makes not that sense anyway. */
5151    if (global_defs_par) {
5152        if (global_defs_par < 0) {
5153            if (is_global(flags)) {
5154                remove_global_flag(flags);
5155                if (tracing_commands_par) {
5156                    begin_diagnostic();
5157                    tprint_nl("{\global canceled}");
5158                    end_diagnostic();
5159                }
5160            }
5161        } else {
5162            if (! is_global(flags)) {
5163                add_global_flag(flags);
5164                if (tracing_commands_par) {
5165                    begin_diagnostic();
5166                    tprint_nl("{\global enforced}");
5167                    end_diagnostic();
5168                }
5169            }
5170        }
5171    }
5172# endif 
5173    if (force || tex_define_permitted(cur_cs, flags)) {
5174        halfword p = cur_cs;
5175        halfword t = expand == 2 ? tex_scan_toks_expand(0, null, 1, 0) : (expand ? tex_scan_macro_expand() : tex_scan_macro_normal());
5176        tex_define(flags, p, tex_flags_to_cmd(flags), t);
5177    }
5178}
5179
5180static void tex_aux_set_let(int flags, int force)
5181{
5182    halfword code = cur_chr;
5183    halfword cs = null;
5184    switch (code) {
5185        case global_let_code:
5186            /*tex |\glet| */
5187            if (global_defs_par >= 0) {
5188                flags = add_global_flag(flags);
5189            }
5190            // fall through
5191        case let_code:
5192            /*tex |\let| */
5193            tex_get_r_token();
5194          LETINDEED:
5195            if (force || tex_define_permitted(cur_cs, flags)) {
5196                cs = cur_cs;
5197                do {
5198                    tex_get_token();
5199                } while (cur_cmd == spacer_cmd);
5200                if (cur_tok == equal_token) {
5201                    tex_get_token();
5202                    if (cur_cmd == spacer_cmd) {
5203                        tex_get_token();
5204                    }
5205                }
5206            }
5207            break;
5208        case future_let_code:
5209        case future_def_code:
5210            /*tex |\futurelet| */
5211            tex_get_r_token();
5212            /*tex
5213                Checking for a frozen macro here is tricky but not doing it would be kind of weird.
5214            */
5215             if (force || tex_define_permitted(cur_cs, flags)) {
5216                 halfword q;
5217                 cs = cur_cs;
5218                 q = tex_get_token();
5219                 tex_back_input(tex_get_token());
5220                 /*tex
5221                     We look ahead and then back up. Note that |back_input| doesn't affect |cur_cmd|,
5222                     |cur_chr|.
5223                 */
5224                 tex_back_input(q);
5225                 if (code == future_def_code) {
5226                     halfword result = get_reference_token();
5227                     halfword r = result;
5228                     r = tex_store_new_token(r, cur_tok);
5229                     cur_cmd = tex_flags_to_cmd(flags);
5230                     cur_chr = result;
5231                 }
5232            }
5233            break;
5234        case let_charcode_code:
5235            /*tex |\letcharcode| (todo: protection) */
5236            {
5237                halfword character = tex_scan_integer(0, NULL, NULL);
5238                if (character > 0) {
5239                    cs = tex_active_to_cs(character, 1);
5240                    do {
5241                        tex_get_token();
5242                    } while (cur_cmd == spacer_cmd);
5243                    if (cur_tok == equal_token) {
5244                        tex_get_token();
5245                        if (cur_cmd == spacer_cmd) {
5246                            tex_get_token();
5247                        }
5248                    }
5249                } else {
5250                    tex_handle_error(
5251                        normal_error_type,
5252                        "invalid number for \\letcharcode",
5253                        NULL
5254                    );
5255                }
5256                break;
5257            }
5258        case swap_cs_values_code:
5259            {
5260                /*tex
5261                    There is no real gain in performance but it looks nicer when tracing when we
5262                    just swap natively (like no save and restore of a temporary variable and
5263                    such). Maybe we should be more restrictive but it's a cheap experiment anyway.
5264
5265                    Flags should match and should not contain permanent, primitive or immutable.
5266                */
5267                halfword s1, s2;
5268                tex_get_r_token();
5269                s1 = cur_cs;
5270                tex_get_r_token();
5271                s2 = cur_cs;
5272                tex_define_swapped(flags, s1, s2, force);
5273                return;
5274            }
5275        case let_protected_code:
5276            tex_get_r_token();
5277            if (force || tex_define_permitted(cur_cs, flags)) {
5278                switch (cur_cmd) {
5279                    case call_cmd:
5280                    case semi_protected_call_cmd:
5281                        set_eq_type(cur_cs, protected_call_cmd);
5282                        break;
5283                    case tolerant_call_cmd:
5284                    case tolerant_semi_protected_call_cmd:
5285                        set_eq_type(cur_cs, tolerant_protected_call_cmd);
5286                        break;
5287                }
5288            }
5289            return;
5290        case unlet_protected_code:
5291            tex_get_r_token();
5292            if (force || tex_define_permitted(cur_cs, flags)) {
5293                switch (cur_cmd) {
5294                    case protected_call_cmd:
5295                    case semi_protected_call_cmd:
5296                        set_eq_type(cur_cs, call_cmd);
5297                        break;
5298                    case tolerant_call_cmd:
5299                    case tolerant_semi_protected_call_cmd:
5300                        set_eq_type(cur_cs, tolerant_call_cmd);
5301                        break;
5302                }
5303            }
5304            return;
5305        case let_frozen_code:
5306            tex_get_r_token();
5307            if (is_call_cmd(cur_cmd) && (force || tex_define_permitted(cur_cs, flags))) {
5308                set_eq_flag(cur_cs, add_frozen_flag(eq_flag(cur_cs)));
5309            }
5310            return;
5311        case unlet_frozen_code:
5312            tex_get_r_token();
5313            if (is_call_cmd(cur_cmd) && (force || tex_define_permitted(cur_cs, flags))) {
5314                set_eq_flag(cur_cs, remove_frozen_flag(eq_flag(cur_cs)));
5315            }
5316            return;
5317        case global_let_csname_code:
5318            if (global_defs_par >= 0) {
5319                flags = add_global_flag(flags);
5320            }
5321            // fall through
5322        case let_csname_code:
5323            cur_cs = tex_create_csname();
5324            goto LETINDEED;
5325        case global_let_to_nothing_code:
5326            if (global_defs_par >= 0) {
5327                flags = add_global_flag(flags);
5328            }
5329            // fall through
5330        case let_to_nothing_code:
5331            tex_get_r_token();
5332         LETTONOTHING:
5333            if (force || tex_define_permitted(cur_cs, flags)) {
5334             // /*tex 
5335             //     The commented line permits plenty empty definitions, a |\let| can run out of 
5336             //     ref count so maybe some day \unknown 
5337             // */
5338             // halfword empty = get_reference_token();
5339             // tex_add_token_reference(empty);
5340                halfword empty = lmt_token_state.empty;
5341                tex_define(flags, cur_cs, tex_flags_to_cmd(flags), empty);
5342            }
5343            return;
5344        case let_to_last_named_cs_code:
5345            /*tex 
5346                There is no real reason for this primitive but it might be more intuitive to see 
5347                |\lettolastcsname \foo| than |\edef \foo {\lastnamedcs}. The gain in performance is 
5348                irrelevant here, it's more about readability and the amount of extra code can be 
5349                neglected. 
5350            */
5351            if (lmt_scanner_state.last_cs_name == null_cs) {
5352                tex_get_r_token();
5353                goto LETTONOTHING;
5354            } else {
5355                /*tex 
5356                    Do we need to bump the ref count already? We anyway need to save the current
5357                    value. Let's assume sane usage which is somewhat hard to imagine with primitives
5358                    like |\lastnamedcs|. 
5359                */
5360                halfword lastcs = lmt_scanner_state.last_cs_name;
5361                tex_get_r_token();
5362                cs = cur_cs;
5363                cur_cs = lastcs;
5364                cur_cmd = eq_type(lastcs); 
5365                cur_chr = eq_value(lastcs);
5366                break;
5367            }
5368        default:
5369            /*tex We please the compiler. */
5370            tex_confusion("let");
5371            break;
5372    }
5373    if (is_referenced_cmd(cur_cmd)) {
5374        tex_add_token_reference(cur_chr);
5375    } else if (is_nodebased_cmd(cur_cmd)) {
5376        cur_chr = cur_chr ? tex_copy_node(cur_chr) : null;
5377    }
5378 // if (cs && cur_cmd >= relax_cmd) {
5379    if (cs && cur_cmd >= 0) {
5380        singleword oldf = eq_flag(cur_cs);
5381        singleword newf = 0;
5382        singleword cmd = (singleword) cur_cmd;
5383        if (is_aliased(flags)) {
5384            /*tex 
5385                Aliases only work for non constants: else make a |\def| of it or we need some 
5386                pointer to the original but as the meaning can change. Too tricky. 
5387            */
5388            newf = oldf;
5389         // if (is_untraced(flags)) {
5390         //  // newf = add_untraced_flag(newf);
5391         //     newf |= untraced_flag_bit;
5392         // } 
5393        } else {
5394            oldf = remove_overload_flags(oldf);
5395            newf = oldf | make_eq_flag_bits(flags);
5396        }
5397        if (is_protected(flags)) {
5398            switch (cmd) {
5399                case call_cmd:
5400                    cmd = protected_call_cmd;
5401                    break;
5402                case tolerant_call_cmd:
5403                    cmd = tolerant_protected_call_cmd;
5404                    break;
5405            }
5406        }
5407        tex_define_inherit(flags, cs, (singleword) newf, (singleword) cmd, cur_chr);
5408    } else {
5409        tex_define(flags, cs, (singleword) cur_cmd, cur_chr); 
5410    }
5411}
5412
5413/*tex
5414    The token-list parameters, |\output| and |\everypar|, etc., receive their values in the
5415    following way. (For safety's sake, we place an enclosing pair of braces around an |\output|
5416    list.)
5417*/
5418
5419static void tex_aux_set_assign_toks(int a) // better just pass cmd and chr
5420{
5421    halfword cs = cur_cs;
5422    halfword cmd = cur_cmd;
5423    halfword chr;
5424    halfword tail;
5425    /*tex We either access by number or we have an internal |every_par_loc|, |output_routine_loc|, \dots */
5426    halfword loc = cmd == register_cmd ? register_toks_location(tex_scan_toks_register_number()) : cur_chr;
5427    /*tex
5428        Skip an optional equal sign and get the next non-blank non-relax non-call token.
5429    */
5430    {
5431        int n = 1 ;
5432        while (1) {
5433            tex_get_x_token();
5434            if (cur_cmd == spacer_cmd) {
5435                /*tex Go on! */
5436            } else if (cur_cmd == relax_cmd) {
5437                n = 0;
5438            } else if (n && cur_tok == equal_token) {
5439                n = 0;
5440            } else {
5441                break;
5442            }
5443        }
5444    }
5445    if (cur_cmd != left_brace_cmd) {
5446        /*tex
5447            If the right-hand side is a token parameter or token register, finish
5448            the assignment and |goto done|
5449        */
5450        if (cur_cmd == register_cmd && cur_chr == token_val_level) {
5451            chr = eq_value(register_toks_location(tex_scan_toks_register_number()));
5452            if (chr) {
5453                tex_add_token_reference(chr);
5454            }
5455            goto DEFINE;
5456        } else if (cur_cmd == register_toks_cmd || cur_cmd == internal_toks_cmd) {
5457            chr = eq_value(cur_chr);
5458            if (chr) {
5459                tex_add_token_reference(chr);
5460            }
5461            goto DEFINE;
5462        } else {
5463            /*tex Recover possibly with error message. */
5464            tex_back_input(cur_tok);
5465            cur_cs = cs;
5466            chr = tex_scan_toks_normal(0, &tail);
5467        }
5468    } else {
5469        cur_cs = cs;
5470        chr = tex_scan_toks_normal(1, &tail);
5471    }
5472    if (! token_link(chr)) {
5473        tex_put_available_token(chr);
5474        chr = null;
5475    } else if (loc == internal_toks_location(output_routine_code)) {
5476        halfword head = token_link(chr);
5477        halfword list = tex_store_new_token(null, left_brace_token + '{');
5478        tex_store_new_token(tail, right_brace_token + '}');
5479        set_token_link(list, head);
5480        set_token_link(chr, list);
5481    }
5482  DEFINE:
5483    tex_define(a, loc, cmd == internal_toks_cmd ? internal_toks_reference_cmd : register_toks_reference_cmd, chr);
5484}
5485
5486/*tex Let |n| be the largest legal code value, based on |cur_chr| */
5487
5488static void tex_aux_set_define_char_code(int a) /* maybe make |a| already a boolean */
5489{
5490    switch (cur_chr) {
5491        case catcode_charcode:
5492            {
5493                halfword chr = tex_scan_char_number(0);
5494                halfword val = tex_scan_category_code(1);
5495                tex_set_cat_code(cat_code_table_par, chr, val, global_or_local(a));
5496            }
5497            break;
5498        case lccode_charcode:
5499            {
5500                halfword chr = tex_scan_char_number(0);
5501                halfword val = tex_scan_char_number(1);
5502                tex_set_lc_code(chr, val, global_or_local(a));
5503            }
5504            break;
5505        case uccode_charcode:
5506            {
5507                halfword chr = tex_scan_char_number(0);
5508                halfword val = tex_scan_char_number(1);
5509                tex_set_uc_code(chr, val, global_or_local(a));
5510            }
5511            break;
5512        case sfcode_charcode:
5513            {
5514                halfword chr = tex_scan_char_number(0);
5515                halfword val = tex_scan_space_factor(1);
5516                tex_set_sf_code(chr, val, global_or_local(a));
5517            }
5518            break;
5519        case hccode_charcode:
5520            {
5521                halfword chr = tex_scan_char_number(0);
5522                halfword val = tex_scan_char_number(1);
5523                tex_set_hc_code(chr, val, global_or_local(a));
5524            }
5525            break;
5526        case hmcode_charcode:
5527            {
5528                halfword chr = tex_scan_char_number(0);
5529                halfword val = tex_scan_math_discretionary_number(1);
5530                tex_set_hm_code(chr, val, global_or_local(a));
5531            }
5532            break;
5533        case amcode_charcode:
5534            {
5535                halfword chr = tex_scan_char_number(0);
5536                halfword val = tex_scan_category_code(1);
5537                tex_set_am_code(chr, val, global_or_local(a));
5538            }
5539            break;
5540        case cccode_charcode:
5541            {
5542                halfword chr = tex_scan_char_number(0);
5543                halfword val = tex_scan_classification_code(1);
5544                tex_set_cc_code(chr, val, global_or_local(a));
5545            }
5546            break;
5547        case mathcode_charcode:
5548            tex_scan_extdef_math_code(global_or_local(a), tex_mathcode);
5549            break;
5550        case extmathcode_charcode:
5551            tex_scan_extdef_math_code(global_or_local(a), umath_mathcode);
5552            break;
5553        case delcode_charcode:
5554            tex_scan_extdef_del_code(global_or_local(a), tex_mathcode);
5555            break;
5556        case extdelcode_charcode:
5557            tex_scan_extdef_del_code(global_or_local(a), umath_mathcode);
5558            break;
5559        default:
5560            break;
5561    }
5562}
5563
5564static void tex_aux_skip_optional_equal(void)
5565{
5566    do {
5567        tex_get_x_token();
5568    } while (cur_cmd == spacer_cmd);
5569    if (cur_tok == equal_token) {
5570        tex_get_x_token();
5571    }
5572}
5573
5574static void tex_aux_set_math_parameter(int a)
5575{
5576    halfword code = cur_chr;
5577    halfword value = null; /* can also be scaled */
5578    switch (code) {
5579        case math_parameter_reset_spacing:
5580            {
5581                tex_reset_all_styles(global_or_local(a));
5582                return;
5583            }
5584        case math_parameter_set_spacing:
5585        case math_parameter_set_atom_rule:
5586            {
5587                halfword left = tex_scan_math_class_number(0);
5588                halfword right = tex_scan_math_class_number(0);
5589                switch (code) {
5590                    case math_parameter_set_spacing:
5591                        code = tex_to_math_spacing_parameter(left, right);
5592                        break;
5593                    case math_parameter_set_atom_rule:
5594                        code = tex_to_math_rules_parameter(left, right);
5595                        break;
5596                }
5597                if (code < 0) {
5598                    tex_handle_error(
5599                        normal_error_type,
5600                        "Invalid math class pair",
5601                        "I'm going to assume ordinary atoms."
5602                    );
5603                    switch (code) {
5604                        case math_parameter_set_spacing:
5605                            code = tex_to_math_spacing_parameter(ordinary_noad_subtype, ordinary_noad_subtype);
5606                            break;
5607                        case math_parameter_set_atom_rule:
5608                            code = tex_to_math_rules_parameter(ordinary_noad_subtype, ordinary_noad_subtype);
5609                            break;
5610                    }
5611                }
5612                break;
5613            }
5614        case math_parameter_let_spacing:
5615        case math_parameter_let_atom_rule:
5616            {
5617                halfword mathclass = tex_scan_math_class_number(0);
5618                halfword display = tex_scan_math_class_number(1);
5619                halfword text = tex_scan_math_class_number(0);
5620                halfword script = tex_scan_math_class_number(0);
5621                halfword scriptscript = tex_scan_math_class_number(0);
5622                if (valid_math_class_code(mathclass)) {
5623                    switch (code) {
5624                        case math_parameter_let_spacing:
5625                            code = internal_integer_location(first_math_class_code + mathclass);
5626                            break;
5627                        case math_parameter_let_atom_rule:
5628                            code = internal_integer_location(first_math_atom_code + mathclass);
5629                            break;
5630                    }
5631                    value = (display << 24) + (text << 16) + (script << 8) + scriptscript;
5632                 // tex_assign_internal_integer_value(a, code, value);
5633                    tex_word_define(a, code, value);
5634                } else {
5635                    tex_handle_error(
5636                        normal_error_type,
5637                        "Invalid math class",
5638                        "I'm going to ignore this alias."
5639                    );
5640                }
5641                return;
5642            }
5643        case math_parameter_copy_spacing:
5644        case math_parameter_copy_atom_rule:
5645        case math_parameter_copy_parent:
5646            {
5647                halfword mathclass = tex_scan_math_class_number(0);
5648                halfword parent = tex_scan_math_class_number(1);
5649                if (valid_math_class_code(mathclass) && valid_math_class_code(parent)) {
5650                    switch (code) {
5651                        case math_parameter_copy_spacing:
5652                            code = internal_integer_location(first_math_class_code + mathclass);
5653                            value = count_parameter(first_math_class_code + parent);
5654                            break;
5655                        case math_parameter_copy_atom_rule:
5656                            code = internal_integer_location(first_math_atom_code + mathclass);
5657                            value = count_parameter(first_math_atom_code + parent);
5658                            break;
5659                        case math_parameter_copy_parent:
5660                            code = internal_integer_location(first_math_parent_code + mathclass);
5661                            value = count_parameter(first_math_parent_code + parent);
5662                            break;
5663                    }
5664                    tex_word_define(a, code, value);
5665                } else {
5666                    tex_handle_error(
5667                        normal_error_type,
5668                        "Invalid math class",
5669                        "I'm going to ignore this alias."
5670                    );
5671                }
5672                return;
5673            }
5674        case math_parameter_set_pre_penalty:
5675        case math_parameter_set_post_penalty:
5676        case math_parameter_set_display_pre_penalty:
5677        case math_parameter_set_display_post_penalty:
5678            {
5679                halfword mathclass = tex_scan_math_class_number(0);
5680                halfword penalty = tex_scan_integer(1, NULL, NULL);
5681                if (valid_math_class_code(mathclass)) {
5682                    switch (code) {
5683                        case math_parameter_set_pre_penalty:
5684                            code = internal_integer_location(first_math_pre_penalty_code + mathclass);
5685                            break;
5686                        case math_parameter_set_post_penalty:
5687                            code = internal_integer_location(first_math_post_penalty_code + mathclass);
5688                            break;
5689                        case math_parameter_set_display_pre_penalty:
5690                            code = internal_integer_location(first_math_display_pre_penalty_code + mathclass);
5691                            break;
5692                        case math_parameter_set_display_post_penalty:
5693                            code = internal_integer_location(first_math_display_post_penalty_code + mathclass);
5694                            break;
5695                    }
5696                    tex_word_define(a, code, penalty);
5697                 // tex_assign_internal_integer_value(a, code, penalty);
5698                } else {
5699                    tex_handle_error(
5700                        normal_error_type,
5701                        "Invalid math class",
5702                        "I'm going to ignore this atom penalty."
5703                    );
5704                }
5705                return;
5706            }
5707        case math_parameter_let_parent:
5708            {
5709                halfword mathclass = tex_scan_math_class_number(0);
5710                halfword pre = tex_scan_math_class_number(1);
5711                halfword post = tex_scan_math_class_number(0);
5712                halfword options = tex_scan_math_class_number(0);
5713                halfword reserved = tex_scan_math_class_number(0);
5714                if (valid_math_class_code(mathclass)) {
5715                    code = internal_integer_location(first_math_parent_code + mathclass);
5716                    value = (reserved << 24) + (options << 16) + (pre << 8) + post;
5717                    tex_word_define(a, code, value);
5718                 // tex_assign_internal_integer_value(a, code, value);
5719                } else {
5720                    tex_handle_error(
5721                        normal_error_type,
5722                        "Invalid math class",
5723                        "I'm going to ignore this penalty alias."
5724                    );
5725                }
5726                return;
5727            }
5728        case math_parameter_ignore:
5729            {
5730                halfword param = tex_scan_math_parameter();
5731                if (param >= 0) {
5732                    code = internal_integer_location(first_math_ignore_code + param);
5733                    value = tex_scan_integer(1, NULL, NULL);
5734                    tex_word_define(a, code, value);
5735                }
5736                return;
5737            }
5738        case math_parameter_options:
5739            {
5740                halfword mathclass = tex_scan_math_class_number(0);
5741                if (valid_math_class_code(mathclass)) {
5742                    code = internal_integer_location(first_math_options_code + mathclass);
5743                    value = tex_scan_integer(1, NULL, NULL);
5744                    tex_word_define(a, code, value);
5745                 // tex_assign_internal_integer_value(a, code, value);
5746                } else {
5747                    tex_handle_error(
5748                        normal_error_type,
5749                        "Invalid math class",
5750                        "I'm going to ignore these options."
5751                    );
5752                }
5753                return;
5754            }
5755        case math_parameter_set_defaults:
5756            tex_set_default_math_codes();
5757            return;
5758    }
5759    {
5760        halfword style = tex_scan_math_style_identifier(0, 1);
5761        halfword indirect = indirect_math_regular;
5762        int freeze = is_frozen(a) && cur_mode == mmode;
5763        if (! freeze && is_inherited(a)) {
5764            tex_aux_skip_optional_equal();
5765            /* maybe also let inherit from another mathparam but that can become circular */
5766            switch (math_parameter_value_type(code)) {
5767                case math_integer_parameter:
5768                    switch (cur_cmd) {
5769                        case integer_cmd:
5770                            value = cur_cs;
5771                            indirect = indirect_math_integer;
5772                            break;
5773                        case register_integer_cmd:
5774                            value = cur_chr;
5775                            indirect = indirect_math_register_integer;
5776                            break;
5777                    }
5778                    break;
5779                case math_dimension_parameter:
5780                    switch (cur_cmd) {
5781                        case dimension_cmd:
5782                            value = cur_cs;
5783                            indirect = indirect_math_dimension;
5784                            break;
5785                        case register_dimension_cmd:
5786                            value = cur_chr;
5787                            indirect = indirect_math_register_dimension;
5788                            break;
5789                    }
5790                    break;
5791                case math_muglue_parameter:
5792                    switch (cur_cmd) {
5793                        case mugluespec_cmd:
5794                            value = cur_cs;
5795                            indirect = indirect_math_mugluespec;
5796                            break;
5797                        case register_muglue_cmd:
5798                            value = cur_chr;
5799                            indirect = indirect_math_register_mugluespec;
5800                            break;
5801                        case internal_muglue_cmd:
5802                            value = cur_chr;
5803                            indirect = indirect_math_internal_mugluespec;
5804                            break;
5805                        case dimension_cmd:
5806                            value = cur_cs;
5807                            indirect = indirect_math_dimension;
5808                            break;
5809                        case register_dimension_cmd:
5810                            value = cur_chr;
5811                            indirect = indirect_math_register_dimension;
5812                            break;
5813                        case gluespec_cmd:
5814                            value = cur_cs;
5815                            indirect = indirect_math_gluespec;
5816                            break;
5817                        case register_glue_cmd:
5818                            value = cur_chr;
5819                            indirect = indirect_math_register_gluespec;
5820                            break;
5821                        case internal_glue_cmd:
5822                            value = cur_chr;
5823                            indirect = indirect_math_internal_gluespec;
5824                            break;
5825                    }
5826                    break;
5827                case math_pair_parameter:
5828                    {
5829                        halfword left = tex_scan_math_class_number(0);
5830                        halfword right = tex_scan_math_class_number(0);
5831                        value = (left << 16) + right;
5832                    }
5833                    break;
5834            }
5835            if (indirect == indirect_math_regular) {
5836                tex_handle_error(
5837                    normal_error_type,
5838                    "Invalid inherited math parameter type",
5839                    "The inheritance type should match the math parameter type"
5840                );
5841                return;
5842            }
5843        } else {
5844            switch (math_parameter_value_type(code)) {
5845                case math_integer_parameter:
5846                    value = tex_scan_integer(1, NULL, NULL);
5847                    break;
5848                case math_dimension_parameter:
5849                    value = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
5850                    break;
5851                case math_muglue_parameter:
5852                    value = tex_scan_glue(muglue_val_level, 1, 0);
5853                    break;
5854                case math_style_parameter:
5855                    value = tex_scan_integer(1, NULL, NULL);
5856                    if (value < 0 || value > last_math_style_variant) {
5857                        /* maybe a warning */
5858                        value = math_normal_style_variant;
5859                    }
5860                    break;
5861                case math_pair_parameter:
5862                    {
5863                        halfword left = tex_scan_math_class_number(0);
5864                        halfword right = tex_scan_math_class_number(0);
5865                        value = (left << 16) + right;
5866                    }
5867                    break;
5868                default:
5869                    tex_confusion("math parameter type");
5870                    return;
5871            }
5872        }
5873        if (freeze) {
5874            halfword n = tex_new_node(parameter_node, (quarterword) style);
5875            parameter_name(n) = code;
5876            parameter_value(n) = value;
5877            attach_current_attribute_list(n);
5878            tex_tail_append(n);
5879        } else {
5880            switch (style) {
5881                case all_display_styles:
5882                    tex_set_display_styles(code, value, global_or_local(a), indirect);
5883                    break;
5884                case all_text_styles:
5885                    tex_set_text_styles(code, value, global_or_local(a), indirect);
5886                    break;
5887                case all_script_styles:
5888                    tex_set_script_styles(code, value, global_or_local(a), indirect);
5889                    break;
5890                case all_script_script_styles:
5891                    tex_set_script_script_styles(code, value, global_or_local(a), indirect);
5892                    break;
5893                case all_math_styles:
5894                    tex_set_all_styles(code, value, global_or_local(a), indirect);
5895                    break;
5896                case all_main_styles:
5897                    tex_set_main_styles(code, value, global_or_local(a), indirect);
5898                    break;
5899                case all_split_styles:
5900                    tex_set_split_styles(code, value, global_or_local(a), indirect);
5901                    break;
5902                case all_unsplit_styles:
5903                    tex_set_unsplit_styles(code, value, global_or_local(a), indirect);
5904                    break;
5905                case all_uncramped_styles:
5906                    tex_set_uncramped_styles(code, value, global_or_local(a), indirect);
5907                    break;
5908                case all_cramped_styles:
5909                    tex_set_cramped_styles(code, value, global_or_local(a), indirect);
5910                    break;
5911                default:
5912                    tex_def_math_parameter(style, code, value, global_or_local(a), indirect, 0);
5913                    break;
5914            }
5915
5916        }
5917    }
5918}
5919
5920/* */
5921
5922static void tex_aux_set_define_family(int a)
5923{
5924    halfword p = cur_chr;
5925    halfword fnt;
5926    halfword fam = tex_scan_math_family_number();
5927    tex_scan_optional_equals();
5928    fnt = tex_scan_font_identifier(NULL);
5929    tex_def_fam_fnt(fam, p, fnt, global_or_local(a));
5930}
5931
5932/*tex Similar routines are used to assign values to the numeric parameters. */
5933
5934static void tex_aux_set_internal_integer(int a)
5935{
5936    halfword p = cur_chr;
5937    halfword v = tex_scan_integer(1, NULL, NULL);
5938    tex_assign_internal_integer_value(a, p, v);
5939}
5940
5941static void tex_aux_set_register_integer(int a)
5942{
5943    halfword p = cur_chr;
5944    halfword v = tex_scan_integer(1, NULL, NULL);
5945    tex_word_define(a, p, v);
5946}
5947
5948static void tex_aux_set_internal_posit(int a)
5949{
5950    halfword p = cur_chr;
5951    scaled v = tex_scan_posit(1);
5952    tex_assign_internal_integer_value(a, p, v);
5953}
5954
5955static void tex_aux_set_register_posit(int a)
5956{
5957    halfword p = cur_chr;
5958    scaled v = tex_scan_posit(1);
5959    tex_word_define(a, p, v);
5960}
5961
5962static void tex_aux_set_internal_attribute(int a)
5963{
5964    halfword p = cur_chr;
5965    halfword v = tex_scan_integer(1, NULL, NULL);
5966    if (internal_attribute_number(p) > lmt_node_memory_state.max_used_attribute) {
5967        lmt_node_memory_state.max_used_attribute = internal_attribute_number(p);
5968    }
5969    tex_change_attribute_register(a, p, v);
5970    tex_word_define(a, p, v);
5971}
5972
5973static void tex_aux_set_register_attribute(int a)
5974{
5975    halfword p = cur_chr;
5976    halfword v = tex_scan_integer(1, NULL, NULL);
5977    if (register_attribute_number(p) > lmt_node_memory_state.max_used_attribute) {
5978        lmt_node_memory_state.max_used_attribute = register_attribute_number(p);
5979    }
5980    tex_change_attribute_register(a, p, v);
5981    tex_word_define(a, p, v);
5982}
5983
5984static void tex_aux_set_internal_dimension(int a)
5985{
5986    halfword p = cur_chr;
5987    scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
5988    tex_assign_internal_dimension_value(a, p, v);
5989}
5990
5991static void tex_aux_set_register_dimension(int a)
5992{
5993    halfword p = cur_chr;
5994    scaled v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
5995    tex_word_define(a, p, v);
5996}
5997
5998static void tex_aux_set_internal_glue(int a)
5999{
6000    halfword p = cur_chr;
6001    halfword v = tex_scan_glue(glue_val_level, 1, 0);
6002 // define(a, p, internal_glue_ref_cmd, v);
6003    tex_assign_internal_skip_value(a, p, v);
6004}
6005
6006static void tex_aux_set_register_glue(int a)
6007{
6008    halfword p = cur_chr;
6009    halfword v = tex_scan_glue(glue_val_level, 1, 1);
6010    tex_define(a, p, register_glue_reference_cmd, v);
6011}
6012
6013static void tex_aux_set_internal_muglue(int a)
6014{
6015    halfword p = cur_chr;
6016    halfword v = tex_scan_glue(muglue_val_level, 1, 0);
6017    tex_define(a, p, internal_muglue_reference_cmd, v);
6018}
6019
6020static void tex_aux_set_register_muglue(int a)
6021{
6022    halfword p = cur_chr;
6023    halfword v = tex_scan_glue(muglue_val_level, 1, 0);
6024    tex_define(a, p, register_muglue_reference_cmd, v);
6025}
6026
6027/*tex
6028    We ignore prefixes that don't apply as we might apply then in the future: just like |\immediate|
6029    so it's not that alien. And maybe frozen can be applied some day in other cases as well. As
6030    reference we keep the old code (long and outer code has been removed elsewhere.) Most of the
6031    calls are the only call so the functions are likely to be inlined.
6032
6033*/
6034
6035static void tex_aux_set_combine_toks(halfword a)
6036{
6037    if (is_global(a)) {
6038        switch (cur_chr) {
6039            case expanded_toks_code:         cur_chr = global_expanded_toks_code; break;
6040            case append_toks_code:           cur_chr = global_append_toks_code; break;
6041            case append_expanded_toks_code:  cur_chr = global_append_expanded_toks_code; break;
6042            case prepend_toks_code:          cur_chr = global_prepend_toks_code; break;
6043            case prepend_expanded_toks_code: cur_chr = global_prepend_expanded_toks_code; break;
6044        }
6045    }
6046    tex_run_combine_the_toks();
6047}
6048
6049static int tex_aux_set_some_item(void)
6050{
6051    switch (cur_chr) {
6052        case lastpenalty_code:  
6053            lmt_page_builder_state.last_penalty = tex_scan_integer(1, NULL, NULL);
6054            return 1;
6055        case lastkern_code:
6056            lmt_page_builder_state.last_kern = tex_scan_integer(1, NULL, NULL);
6057            return 1;
6058        case lastskip_code:
6059            if (lmt_page_builder_state.last_glue != max_halfword) {
6060                tex_flush_node(lmt_page_builder_state.last_glue);
6061                lmt_page_builder_state.last_glue = tex_scan_glue(glue_val_level, 1, 0);
6062            }
6063            return 1;
6064        case lastboundary_code:
6065            lmt_page_builder_state.last_boundary = tex_scan_integer(1, NULL, NULL);
6066            return 1;
6067        case last_node_type_code:
6068            lmt_page_builder_state.last_node_type = tex_scan_integer(1, NULL, NULL);
6069            return 1;
6070        case last_node_subtype_code:
6071            lmt_page_builder_state.last_node_subtype = tex_scan_integer(1, NULL, NULL);
6072            return 1;
6073        case last_left_class_code:
6074            lmt_math_state.last_left = tex_scan_math_class_number(1);
6075            return 1;
6076        case last_right_class_code:
6077            lmt_math_state.last_right = tex_scan_math_class_number(1);
6078            return 1;
6079        case last_atom_class_code:
6080            lmt_math_state.last_atom = tex_scan_math_class_number(1);
6081            return 1;
6082        default: 
6083            return 0;
6084    }
6085}
6086
6087static void tex_aux_set_constant_register(halfword cmd, halfword cs, halfword flags)
6088{
6089    halfword v = null;
6090    switch(cmd) {
6091        case integer_cmd:
6092            v = tex_scan_integer(1, NULL, NULL);
6093            break;
6094        case dimension_cmd:
6095            v = tex_scan_dimension(0, 0, 0, 1, NULL, NULL);
6096            break;
6097        case posit_cmd:
6098            v = tex_scan_posit(1);
6099            break;
6100        case gluespec_cmd:
6101            v = tex_scan_glue(glue_val_level, 1, 1);
6102            break;
6103        case mugluespec_cmd:
6104            v = tex_scan_glue(muglue_val_level, 1, 0);
6105            break;
6106    }
6107    tex_define(flags, cs, (singleword) cmd, v);
6108}
6109
6110static void tex_run_prefixed_command(void)
6111{
6112    /*tex accumulated prefix codes so far */
6113    int flags = 0;
6114    int force = 0;
6115    halfword lastprefix = -1;
6116    while (cur_cmd == prefix_cmd) {
6117        switch (cur_chr) {
6118            case frozen_code       : flags = add_frozen_flag       (flags); break;
6119            case permanent_code    : flags = add_permanent_flag    (flags); break;
6120            case immutable_code    : flags = add_immutable_flag    (flags); break;
6121            case mutable_code      : flags = add_mutable_flag      (flags); break;
6122            case noaligned_code    : flags = add_noaligned_flag    (flags); break;
6123            case instance_code     : flags = add_instance_flag     (flags); break;
6124            case untraced_code     : flags = add_untraced_flag     (flags); break;
6125            case global_code       : flags = add_global_flag       (flags); break;
6126            case tolerant_code     : flags = add_tolerant_flag     (flags); break;
6127            case protected_code    : flags = add_protected_flag    (flags); break;
6128            case overloaded_code   : flags = add_overloaded_flag   (flags); break;
6129            case aliased_code      : flags = add_aliased_flag      (flags); break;
6130            case immediate_code    : flags = add_immediate_flag    (flags); break;
6131         // case deferred_code     : break;
6132            case semiprotected_code: flags = add_semiprotected_flag(flags); break;            
6133            case always_code       : flags = add_aliased_flag      (flags); force = 1; break; /*tex This one is bound. */
6134            case inherited_code    : flags = add_inherited_flag    (flags); break;
6135            case constant_code     : flags = add_constant_flag     (flags); break;
6136            case retained_code     : flags = add_retained_flag     (flags); break;
6137            case constrained_code  : flags = add_constrained_flag  (flags); break;
6138         // case long_code         : break;  
6139         // case outer_code        : break;
6140            default:
6141                goto PICKUP;
6142        }
6143        lastprefix = cur_chr;
6144      PICKUP:
6145        /*tex We no longer report prefixes. */
6146        do {
6147            tex_get_x_token();
6148        } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd);
6149        if (tracing_commands_par > 2) {
6150            tex_show_cmd_chr(cur_cmd, cur_chr);
6151        }
6152    }
6153
6154    /*tex: Here we can quit when we have a constant! */
6155
6156    /*tex
6157        Adjust for the setting of |\globaldefs|. A negative value removed global, also for the 
6158        |g*| and |x*| primitives. 
6159    */
6160    if (global_defs_par) {
6161        flags = global_defs_par > 0 ? add_global_flag(flags) : remove_global_flag(flags);
6162    }
6163    /*tex
6164        Now we arrived at all the def variants. We only apply the prefixes that make sense (for
6165        now).
6166    */
6167    switch (cur_cmd) {
6168        case some_item_cmd: 
6169            if (! tex_aux_set_some_item()) {
6170                tex_aux_run_illegal_case();
6171            } 
6172            break;
6173        case internal_toks_cmd:
6174        case register_toks_cmd:
6175            tex_aux_set_assign_toks(flags);
6176            break;
6177        case internal_integer_cmd:
6178            tex_aux_set_internal_integer(flags);
6179            break;
6180        case register_integer_cmd:
6181            tex_aux_set_register_integer(flags);
6182            break;
6183        case internal_attribute_cmd:
6184            tex_aux_set_internal_attribute(flags);
6185            break;
6186        case register_attribute_cmd:
6187            tex_aux_set_register_attribute(flags);
6188            break;
6189        case internal_posit_cmd:
6190            tex_aux_set_internal_posit(flags);
6191            break;
6192        case register_posit_cmd:
6193            tex_aux_set_register_posit(flags);
6194            break;
6195        case internal_dimension_cmd:
6196            tex_aux_set_internal_dimension(flags);
6197            break;
6198        case register_dimension_cmd:
6199            tex_aux_set_register_dimension(flags);
6200            break;
6201        case internal_glue_cmd:
6202            tex_aux_set_internal_glue(flags);
6203            break;
6204        case register_glue_cmd:
6205            tex_aux_set_register_glue(flags);
6206            break;
6207        case internal_muglue_cmd:
6208            tex_aux_set_internal_muglue(flags);
6209            break;
6210        case register_muglue_cmd:
6211            tex_aux_set_register_muglue(flags);
6212            break;
6213        case lua_value_cmd:
6214            tex_aux_set_lua_value(flags);
6215            break;
6216        case font_property_cmd:
6217            tex_aux_set_font_property();
6218            break;
6219        case auxiliary_cmd:
6220            tex_aux_set_auxiliary(flags);
6221            break;
6222        case hyphenation_cmd:
6223            tex_aux_set_hyph_data();
6224            break;
6225        case page_property_cmd:
6226            tex_aux_set_page_property();
6227            break;
6228        case box_property_cmd:
6229            tex_aux_set_box_property();
6230            break;
6231        case specification_cmd:
6232            tex_aux_set_specification(flags, cur_chr);
6233            break;
6234        case define_char_code_cmd:
6235            tex_aux_set_define_char_code(flags);
6236            break;
6237        case define_family_cmd:
6238            tex_aux_set_define_family(flags);
6239            break;
6240        case math_parameter_cmd:
6241            tex_aux_set_math_parameter(flags);
6242            break;
6243        case set_font_cmd:
6244            tex_aux_set_font(flags);
6245            break;
6246        case define_font_cmd:
6247            tex_aux_set_define_font(flags);
6248            break;
6249        case integer_cmd:
6250        case posit_cmd:
6251        case dimension_cmd:
6252        case gluespec_cmd:
6253        case mugluespec_cmd:
6254            tex_aux_set_constant_register(cur_cmd, cur_cs, flags);
6255            break;
6256        case index_cmd: 
6257            /*tex  
6258                This one is special because in this usage scenario it is not set but does 
6259                something instead. 
6260            */
6261            tex_inject_parameter(cur_chr); 
6262            break;
6263        case association_cmd:
6264            tex_aux_set_association(flags, force);
6265            break;
6266        case interaction_cmd:
6267            tex_aux_set_interaction(cur_chr);
6268            break;
6269        case register_cmd:
6270            if (cur_chr == token_val_level) {
6271                tex_aux_set_assign_toks(flags);
6272            } else {
6273                tex_aux_set_register(flags);
6274            }
6275            break;
6276        case combine_toks_cmd:
6277            tex_aux_set_combine_toks(flags);
6278            break;
6279        case arithmic_cmd:
6280            tex_aux_arithmic_register(flags, cur_chr);
6281            break;
6282        case let_cmd:
6283            tex_aux_set_let(flags, force);
6284            break;
6285        case shorthand_def_cmd:
6286            tex_aux_set_shorthand_def(flags, force);
6287            break;
6288        case def_cmd:
6289            tex_aux_set_def(flags, force);
6290            break;
6291        case set_box_cmd:
6292            tex_aux_set_box(flags);
6293            break;
6294        default:
6295            if (lastprefix < 0) {
6296                tex_confusion("prefixed command");
6297            } else {
6298                tex_handle_error(
6299                    normal_error_type,
6300                    "You can't use a prefix %C with %C",
6301                    prefix_cmd, lastprefix, cur_cmd, cur_chr,
6302                    "A prefix should be followed by a quantity that can be assigned to. Intermediate\n"
6303                    "spaces and \\relax tokens are gobbled in the process.\n"
6304                );
6305                break;
6306            }
6307    }
6308    /*tex
6309        End of assignments cases. We insert a token saved by |\afterassignment|, if any.
6310    */
6311    tex_aux_finish_after_assignment();
6312}
6313
6314/*tex
6315
6316    When a control sequence is to be defined, by |\def| or |\let| or something similar, the
6317    |get_r_token| routine will substitute a special control sequence for a token that is not
6318    redefinable.
6319
6320*/
6321
6322void tex_get_r_token(void)
6323{
6324  RESTART:
6325    do {
6326        tex_get_token();
6327    } while (cur_tok == space_token);
6328    if (eqtb_invalid_cs(cur_cs)) {
6329        if (cur_cmd == active_char_cmd) {
6330            cur_cs = tex_active_to_cs(cur_chr, 1);
6331            cur_cmd = eq_type(cur_cs);
6332            cur_chr = eq_value(cur_cs);
6333         // tex_x_token();
6334         //  if (! eqtb_invalid_cs(cur_cs)) {
6335            return;
6336         //  }
6337        }
6338        if (cur_cs == 0) {
6339            tex_back_input(cur_tok);
6340        }
6341        cur_tok = deep_frozen_protection_token;
6342        /*tex 
6343            Moved down but this might interfere with input on the console which we don't use 
6344            anyway. 
6345        */
6346        tex_handle_error(
6347            insert_error_type,
6348            "Missing control sequence inserted",
6349            "Please don't say '\\def cs{...}', say '\\def\\cs{...}'. I've inserted an\n"
6350            "inaccessible control sequence so that your definition will be completed without\n"
6351            "mixing me up too badly.\n"
6352        );
6353        goto RESTART;
6354 // } else if (cur_cmd == cs_name_cmd && cur_chr == cs_name_code) { 
6355 //     /*tex 
6356 //         This permits 
6357 //             |\someassignment\csname foo\endcsname| 
6358 //         over 
6359 //             |\expandafter\someassignment\csname foo\endcsname| 
6360 //         but it actually doesn't happen that frequently so even if we measure a gain of some 
6361 //         10\percent on some cases (with millions of iterations) for now I will not enable this
6362 //         feature. Of course it is also incompatible but I don't expect anyone to redefine csname
6363 //         so it is actually a pretty safe extension. Of course it also reduces tracing. I will 
6364 //         come back to it when I suspect a performance gain in \CONTEXT\ (or want my code to look 
6365 //         better).
6366 //     */
6367 //     cur_cs = tex_create_csname();    
6368    }
6369}
6370
6371/*tex
6372    Some of the internal int values need a special treatment. This used to be a more complex
6373    function, also dealing with other registers than didn't really need a check, also because we
6374    now split into internals and registers.
6375
6376    Beware: the post binary and relation penalties are not synchronzed here because we assume a
6377    proper overload of the primitive. They can still be set and their setting is reflected in the
6378    atom panalties but that's all. No need for more code.
6379*/
6380
6381void tex_assign_internal_integer_value(int a, halfword p, int val)
6382{
6383    switch (internal_integer_number(p)) {
6384        case par_direction_code:
6385        case math_direction_code:
6386            {
6387                check_direction_value(val);
6388                tex_word_define(a, p, val);
6389            }
6390            break;
6391        case text_direction_code:
6392            {
6393                check_direction_value(val);
6394                tex_inject_text_or_line_dir(val, 0);
6395                tex_word_define(a, p, val);
6396                /*tex Plus: */
6397                update_tex_internal_dir_state(internal_dir_state_par + 1);
6398            }
6399            break;
6400        case line_direction_code:
6401            {
6402                check_direction_value(val);
6403                tex_inject_text_or_line_dir(val, 1);
6404                p = internal_integer_location(text_direction_code);
6405                tex_word_define(a, p, val);
6406                /*tex Plus: */
6407                update_tex_internal_dir_state(internal_dir_state_par + 1);
6408            }
6409            break;
6410        case cat_code_table_code:
6411            if (tex_valid_catcode_table(val)) {
6412                if (val != cat_code_table_par) {
6413                    tex_word_define(a, p, val);
6414                }
6415            } else {
6416                tex_handle_error(
6417                    normal_error_type,
6418                    "Invalid \\catcode table",
6419                    "You can only switch to a \\catcode table that is initialized using\n"
6420                    "\\savecatcodetable or \\initcatcodetable, or to table 0"
6421                );
6422            }
6423            break;
6424        case glyph_scale_code:
6425        case glyph_x_scale_code:
6426        case glyph_y_scale_code:
6427            /* todo: check for reasonable */
6428            if (val) {
6429                tex_word_define(a, p, val);
6430            } else {
6431                /* maybe an error message */
6432            }
6433            break;
6434     // case glyph_slant_code: 
6435     // case glyph_weight_code: 
6436     //     /* maybe test for maxima */
6437     //     break;
6438        case glyph_text_scale_code:
6439        case glyph_script_scale_code:
6440        case glyph_scriptscript_scale_code:
6441            /* here zero is a signal */
6442            if (val < min_math_style_scale || val > max_math_style_scale) {
6443                tex_handle_error(
6444                    normal_error_type,
6445                    "Invalid \\glyph..scale",
6446                    "The value for \\glyph..scale has to be between " LMT_TOSTRING(min_math_style_scale) " and " LMT_TOSTRING(max_math_style_scale) " where\n"
6447                    "a value of zero forces font percentage scaling to be used."
6448                );
6449                val = max_limited_scale;
6450            }
6451            tex_word_define(a, p, val);
6452            break;
6453        case math_begin_class_code:
6454        case math_end_class_code:
6455        case math_left_class_code:
6456        case math_right_class_code:
6457            if (! valid_math_class_code(val)) {
6458                val = unset_noad_class;
6459            }
6460            tex_word_define(a, p, val);
6461            break;
6462        case output_box_code:
6463            if (val < 0 || val > max_box_index) {
6464                tex_handle_error(
6465                    normal_error_type,
6466                    "Invalid \\outputbox",
6467                    "The value for \\outputbox has to be between 0 and " LMT_TOSTRING(max_box_index) "."
6468                );
6469            } else {
6470                tex_word_define(a, p, val);
6471            }
6472            break;
6473        case new_line_char_code:
6474            if (val > max_newline_character) {
6475                tex_handle_error(
6476                    normal_error_type,
6477                    "Invalid \\newlinechar",
6478                    "The value for \\newlinechar has to be no higher than " LMT_TOSTRING(max_newline_character) ".\n"
6479                    "Your invalid assignment will be ignored."
6480                );
6481            }
6482            else {
6483                tex_word_define(a, p, val);
6484            }
6485            break;
6486        case end_line_char_code:
6487           if (val > max_endline_character) {
6488               tex_handle_error(
6489                   normal_error_type,
6490                   "Invalid \\endlinechar",
6491                   "The value for \\endlinechar has to be no higher than " LMT_TOSTRING(max_endline_character) "."
6492               );
6493           }
6494           else {
6495                tex_word_define(a, p, val);
6496            }
6497            break;
6498        case language_code:
6499            /* this is |\language| */
6500            if (val < 0) {
6501                val = 0;
6502            }
6503            if (tex_is_valid_language(val)) {
6504                update_tex_language(a, val);
6505            }
6506            else {
6507                tex_handle_error(
6508                    normal_error_type,
6509                    "Invalid \\language",
6510                    "The value for \\language has to be defined and in the range 0 .. " LMT_TOSTRING(max_n_of_languages) "."
6511                );
6512            }
6513            break;
6514        case font_code:
6515            if (val < 0) {
6516                val = 0;
6517            }
6518            if (tex_is_valid_font(val)) {
6519                tex_set_cur_font(a, val);
6520            }
6521            else {
6522                tex_handle_error(
6523                    normal_error_type,
6524                    "Invalid \\fontid",
6525                    "The value for \\fontid has to be defined and in the range 0 .. " LMT_TOSTRING(max_n_of_fonts) "."
6526                );
6527            }
6528            break;
6529        case hyphenation_mode_code:
6530            if (val < 0) {
6531                val = 0;
6532            }
6533            /* We don't update |\uchyph| here. */
6534            tex_word_define(a, p, val);
6535            break;
6536        case uc_hyph_code:
6537            /*tex For old times sake. */
6538            tex_word_define(a, p, val);
6539            /*tex But we do use this instead. */
6540            val = val ? set_hyphenation_mode(hyphenation_mode_par, uppercase_hyphenation_mode) : unset_hyphenation_mode(hyphenation_mode_par, uppercase_hyphenation_mode);
6541            tex_word_define(a, internal_integer_location(hyphenation_mode_code), val);
6542            break;
6543        case local_interline_penalty_code:
6544        case local_broken_penalty_code:
6545        case local_tolerance_code:
6546        case local_pre_tolerance_code:
6547            if (cur_mode == hmode) {
6548                /*tex 
6549                    We can check if the last node is a paramrter par node and just patch that 
6550                    one but we have a callback on a new one so for now we just add redundant 
6551                    nodes. It's a bit inefficient when we have local boxes, so if I really start 
6552                    using this feature I might go for efficiency. 
6553                */
6554                tex_word_define(a, p, val);
6555                tex_tail_append(tex_new_par_node(parameter_par_subtype));
6556                update_tex_internal_par_state(internal_par_state_par + 1);
6557            } else { 
6558                /*tex
6559                    Here is an old comment: If we are defining subparagraph penalty levels while 
6560                    we are in hmode, then we put out a whatsit (here a par node) immediately, 
6561                    otherwise we leave it alone. This mechanism might not be sufficiently powerful,
6562                    and some other algorithm, searching down the stack, might be necessary. This 
6563                    is a good first step. Well, in \LUAMETATEX\ we no longer do this and silently 
6564                    ignore this setting. 
6565                */
6566            }
6567            break;
6568        case adjust_spacing_code:
6569            if (val < adjust_spacing_off) {
6570                val = adjust_spacing_off;
6571            }
6572            else if (val > adjust_spacing_font) {
6573                val = adjust_spacing_font;
6574            }
6575            goto DEFINE; /* par property */
6576        case protrude_chars_code:
6577            if (val < protrude_chars_off) {
6578                val = protrude_chars_off;
6579            }
6580            else if (val > protrude_chars_advanced) {
6581                val = protrude_chars_advanced;
6582            }
6583            goto DEFINE; /* par property */
6584        case glyph_options_code:
6585            val &= glyph_option_valid;
6586            tex_word_define(a, p, val);
6587            break;
6588        case discretionary_options_code:
6589            val &= disc_option_valid;
6590            tex_word_define(a, p, val);
6591            break;
6592        case overload_mode_code:
6593         // if (overload_mode_par != 255) {
6594                tex_word_define(a, p, val);
6595         // }
6596            break;
6597        /* We only synchronize these four one way. */
6598        case post_binary_penalty_code:
6599            tex_word_define(a, internal_integer_location(first_math_post_penalty_code + binary_noad_subtype), val);
6600            tex_word_define(a, internal_integer_location(first_math_display_post_penalty_code + binary_noad_subtype), val);
6601            break;
6602        case post_relation_penalty_code:
6603            tex_word_define(a, internal_integer_location(first_math_post_penalty_code + relation_noad_subtype), val);
6604            tex_word_define(a, internal_integer_location(first_math_display_post_penalty_code + relation_noad_subtype), val);
6605            break;
6606        case pre_binary_penalty_code:
6607            tex_word_define(a, internal_integer_location(first_math_pre_penalty_code + binary_noad_subtype), val);
6608            tex_word_define(a, internal_integer_location(first_math_display_pre_penalty_code + binary_noad_subtype), val);
6609            break;
6610        case pre_relation_penalty_code:
6611            tex_word_define(a, internal_integer_location(first_math_pre_penalty_code + relation_noad_subtype), val);
6612            tex_word_define(a, internal_integer_location(first_math_display_pre_penalty_code + relation_noad_subtype), val);
6613            break;
6614        /* We could do this, but then we also need to do day and check it per month. */ /*
6615        case month_code:
6616            if (val < 1) {
6617                val = 1;
6618            } else if (val > 12) {
6619                val = 12;
6620            }
6621            goto DEFINE;
6622        */
6623        case eu_factor_code:
6624            if (val < eu_min_factor) {
6625                val = eu_min_factor;
6626            } else if (val > eu_max_factor) { 
6627                val = eu_max_factor;
6628            }
6629            tex_word_define(a, p, val);
6630            break;
6631        default:
6632          DEFINE:
6633            tex_word_define(a, p, val);
6634            if (is_frozen(a) && cur_mode == hmode) {
6635                tex_update_par_par(internal_integer_cmd, internal_integer_number(p));
6636            }
6637    }
6638}
6639
6640void tex_assign_internal_attribute_value(int a, halfword p, int val)
6641{
6642    if (register_attribute_number(p) > lmt_node_memory_state.max_used_attribute) {
6643        lmt_node_memory_state.max_used_attribute = register_attribute_number(p);
6644    }
6645    tex_change_attribute_register(a, p, val);
6646    tex_word_define(a, p, val);
6647}
6648
6649void tex_assign_internal_posit_value(int a, halfword p, int val)
6650{
6651    tex_word_define(a, p, val);
6652 // if (is_frozen(a) && cur_mode == hmode) {
6653 //     tex_update_par_par(internal_posit_cmd, internal_posit_number(p));
6654 // }
6655}
6656
6657void tex_assign_internal_dimension_value(int a, halfword p, int val)
6658{
6659    tex_word_define(a, p, val);
6660    if (is_frozen(a) && cur_mode == hmode) {
6661        tex_update_par_par(internal_dimension_cmd, internal_dimension_number(p));
6662    }
6663}
6664
6665void tex_assign_internal_skip_value(int a, halfword p, int val)
6666{
6667    switch (internal_glue_number(p)) {
6668        case additional_page_skip_code:
6669            tex_define(a & global_flag_bit, p, internal_glue_reference_cmd, val);
6670            if (cur_mode == vmode) {
6671                tex_additional_page_skip();
6672            }
6673            break;
6674        default:
6675            tex_define(a, p, internal_glue_reference_cmd, val);
6676            break;
6677    }
6678    if (is_frozen(a) && cur_mode == hmode) {
6679        tex_update_par_par(internal_glue_cmd, internal_glue_number(p));
6680    }
6681}
6682
6683/*tex
6684
6685    Here is a procedure that might be called \quotation {Get the next non-blank non-relax non-call
6686    non-assignment token}. It is a runner used in text accents and math alignments. It probably
6687    has to be adapted to the additional command codes that we have.
6688
6689*/
6690
6691void tex_handle_assignments(void)
6692{
6693    while (1) {
6694        do {
6695            tex_get_x_token();
6696        } while (cur_cmd == spacer_cmd || cur_cmd == relax_cmd);
6697        if (cur_cmd <= max_non_prefixed_cmd)  {
6698            return;
6699        } else {
6700            lmt_error_state.set_box_allowed = 0;
6701            tex_run_prefixed_command();
6702            lmt_error_state.set_box_allowed = 1;
6703        }
6704    }
6705}
6706
6707/*tex Has the long |\errmessage| help been used? */
6708
6709static strnumber tex_aux_scan_string(void)
6710{
6711    int saved_selector = lmt_print_state.selector; /*tex holds |selector| setting */
6712    halfword result = tex_scan_toks_expand(0, NULL, 0, 0);
6713 // saved_selector = lmt_print_state.selector;
6714    lmt_print_state.selector = new_string_selector_code;
6715    tex_token_show(result);
6716    tex_flush_token_list(result);
6717    lmt_print_state.selector = saved_selector;
6718    return tex_make_string(); /* todo: we can use take_string instead but happens only @ error */
6719}
6720
6721static void tex_aux_run_message(void)
6722{
6723    switch (cur_chr) {
6724        case message_code:
6725            {
6726                /*tex Print string |s| on the terminal */
6727                strnumber s = tex_aux_scan_string();
6728                if ((lmt_print_state.terminal_offset > 0) || (lmt_print_state.logfile_offset > 0)) {
6729                    tex_print_char(' ');
6730                }
6731                tex_print_tex_str(s);
6732                tex_terminal_update();
6733                tex_flush_str(s);
6734                break;
6735            }
6736        case error_message_code:
6737            {
6738                /*tex
6739                    Print string |s| as an error message. If |\errmessage| occurs often in
6740                    |scroll_mode|, without user-defined |\errhelp|, we don't want to give a long
6741                    help message each time. So we give a verbose explanation only once. These
6742                    help messages are not expanded because that could itself generate an error.
6743                */
6744                strnumber s = tex_aux_scan_string();
6745                if (error_help_par) {
6746                    strnumber helpinfo = tex_tokens_to_string(error_help_par);
6747                    const char *h = tex_to_cstring(helpinfo);
6748                    tex_handle_error(
6749                        normal_error_type,
6750                        "%T",
6751                        s,
6752                        h
6753                    );
6754                    tex_flush_str(helpinfo);
6755                } else if (lmt_error_state.long_help_seen) {
6756                    tex_handle_error(
6757                        normal_error_type,
6758                        "%T",
6759                        s,
6760                        "(That was another \\errmessage.)"
6761                    );
6762                } else {
6763                    if (lmt_error_state.interaction < error_stop_mode) {
6764                        lmt_error_state.long_help_seen = 1;
6765                    }
6766                    tex_handle_error(
6767                        normal_error_type,
6768                        "%T",
6769                        s,
6770                        "This error message was generated by an \\errmessage command, so I can't give any\n"
6771                        "explicit help. Pretend that you're Hercule Poirot: Examine all clues, and deduce\n"
6772                        "the truth by order and method."
6773                    );
6774                }
6775                tex_flush_str(s);
6776                break;
6777            }
6778    }
6779}
6780
6781/*tex
6782
6783    The |\uppercase| and |\lowercase| commands are implemented by building a token list and then
6784    changing the cases of the letters in it.
6785
6786    Change the case of the token in |p|, if a change is appropriate. When the case of a |chr_code|
6787    changes, we don't change the |cmd|. We also change active characters. (The last fact permits
6788    trickery.)
6789
6790*/
6791
6792static void tex_aux_run_case_shift(void)
6793{
6794    tex_run_case_shift(cur_chr);
6795}
6796
6797/*tex
6798
6799    We come finally to the last pieces missing from |main_control|, namely the |\show| commands that
6800    are useful when debugging.
6801
6802*/
6803
6804static void tex_aux_run_show_whatever(void)
6805{
6806    int justshow = 1;
6807    switch (cur_chr) {
6808        case show_code:
6809            /*tex Show the current meaning of a token, then |goto common_ending|. */
6810            {
6811                tex_get_token();
6812                tex_print_nlp();
6813                tex_print_str("> ");
6814                if (cur_cs != 0) {
6815                    tex_print_cs(cur_cs);
6816                    tex_print_char('=');
6817                }
6818                tex_print_meaning(meaning_full_code);
6819                goto COMMON_ENDING;
6820            }
6821        case show_box_code:
6822            /*tex Show the current contents of a box. */
6823            {
6824                int nolevels = 0;
6825                int diagnose = 0;
6826                int content = 0;
6827                int online = 0;
6828                int max = 0;
6829                while (1) {
6830                    switch (tex_scan_character("ocdnaOCDNA", 0, 1, 0)) {
6831                        case 'a': case 'A':
6832                            if (tex_scan_mandate_keyword("all", 1)) {
6833                                max = 1;
6834                            }
6835                            break;
6836                        case 'c': case 'C':
6837                            if (tex_scan_mandate_keyword("content", 1)) {
6838                                content = 1;
6839                            }
6840                            break;
6841                        case 'd': case 'D':
6842                            if (tex_scan_mandate_keyword("diagnose", 1)) {
6843                                diagnose = 1;
6844                            }
6845                            break;
6846                        case 'n': case 'N':
6847                            if (tex_scan_mandate_keyword("nolevels", 1)) {
6848                                nolevels = 1;
6849                            }
6850                            break;
6851                        case 'o': case 'O':
6852                            if (tex_scan_mandate_keyword("online", 1)) {
6853                                online = 1;
6854                            }
6855                            break;
6856                        default:
6857                            goto DONE;
6858                    }
6859                }
6860              DONE:
6861                /*tex This can become a general helper. */
6862                {
6863                    halfword n = tex_scan_box_register_number();
6864                    halfword r = box_register(n);
6865                    halfword l = tracing_levels_par;
6866                    halfword o = tracing_online_par;
6867                    halfword d = show_box_depth_par;
6868                    halfword b = show_box_breadth_par;
6869                    if (nolevels) {
6870                        tracing_levels_par = 0;
6871                    }
6872                    if (online) {
6873                        tracing_online_par = 2;
6874                    }
6875                    if (max) {
6876                        show_box_depth_par = max_integer;
6877                        show_box_breadth_par = max_integer;
6878                    }
6879                    if (diagnose) {
6880                        tex_begin_diagnostic();
6881                    }
6882                    if (! content) {
6883                        tex_print_format("> \\box%i=",n);
6884                    }
6885                    if (r) {
6886                        tex_show_box(r);
6887                    } else {
6888                        tex_print_str("void");
6889                    }
6890                    if (diagnose) {
6891                        tex_end_diagnostic();
6892                    }
6893                    tracing_levels_par = l;
6894                    tracing_online_par = o;
6895                    show_box_depth_par = d;
6896                    show_box_breadth_par = b;
6897                }
6898                break;
6899            }
6900        case show_the_code:
6901            {
6902                halfword head = tex_the_value_toks(the_code, NULL, 0);
6903                tex_print_nlp();
6904                tex_print_str("> ");
6905                tex_show_token_list(head, 0, 0);
6906                tex_flush_token_list(head);
6907                goto COMMON_ENDING;
6908            }
6909       case show_lists_code:
6910            {
6911                tex_begin_diagnostic();
6912                tex_show_activities();
6913                tex_end_diagnostic();
6914                break;
6915            }
6916        case show_groups_code:
6917            {
6918                tex_begin_diagnostic();
6919                tex_show_save_groups();
6920                tex_end_diagnostic();
6921                break;
6922            }
6923        case show_stack_code:
6924            {
6925                tex_begin_diagnostic();
6926                tex_show_save_stack();
6927                tex_end_diagnostic();
6928                break;
6929            }
6930        case show_code_stack_code:
6931            {
6932                tex_begin_diagnostic();
6933                tex_show_code_stack();
6934                tex_end_diagnostic();
6935                break;
6936            }
6937        case show_tokens_code:
6938            {
6939                halfword head = tex_the_detokenized_toks(NULL, 0, 0);
6940                tex_print_nlp();
6941                tex_print_str("> ");
6942                tex_show_token_list(head, 0, 0);
6943                tex_flush_token_list(head);
6944                goto COMMON_ENDING;
6945            }
6946        case show_ifs_code:
6947            {
6948             // if (! justshow) {
6949                tex_begin_diagnostic();
6950             // }
6951                tex_show_ifs();
6952             // if (! justshow) {
6953                tex_end_diagnostic();
6954             // }
6955                break;
6956            }
6957        default:
6958            /* can't happen */
6959            break;
6960    }
6961    if (justshow) {
6962        return;
6963    } else {
6964        /*tex By default we |justshow| now so the next is dead code. */
6965    }
6966    /*tex Complete a potentially long |\show| command: */
6967    tex_handle_error_message_only("OK");
6968    if (lmt_print_state.selector == terminal_and_logfile_selector_code && tracing_online_par <= 0) {
6969        lmt_print_state.selector = terminal_selector_code;
6970        tex_print_str(" (see the transcript file)"); /*tex Here |transcript| means |log|.*/
6971        lmt_print_state.selector = terminal_and_logfile_selector_code;
6972    }
6973  COMMON_ENDING:
6974    if (justshow) {
6975        return;
6976    } else if (lmt_error_state.interaction < error_stop_mode) {
6977        tex_handle_error(
6978            normal_error_type,
6979            NULL, /* no message */
6980            NULL  /* no help */
6981        );
6982        --lmt_error_state.error_count;
6983 /* } else if (tracing_online_par > 0) { */
6984    } else {
6985        tex_handle_error(
6986            normal_error_type,
6987            NULL, /* no message */
6988            "This isn't an error message; I'm just \\showing something.\n"
6989        );
6990    }
6991}
6992
6993/*tex */
6994
6995static inline halfword tex_aux_expand_escaped(halfword tok, halfword *tail)
6996{
6997    halfword value = tex_get_available_token(tok);
6998    if (tail) { 
6999        *tail = value;
7000    }
7001    return value;
7002}
7003
7004/*tex So far |I P G H [N] L R S T X [Z]| are sensitive! Maybe under parameter control? */
7005
7006halfword tex_expand_parameter(halfword tok, halfword *tail)
7007{
7008    halfword value = 0;
7009    switch (tok) { 
7010        /* loop counters */
7011        case I_token_l: case I_token_o: value = lmt_main_control_state.loop_iterator; break; /* iterator */
7012        case P_token_l: case P_token_o: value = tex_previous_loop_iterator_delta(1);  break; /* parent */
7013        case G_token_l: case G_token_o: value = tex_previous_loop_iterator_delta(2);  break; /* grandparent */
7014        /* escape tokens */
7015        case H_token_l: case H_token_o: return tex_aux_expand_escaped(hash_token_o, tail);
7016        case L_token_l: case L_token_o: return tex_aux_expand_escaped(newline_token_o, tail);
7017     // case N_token_l: case N_token_o: return tex_aux_expand_escaped(nbsp_token_o, tail);
7018        case Q_token_l: case Q_token_o: return tex_aux_expand_escaped(double_quote_token_o, tail);
7019        case R_token_l: case R_token_o: return tex_aux_expand_escaped(return_token_o, tail);
7020        case S_token_l: case S_token_o: return tex_aux_expand_escaped(space_token_o, tail);
7021        case T_token_l: case T_token_o: return tex_aux_expand_escaped(tab_token_o, tail);
7022        case X_token_l: case X_token_o: return tex_aux_expand_escaped(backslash_token_o, tail);
7023     // case Z_token_l: case Z_token_o: return tex_aux_expand_escaped(zws_token_o, tail);
7024        /* rest */
7025        default:                        return null;
7026    }
7027    /*tex This could be a helper. */
7028    {
7029        int saved_selector = lmt_print_state.selector;
7030        lmt_print_state.selector = new_string_selector_code;
7031        tex_print_int(value);
7032        lmt_print_state.selector = saved_selector;
7033        return tex_cur_str_toks(tail);
7034    }
7035}
7036
7037halfword tex_expand_iterator(halfword tok)
7038{
7039    switch (tok) { 
7040        case I_token_l: case I_token_o: return lmt_main_control_state.loop_iterator;
7041        case P_token_l: case P_token_o: return tex_previous_loop_iterator_delta(1); 
7042        case G_token_l: case G_token_o: return tex_previous_loop_iterator_delta(2); 
7043        default:                        return 0;
7044    }
7045}
7046
7047static void tex_aux_run_parameter(void)
7048{
7049    tex_get_token();
7050    {
7051        int okay = tex_expand_parameter(cur_tok, NULL);
7052        if (okay) { 
7053            tex_begin_inserted_list(okay);
7054        } else { 
7055            tex_back_input(cur_tok);
7056            tex_aux_run_illegal_case(); 
7057        }
7058    }
7059}
7060
7061static void tex_aux_run_mvl(void)
7062{
7063    switch (cur_chr) {
7064        case begin_mvl_code:
7065            tex_start_mvl(); // todo: use na range specific scanner 
7066            break;
7067        case end_mvl_code:
7068            if (cur_list.mode == hmode) {
7069                tex_aux_run_paragraph_end_hmode();
7070            }
7071            tex_stop_mvl();
7072            break;
7073        default:
7074            break;
7075    }
7076}
7077
7078/*tex
7079
7080    These procedures get things started properly. The initializer sets up the function table. We
7081    have a few aliases to run_functions that are also used otherwise.
7082
7083    We actually only have some 50 cases where there is a difference between the modes and it makes
7084    sense now to combine the handling and move the mode checking to those combined functions. That
7085    way we get a switch no longer a jump. Actually, some already share a function and check for the
7086    mode. On the other hand, this is how \TEX\ does it.
7087
7088    When we have version 2.10 released I might move the mode tests to the runners so that we get a
7089    smaller case cq. jump table and we might also go for mode 1 permanently. A side effect will be
7090    that some commands codes will be collapsed (move and such). See older source for the two 
7091    intermediate variants that were tested for a few years. 
7092
7093*/
7094
7095static inline void tex_aux_big_switch(int mode, int cmd)
7096{
7097    /* todo: order */
7098
7099    switch (cmd) {
7100
7101        case arithmic_cmd: 
7102        case internal_integer_cmd: 
7103        case register_integer_cmd: 
7104        case internal_attribute_cmd: 
7105        case register_attribute_cmd: 
7106        case internal_posit_cmd: 
7107        case register_posit_cmd: 
7108        case internal_dimension_cmd: 
7109        case register_dimension_cmd: 
7110        case font_property_cmd : 
7111        case internal_glue_cmd: 
7112        case register_glue_cmd: 
7113        case internal_muglue_cmd: 
7114        case register_muglue_cmd: 
7115        case internal_toks_cmd: 
7116        case register_toks_cmd: 
7117        case define_char_code_cmd: 
7118        case def_cmd: 
7119        case define_family_cmd: 
7120        case define_font_cmd: 
7121        case hyphenation_cmd: 
7122        case let_cmd: 
7123        case prefix_cmd: 
7124        case register_cmd: 
7125        case auxiliary_cmd: 
7126        case set_box_cmd: 
7127        case box_property_cmd: 
7128        case set_font_cmd: 
7129        case interaction_cmd: 
7130        case math_parameter_cmd: 
7131        case page_property_cmd: 
7132        case specification_cmd: 
7133        case shorthand_def_cmd: 
7134        case association_cmd: 
7135        case lua_value_cmd: 
7136        case integer_cmd: 
7137        case posit_cmd: 
7138        case dimension_cmd: 
7139        case gluespec_cmd: 
7140        case mugluespec_cmd: 
7141        case index_cmd: 
7142        case combine_toks_cmd:
7143        case some_item_cmd:               tex_run_prefixed_command();       break;
7144        case fontspec_cmd:                tex_run_font_spec();              break;
7145        case specificationspec_cmd:       tex_run_specification_spec();     break;
7146        case parameter_cmd:               tex_aux_run_parameter();          break;
7147        case iterator_value_cmd:          tex_aux_run_illegal_case();       break;
7148        case after_something_cmd:         tex_aux_run_after_something();    break;
7149        case begin_group_cmd:             tex_aux_run_begin_group();        break;
7150        case penalty_cmd:                 tex_aux_run_penalty();            break;
7151        case case_shift_cmd:              tex_aux_run_case_shift();         break;
7152        case catcode_table_cmd:           tex_aux_run_catcode_table();      break;
7153        case end_cs_name_cmd:             tex_aux_run_cs_error();           break;
7154        case end_group_cmd:               tex_aux_run_end_group();          break;
7155        case end_local_cmd:               tex_aux_run_end_local();          break;
7156        case ignore_something_cmd:        tex_aux_run_ignore_something();   break;
7157        case insert_cmd:                  tex_run_insert();                 break;
7158        case kern_cmd:                    tex_aux_run_kern();               break;
7159        case leader_cmd:                  tex_aux_run_leader();             break;
7160        case legacy_cmd:                  tex_aux_run_legacy();             break;
7161        case local_box_cmd:               tex_aux_run_local_box();          break;
7162        case lua_protected_call_cmd:      
7163        case lua_semi_protected_call_cmd: tex_aux_run_lua_protected_call(); break;
7164        case lua_function_call_cmd:       tex_aux_run_lua_function_call();  break;
7165        case make_box_cmd:                tex_aux_run_make_box();           break;
7166        case mark_cmd:                    tex_run_mark();                   break;
7167        case message_cmd:                 tex_aux_run_message();            break;
7168        case node_cmd:                    tex_aux_run_node();               break;
7169        case relax_cmd:                   
7170        case ignore_cmd:                  tex_aux_run_relax();              break;
7171        case active_char_cmd:             tex_aux_run_active();             break;
7172        case remove_item_cmd:             tex_aux_run_remove_item();        break;
7173        case right_brace_cmd:             tex_aux_run_right_brace();        break;
7174        case vcenter_cmd:                 tex_run_vcenter();                break;
7175        case xray_cmd:                    tex_aux_run_show_whatever();      break;
7176        case alignment_cmd:               
7177        case alignment_tab_cmd:           tex_run_alignment_error();        break;
7178        case end_template_cmd:            tex_run_alignment_end_template(); break;
7179        case mvl_cmd:                     tex_aux_run_mvl();                break;      
7180        /* */
7181        case math_fraction_cmd:    mode == mmode ? tex_run_math_fraction()         : tex_aux_run_insert_dollar_sign(); break;
7182        case delimiter_number_cmd: mode == mmode ? tex_run_math_delimiter_number() : tex_aux_run_insert_dollar_sign(); break;
7183        case math_fence_cmd:       mode == mmode ? tex_run_math_fence()            : tex_aux_run_insert_dollar_sign(); break;
7184        case math_modifier_cmd:    mode == mmode ? tex_run_math_modifier()         : tex_aux_run_insert_dollar_sign(); break;
7185        case math_accent_cmd:      mode == mmode ? tex_run_math_accent()           : tex_aux_run_insert_dollar_sign(); break;
7186        case math_choice_cmd:      mode == mmode ? tex_run_math_choice()           : tex_aux_run_insert_dollar_sign(); break;
7187        case math_component_cmd:   mode == mmode ? tex_run_math_math_component()   : tex_aux_run_insert_dollar_sign(); break;
7188        case math_style_cmd:       mode == mmode ? tex_run_math_style()            : tex_aux_run_insert_dollar_sign(); break;
7189        case mkern_cmd:            mode == mmode ? tex_aux_run_mkern()             : tex_aux_run_insert_dollar_sign(); break;
7190        case mskip_cmd:            mode == mmode ? tex_aux_run_mglue()             : tex_aux_run_insert_dollar_sign(); break;
7191        case math_radical_cmd:     mode == mmode ? tex_run_math_radical()          : tex_aux_run_insert_dollar_sign(); break;
7192        case subscript_cmd:          
7193        case superscript_cmd:        
7194        case math_script_cmd:      mode == mmode ? tex_run_math_script()           : tex_aux_run_insert_dollar_sign(); break;
7195        case equation_number_cmd:  mode == mmode ? tex_run_math_equation_number()  : tex_aux_run_illegal_case();       break;
7196        case left_brace_cmd:       mode == mmode ? tex_run_math_left_brace()       : tex_aux_run_left_brace();         break;
7197
7198        /* */
7199
7200        case vadjust_cmd:          mode == vmode ? tex_aux_run_illegal_case()  : tex_run_vadjust();           break;
7201        case discretionary_cmd:    mode == vmode ? tex_aux_run_new_paragraph() : tex_aux_run_discretionary(); break;
7202        case explicit_space_cmd:   mode == vmode ? tex_aux_run_new_paragraph() : tex_aux_run_space();         break;
7203        case hmove_cmd:            mode == vmode ? tex_aux_run_move()          : tex_aux_run_illegal_case();  break;
7204        case vmove_cmd:            mode == vmode ? tex_aux_run_illegal_case()  : tex_aux_run_move();          break;    
7205        case hskip_cmd:            mode == vmode ? tex_aux_run_new_paragraph() : tex_aux_run_glue();          break;       
7206        case un_hbox_cmd:          mode == vmode ? tex_aux_run_new_paragraph() : tex_run_unpackage();         break;   
7207
7208        /* */
7209
7210        case math_char_number_cmd:
7211            switch (mode) { 
7212                case vmode: tex_aux_run_math_non_math();     break;
7213                case hmode: tex_run_text_math_char_number(); break;
7214                case mmode: tex_run_math_math_char_number(); break;
7215            } 
7216            break;
7217        case italic_correction_cmd:
7218            switch (mode) { 
7219                case vmode: tex_aux_run_illegal_case();           break;
7220                case hmode: tex_aux_run_text_italic_correction(); break;
7221                case mmode: tex_run_math_italic_correction();     break;
7222            } 
7223            break;
7224        case mathspec_cmd:
7225            switch (mode) { 
7226                case vmode: tex_aux_run_math_non_math(); break;
7227                case hmode: tex_run_text_math_spec();    break;
7228                case mmode: tex_run_math_math_spec();    break;
7229            } 
7230            break;
7231        case char_given_cmd:   
7232        case letter_cmd:    
7233        case other_char_cmd:   
7234            switch (mode) { 
7235                case vmode: tex_aux_run_new_paragraph(); break;
7236                case hmode: tex_aux_run_text_letter();   break;
7237                case mmode: tex_run_math_letter();       break;
7238            } 
7239            break;
7240
7241        case accent_cmd:             
7242            switch (mode) { 
7243                case vmode: tex_aux_run_new_paragraph(); break;    
7244                case hmode: tex_aux_run_text_accent();   break;
7245                case mmode: tex_run_math_accent();       break;
7246            } 
7247            break;
7248        case boundary_cmd:             
7249            switch (mode) { 
7250                case vmode: tex_aux_run_par_boundary();  break;    
7251                case hmode: tex_aux_run_text_boundary(); break;      
7252                case mmode: tex_aux_run_math_boundary(); break;
7253            } 
7254            break;
7255        case char_number_cmd:    
7256            switch (mode) { 
7257                case vmode: tex_aux_run_new_paragraph();    break;     
7258                case hmode: tex_aux_run_text_char_number(); break;    
7259                case mmode: tex_run_math_char_number();     break;
7260            } 
7261            break;
7262        case math_shift_cmd: 
7263        case math_shift_cs_cmd: 
7264            switch (mode) { 
7265                case vmode: tex_aux_run_new_paragraph(); break; 
7266                case hmode: tex_run_math_initialize();   break;   
7267                case mmode: tex_run_math_shift();        break;
7268            } 
7269            break;
7270        case end_paragraph_cmd:      
7271            switch (mode) { 
7272                case vmode: tex_aux_run_paragraph_end_vmode(); break;
7273                case hmode: tex_aux_run_paragraph_end_hmode(); break;
7274                case mmode: tex_aux_run_relax();               break;
7275            } 
7276            break;
7277        case spacer_cmd:             
7278            switch (mode) { 
7279                case vmode: tex_aux_run_relax();      break;
7280                case hmode: tex_aux_run_space();      break;    
7281                case mmode: tex_aux_run_math_space(); break;
7282            } 
7283            break;
7284        case begin_paragraph_cmd:    
7285            switch (mode) { 
7286                case vmode: tex_aux_run_begin_paragraph_vmode(); break;
7287                case hmode: tex_aux_run_begin_paragraph_hmode(); break;
7288                case mmode: tex_aux_run_begin_paragraph_mmode(); break;
7289            } 
7290            break;
7291        case end_job_cmd:  
7292            switch (mode) { 
7293                case vmode: tex_aux_run_end_job();            break;
7294                case hmode: tex_aux_run_head_for_vmode();     break;
7295                case mmode: tex_aux_run_insert_dollar_sign(); break;
7296            } 
7297            break;
7298
7299        case vskip_cmd:              
7300            switch (mode) { 
7301                case vmode: tex_aux_run_glue();               break;   
7302                case hmode: tex_aux_run_head_for_vmode();     break;   
7303                case mmode: tex_aux_run_insert_dollar_sign(); break;
7304            } 
7305            break;
7306        case un_vbox_cmd:            
7307            switch (mode) { 
7308                case vmode: tex_run_unpackage();              break;   
7309                case hmode: tex_aux_run_head_for_vmode();     break;  
7310                case mmode: tex_aux_run_insert_dollar_sign(); break;
7311            } 
7312            break;
7313
7314        case halign_cmd:            
7315            switch (mode) { 
7316                case vmode: tex_run_alignment_initialize(); break;  
7317                case hmode: tex_aux_run_head_for_vmode();   break;   
7318                case mmode: tex_aux_run_halign_mmode();     break;
7319            } 
7320            break;
7321        case valign_cmd:       
7322            switch (mode) { 
7323                case vmode: tex_aux_run_new_paragraph();      break;    
7324                case hmode: tex_run_alignment_initialize();   break;   
7325                case mmode: tex_aux_run_insert_dollar_sign(); break;
7326            } 
7327            break;
7328
7329        case hrule_cmd:      
7330            switch (mode) { 
7331                case vmode: tex_aux_run_hrule();              break;     
7332                case hmode: tex_aux_run_head_for_vmode();     break;    
7333                case mmode: tex_aux_run_insert_dollar_sign(); break;
7334                } 
7335            break;
7336        case vrule_cmd:  
7337            switch (mode) { 
7338                case vmode: tex_aux_run_new_paragraph(); break;
7339                case hmode: tex_aux_run_vrule();         break;   
7340                case mmode: tex_aux_run_mrule();         break;
7341            } 
7342            break;
7343
7344        /* */
7345
7346        default:
7347            /*tex The next is unlikely to happen but compilers like the check. */
7348            tex_confusion("unknown cmd code");
7349            break;
7350    }
7351
7352}
7353
7354/*tex
7355    Some preset values no longer make sense, like family 1 for some math symbols but we keep them
7356    for compatibility reasons. All settings are moved to the relevant modules.
7357*/
7358
7359void tex_initialize_variables(void)
7360{
7361    if (lmt_main_state.run_state == initializing_state) {
7362     /* mag_par = 1000; */
7363        tolerance_par = default_tolerance;
7364        hang_after_par = default_hangafter;
7365        max_dead_cycles_par = default_deadcycles;
7366        math_pre_display_gap_factor_par = default_pre_display_gap;
7367     /* pre_binary_penalty_par = infinite_penalty; */
7368     /* pre_relation_penalty_par = infinite_penalty; */
7369        math_font_control_par = assumed_math_control; 
7370        math_eqno_gap_step_par = default_eqno_gap_step;
7371        px_dimension_par = one_bp;
7372        eu_factor_par = eu_def_factor;
7373        show_node_details_par = 2; /*tex $>1$: |[subtype]| $>2$: |[attributes]| */
7374        ex_hyphen_char_par = '-';
7375        escape_char_par = '\\';
7376        end_line_char_par = '\r';
7377        space_char_par = ' ';
7378        output_box_par = default_output_box;
7379        adjust_spacing_step_par = -1;
7380        adjust_spacing_stretch_par = -1;
7381        adjust_spacing_shrink_par = -1;
7382        math_double_script_mode_par = -1, 
7383        math_glue_mode_par = default_math_glue_mode; 
7384        hyphenation_mode_par = default_hyphenation_mode;
7385        glyph_scale_par = scaling_factor;
7386        glyph_x_scale_par = scaling_factor;
7387        glyph_y_scale_par = scaling_factor;
7388        glyph_x_offset_par = 0;
7389        glyph_y_offset_par = 0;
7390        math_begin_class_par = math_begin_class;
7391        math_end_class_par = math_end_class;
7392        math_left_class_par = unset_noad_class;
7393        math_right_class_par = unset_noad_class;
7394        math_display_penalty_factor_par = scaling_factor;
7395        math_inline_penalty_factor_par = scaling_factor;
7396        pre_inline_penalty_par = max_integer;
7397        post_inline_penalty_par = max_integer;
7398        pre_short_inline_penalty_par = max_integer;
7399        post_short_inline_penalty_par = max_integer;
7400        variable_family_par = -1, 
7401        ignore_depth_criterion_par = ignore_depth;
7402        script_space_before_factor_par = scaling_factor;
7403        script_space_between_factor_par = scaling_factor;
7404        script_space_after_factor_par = scaling_factor;
7405        hbadness_mode_par = badness_mode_all;
7406        vbadness_mode_par = badness_mode_all;
7407        empty_paragraph_mode_par = effective_empty_option_all;
7408        aux_get_date_and_time(&time_par, &day_par, &month_par, &year_par, &lmt_engine_state.utc_time);
7409    }
7410}
7411