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