1% maincontrol.w
2%
3% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20@ @c
21
22
23#include "ptexlib.h"
24#include "lua/luatex-api.h"
25
26@ @c
27#define explicit 1
28#define acc_kern 2
29#define lp_code_base 2
30#define rp_code_base 3
31#define ef_code_base 4
32#define tag_code 5
33#define auto_kern explicit
34#define no_lig_code 6
35
36
37#define prev_depth cur_list.prev_depth_field
38#define space_factor cur_list.space_factor_field
39#define par_shape_ptr  equiv(par_shape_loc)
40
41#define cur_lang int_par(cur_lang_code)
42#define global_defs int_par(global_defs_code)
43#define output_box int_par(output_box_code)
44#define end_line_char int_par(end_line_char_code)
45#define new_line_char int_par(new_line_char_code)
46#define tracing_online int_par(tracing_online_code)
47#define no_local_whatsits int_par(no_local_whatsits_code)
48#define no_local_dirs int_par(no_local_dirs_code)
49#define err_help equiv(err_help_loc)
50#define text_direction int_par(text_direction_code)
51#define every_par equiv(every_par_loc)
52#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code)
53#define par_direction int_par(par_direction_code)
54
55#define pdf_first_line_height  dimen_par(pdf_first_line_height_code)
56#define pdf_last_line_depth    dimen_par(pdf_last_line_depth_code)
57#define pdf_each_line_height   dimen_par(pdf_each_line_height_code)
58#define pdf_each_line_depth    dimen_par(pdf_each_line_depth_code)
59#define page_left_offset dimen_par(page_left_offset_code)
60#define page_top_offset dimen_par(page_top_offset_code)
61#define page_right_offset dimen_par(page_right_offset_code)
62#define page_bottom_offset dimen_par(page_bottom_offset_code)
63#define pdf_h_origin       dimen_par(pdf_h_origin_code)
64#define pdf_v_origin       dimen_par(pdf_v_origin_code)
65#define pdf_px_dimen       dimen_par(pdf_px_dimen_code)
66
67#define pdf_image_resolution   int_par(pdf_image_resolution_code)
68#define escape_char int_par(escape_char_code)
69#define max_dead_cycles int_par(max_dead_cycles_code)
70#define tolerance int_par(tolerance_code)
71#define mag int_par(mag_code)
72#define cat_code_table int_par(cat_code_table_code)
73
74#define par_indent dimen_par(par_indent_code)
75#define looseness int_par(looseness_code)
76#define space_skip glue_par(space_skip_code)
77#define xspace_skip glue_par(xspace_skip_code)
78#define every_vbox equiv(every_vbox_loc)
79
80#define split_top_skip glue_par(split_top_skip_code)
81#define split_max_depth dimen_par(split_max_depth_code)
82
83#define hang_indent dimen_par(hang_indent_code)
84#define hang_after int_par(hang_after_code)
85#define inter_line_penalties_ptr equiv(inter_line_penalties_loc)
86
87#define box(A) eqtb[box_base+(A)].hh.rh
88#define cur_font equiv(cur_font_loc)
89#define hsize dimen_par(hsize_code)
90#define ex_hyphen_char int_par(ex_hyphen_char_code)
91#define floating_penalty int_par(floating_penalty_code)
92
93#define mode          cur_list.mode_field
94#define tail          cur_list.tail_field
95#define head          cur_list.head_field
96#define prev_graf     cur_list.pg_field
97#define dir_save      cur_list.dirs_field
98
99#define check_filter(A) if (!output_active) lua_node_filter_s(buildpage_filter_callback,lua_key_index(A))
100
101#define var_code 7              /* math code meaning ``use the current family'' */
102
103@ We come now to the |main_control| routine, which contains the master
104switch that causes all the various pieces of \TeX\ to do their things,
105in the right order.
106
107In a sense, this is the grand climax of the program: It applies all the
108tools that we have worked so hard to construct. In another sense, this is
109the messiest part of the program: It necessarily refers to other pieces
110of code all over the place, so that a person can't fully understand what is
111going on without paging back and forth to be reminded of conventions that
112are defined elsewhere. We are now at the hub of the web, the central nervous
113system that touches most of the other parts and ties them together.
114@^brain@>
115
116The structure of |main_control| itself is quite simple. There's a label
117called |big_switch|, at which point the next token of input is fetched
118using |get_x_token|. Then the program branches at high speed into one of
119about 100 possible directions, based on the value of the current
120mode and the newly fetched command code; the sum |abs(mode)+cur_cmd|
121indicates what to do next. For example, the case `|vmode+letter|' arises
122when a letter occurs in vertical mode (or internal vertical mode); this
123case leads to instructions that initialize a new paragraph and enter
124horizontal mode.
125
126The big |case| statement that contains this multiway switch has been labeled
127|reswitch|, so that the program can |goto reswitch| when the next token
128has already been fetched. Most of the cases are quite short; they call
129an ``action procedure'' that does the work for that case, and then they
130either |goto reswitch| or they ``fall through'' to the end of the |case|
131statement, which returns control back to |big_switch|. Thus, |main_control|
132is not an extremely large procedure, in spite of the multiplicity of things
133it must do; it is small enough to be handled by PASCAL compilers that put
134severe restrictions on procedure size.
135@!@^action procedure@>
136
137One case is singled out for special treatment, because it accounts for most
138of \TeX's activities in typical applications. The process of reading simple
139text and converting it into |char_node| records, while looking for ligatures
140and kerns, is part of \TeX's ``inner loop''; the whole program runs
141efficiently when its inner loop is fast, so this part has been written
142with particular care.
143
144@c
145static halfword main_p;         /* temporary register for list manipulation */
146static halfword main_s;         /* space factor value */
147
148
149@ We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we
150set it equal to |sf_code(cur_chr)|, except that it should never change
151from a value less than 1000 to a value exceeding 1000. The most common
152case is |sf_code(cur_chr)=1000|, so we want that case to be fast.
153
154@c
155void adjust_space_factor(void)
156{
157    main_s = get_sf_code(cur_chr);
158    if (main_s == 1000) {
159        space_factor = 1000;
160    } else if (main_s < 1000) {
161        if (main_s > 0)
162            space_factor = main_s;
163    } else if (space_factor < 1000) {
164        space_factor = 1000;
165    } else {
166        space_factor = main_s;
167    }
168}
169
170
171@ From Knuth: ``Having |font_glue| allocated for each text font saves
172both time and memory.''  That may be true, but it also punches through
173the API wall for fonts, so I removed that -- Taco. But a bit of caching
174is very welcome, which is why I need to have the next two globals:
175
176@c
177internal_font_number space_spec_font;
178halfword space_spec_cache;
179
180@ To handle the execution state of |main_control|'s eternal loop,
181an extra global variable is used, along with a macro to define
182its values.
183
184@c
185#define goto_next 0
186#define goto_skip_token 1
187#define goto_return 2
188
189static int main_control_state;
190
191
192@* Main control helpers.
193
194Here are all the functions that are called from |main_control| that
195are not already defined elsewhere. For the moment, this list simply
196in the order that the appear in |init_main_control|, below.
197
198@
199@c
200static void run_char_num (void) {
201    scan_char_num();
202    cur_chr = cur_val;
203    adjust_space_factor();
204    tail_append(new_char(cur_font, cur_chr));
205}
206
207static void run_char (void) {
208    adjust_space_factor();
209    tail_append(new_char(cur_font, cur_chr));
210}
211
212@
213The occurrence of blank spaces is almost part of \TeX's inner loop,
214since we usually encounter about one space for every five non-blank characters.
215Therefore |main_control| gives second-highest priority to ordinary spaces.
216
217When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will
218see to it later that the corresponding glue specification is precisely
219|zero_glue|, not merely a pointer to some specification that happens
220to be full of zeroes. Therefore it is simple to test whether a glue parameter
221is zero or~not.
222
223
224@c
225static void run_app_space (void) {
226    if ((abs(mode) + cur_cmd == hmode + spacer_cmd)
227        && (!(space_factor == 1000))) {
228        app_space();
229    } else {
230        /* Append a normal inter-word space to the current list */
231        if (space_skip == zero_glue) {
232            /* Find the glue specification, |main_p|, for
233               text spaces in the current font */
234            if (cur_font != space_spec_font) {
235                if (space_spec_cache != zero_glue)
236                    delete_glue_ref(space_spec_cache);
237                space_spec_cache = new_spec(zero_glue);
238                width(space_spec_cache) = space(cur_font);
239                stretch(space_spec_cache) = space_stretch(cur_font);
240                shrink(space_spec_cache) = space_shrink(cur_font);
241                space_spec_font = cur_font;
242            }
243            main_p = space_spec_cache;
244
245            temp_ptr = new_glue(main_p);
246        } else {
247            temp_ptr = new_param_glue(space_skip_code);
248        }
249        couple_nodes(tail,temp_ptr);
250        tail = temp_ptr;
251
252    }
253}
254
255@ Append a |cancel_boundary_node|
256@c
257static void run_no_boundary (void) {
258    new_whatsit(cancel_boundary_node);
259}
260
261@ @c
262static void run_char_ghost (void) {
263    int t;
264    t = cur_chr;
265    get_x_token();
266    if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd)
267        || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) {
268        halfword p = new_glyph(get_cur_font(), cur_chr);
269        if (t == 0) {
270            set_is_leftghost(p);
271        } else {
272            set_is_rightghost(p);
273        }
274        tail_append(p);
275    }
276}
277
278@ @c
279static void run_relax (void) {
280    return;
281}
282
283@ |ignore_spaces| is a special case: after it has acted, |get_x_token| has already
284fetched the next token from the input, so that operation in |main_control|
285should be skipped.
286
287@c
288static void run_ignore_spaces (void) {
289    if (cur_chr == 0) {
290        /* Get the next non-blank non-call... */
291        do {
292            get_x_token();
293        } while (cur_cmd == spacer_cmd);
294
295        main_control_state = goto_skip_token;
296    } else {
297        int t = scanner_status;
298        scanner_status = normal;
299        get_token_lua();
300        scanner_status = t;
301        cur_cs = prim_lookup(cs_text(cur_cs));
302        if (cur_cs != undefined_primitive) {
303            cur_cmd = get_prim_eq_type(cur_cs);
304            cur_chr = get_prim_equiv(cur_cs);
305            cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr;
306            main_control_state = goto_skip_token;
307        }
308    }
309}
310
311@ |stop| is the second special case. We want |main_control| to return to its caller
312if there is nothing left to do.
313
314@c
315static void run_stop (void) {
316    if (its_all_over())
317       main_control_state= goto_return; /* this is the only way out */
318}
319
320@ @c
321static void run_non_math_math (void) {
322    back_input();
323    new_graf(true);
324}
325
326@ @c
327static void run_math_char_num (void) {
328    mathcodeval mval;           /* to build up an argument to |set_math_char| */
329    if (cur_chr == 0)
330        mval = scan_mathchar(tex_mathcode);
331    else if (cur_chr == 1)
332        mval = scan_mathchar(xetex_mathcode);
333    else
334        mval = scan_mathchar(xetexnum_mathcode);
335    math_char_in_text(mval);
336}
337
338@ @c
339static void run_math_given (void) {
340    mathcodeval mval;           /* to build up an argument to |set_math_char| */
341    mval = mathchar_from_integer(cur_chr, tex_mathcode);
342    math_char_in_text(mval);
343}
344
345static void run_xmath_given (void) {
346    mathcodeval mval;           /* to build up an argument to |set_math_char| */
347    mval = mathchar_from_integer(cur_chr, xetex_mathcode);
348    math_char_in_text(mval);
349}
350
351@  The most important parts of |main_control| are concerned with \TeX's
352chief mission of box-making. We need to control the activities that put
353entries on vlists and hlists, as well as the activities that convert
354those lists into boxes. All of the necessary machinery has already been
355developed; it remains for us to ``push the buttons'' at the right times.
356
357As an introduction to these routines, let's consider one of the simplest
358cases: What happens when `\.{\\hrule}' occurs in vertical mode, or
359`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control|
360is short, since the |scan_rule_spec| routine already does most of what is
361required; thus, there is no need for a special action procedure.
362
363Note that baselineskip calculations are disabled after a rule in vertical
364mode, by setting |prev_depth:=pdf_ignored_dimen|.
365
366@c
367static void run_rule (void) {
368    tail_append(scan_rule_spec());
369    if (abs(mode) == vmode)
370        prev_depth = pdf_ignored_dimen;
371    else if (abs(mode) == hmode)
372        space_factor = 1000;
373}
374
375@
376Many of the actions related to box-making are triggered by the appearance
377of braces in the input. For example, when the user says `\.{\\hbox}
378\.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode,
379the information about the box size (100pt, |exactly|) is put onto |save_stack|
380with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|;
381\TeX\ enters restricted horizontal mode to process the hlist. The right
382brace eventually causes |save_stack| to be restored to its former state,
383at which time the information about the box size (100pt, |exactly|) is
384available once again; a box is packaged and we leave restricted horizontal
385mode, appending the new box to the current list of the enclosing mode
386(in this case to the current list of vertical mode), followed by any
387vertical adjustments that were removed from the box by |hpack|.
388
389The next few sections of the program are therefore concerned with the
390treatment of left and right curly braces.
391
392If a left brace occurs in the middle of a page or paragraph, it simply
393introduces a new level of grouping, and the matching right brace will not have
394such a drastic effect. Such grouping affects neither the mode nor the
395current list.
396
397@c
398static void run_left_brace (void) {
399    new_save_level(simple_group);
400    eq_word_define(int_base + no_local_whatsits_code, 0);
401    eq_word_define(int_base + no_local_dirs_code, 0);
402}
403
404static void run_begin_group (void) {
405    new_save_level(semi_simple_group);
406    eq_word_define(int_base + no_local_whatsits_code, 0);
407    eq_word_define(int_base + no_local_dirs_code, 0);
408}
409
410static void run_end_group (void) {
411    if (cur_group == semi_simple_group) {
412        fixup_directions();
413    } else {
414        off_save();
415    }
416}
417
418@ Constructions that require a box are started by calling |scan_box| with
419a specified context code. The |scan_box| routine verifies
420that a |make_box| command comes next and then it calls |begin_box|.
421
422@c
423static void run_move (void) {
424    int t = cur_chr;
425    scan_normal_dimen();
426    if (t == 0)
427        scan_box(cur_val);
428    else
429        scan_box(-cur_val);
430}
431
432@ @c
433static void run_leader_ship (void) {
434    scan_box(leader_flag - a_leaders + cur_chr);
435}
436
437@ @c
438static void run_make_box (void) {
439    begin_box(0);
440}
441
442@ @c
443static void run_box_dir (void) {
444    scan_register_num();
445    cur_box = box(cur_val);
446    scan_optional_equals();
447    scan_direction();
448    if (cur_box != null)
449        box_dir(cur_box) = cur_val;
450}
451
452@ There is a really small patch to add a new primitive called
453\.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent},
454but in horizontal and math modes it is really a no-op (as opposed to
455\.{\\indent}, which executes the |indent_in_hmode| procedure).
456
457A paragraph begins when horizontal-mode material occurs in vertical mode,
458or when the paragraph is explicitly started by `\.{\\quitvmode}',
459`\.{\\indent}' or `\.{\\noindent}'.
460
461@c
462static void run_start_par_vmode (void) {
463    new_graf((cur_chr > 0));
464}
465
466@ @c
467static void run_start_par (void) {
468   if (cur_chr != 2)
469       indent_in_hmode();
470}
471
472@ @c
473static void run_new_graf (void) {
474   back_input();
475   new_graf(true);
476}
477
478@ A paragraph ends when a |par_end| command is sensed, or when we are in
479horizontal mode when reaching the right brace of vertical-mode routines
480like \.{\\vbox}, \.{\\insert}, or \.{\\output}.
481
482@c
483static void run_par_end_vmode (void) {
484    normal_paragraph();
485    if (mode > 0) {
486        check_filter(vmode_par);
487        build_page();
488    }
489}
490
491@ @c
492static void run_par_end_hmode (void) {
493    if (align_state < 0)
494        off_save();         /* this tries to  recover from an alignment that didn't end properly */
495    end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */
496    if (mode == vmode) {
497        check_filter(hmode_par);
498        build_page();
499    }
500}
501
502@ @c
503static void append_italic_correction_mmode (void) {
504    tail_append(new_kern(0));
505}
506
507@ @c
508static void run_local_box (void) {
509    append_local_box(cur_chr);
510}
511
512@ @c
513static void run_halign_mmode (void) {
514    if (privileged()) {
515        if (cur_group == math_shift_group)
516            init_align();
517        else
518            off_save();
519    }
520}
521
522@ @c
523static void run_eq_no (void) {
524    if (privileged()) {
525        if (cur_group == math_shift_group)
526            start_eq_no();
527        else
528            off_save();
529    }
530}
531
532@ @c
533static void run_letter_mmode (void) {
534   set_math_char(get_math_code(cur_chr));
535}
536
537@ @c
538static void run_char_num_mmode (void) {
539    scan_char_num();
540    cur_chr = cur_val;
541    set_math_char(get_math_code(cur_chr));
542}
543
544@ @c
545static void run_math_char_num_mmode (void) {
546    mathcodeval mval;           /* to build up an argument to |set_math_char| */
547    if (cur_chr == 0)
548        mval = scan_mathchar(tex_mathcode);
549    else if (cur_chr == 1)
550        mval = scan_mathchar(xetex_mathcode);
551    else
552        mval = scan_mathchar(xetexnum_mathcode);
553    set_math_char(mval);
554}
555
556@ @c
557static void run_math_given_mmode (void) {
558    mathcodeval mval;           /* to build up an argument to |set_math_char| */
559    mval = mathchar_from_integer(cur_chr, tex_mathcode);
560    set_math_char(mval);
561}
562
563static void run_xmath_given_mmode (void) {
564    mathcodeval mval;           /* to build up an argument to |set_math_char| */
565    mval = mathchar_from_integer(cur_chr, xetex_mathcode);
566    set_math_char(mval);
567}
568
569@ @c
570static void run_delim_num (void) {
571    mathcodeval mval;           /* to build up an argument to |set_math_char| */
572    if (cur_chr == 0)
573        mval = scan_delimiter_as_mathchar(tex_mathcode);
574    else
575        mval = scan_delimiter_as_mathchar(xetex_mathcode);
576    set_math_char(mval);
577
578}
579
580@ @c
581static void run_vcenter (void) {
582    scan_spec(vcenter_group);
583    normal_paragraph();
584    push_nest();
585    mode = -vmode;
586    prev_depth = pdf_ignored_dimen;
587    if (every_vbox != null)
588        begin_token_list(every_vbox, every_vbox_text);
589}
590
591@ @c
592static void run_math_style (void) {
593    tail_append(new_style((small_number) cur_chr));
594}
595
596@ @c
597static void run_non_script (void) {
598    tail_append(new_glue(zero_glue));
599    subtype(tail) = cond_math_glue;
600}
601
602@ @c
603static void run_math_choice (void) {
604    if (cur_chr == 0)
605        append_choices();
606    else
607        setup_math_style();
608}
609
610@ @c
611static void run_math_shift (void) {
612    if (cur_group == math_shift_group)
613        after_math();
614    else
615        off_save();
616}
617
618@ @c
619static void run_after_assignment (void) {
620    get_token();
621    after_token = cur_tok;
622}
623
624@ @c
625static void run_after_group (void) {
626    get_token();
627    save_for_after(cur_tok);
628}
629
630@ @c
631static void run_extension (void) {
632    do_extension(static_pdf);
633}
634
635
636@ For mode-independent commands, the following macro is useful.
637
638Also, there is a list of cases where the user has probably gotten into or out of math
639mode by mistake. \TeX\ will insert a dollar sign and rescan the current token, and
640it makes sense ot have a macro for that as well.
641
642@c
643#define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B
644#define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B;
645
646
647@ The |main_control| uses a jump table, and |init_main_control| sets that table up.
648@c
649typedef void (*main_control_function) (void);
650main_control_function *jump_table;
651
652static void init_main_control (void) {
653    jump_table = xmalloc((mmode+max_command_cmd+1) * sizeof(main_control_function)) ;
654
655    jump_table[hmode + char_num_cmd] = run_char_num;
656    jump_table[hmode + letter_cmd] = run_char;
657    jump_table[hmode + other_char_cmd] = run_char;
658    jump_table[hmode + char_given_cmd] = run_char;
659    jump_table[hmode + spacer_cmd] = run_app_space;
660    jump_table[hmode + ex_space_cmd] = run_app_space;
661    jump_table[mmode + ex_space_cmd] = run_app_space;
662    jump_table[hmode + no_boundary_cmd] = run_no_boundary;
663    jump_table[hmode + char_ghost_cmd] = run_char_ghost;
664    jump_table[mmode + char_ghost_cmd] = run_char_ghost;
665    any_mode(relax_cmd, run_relax);
666    jump_table[vmode + spacer_cmd] = run_relax;
667    jump_table[mmode + spacer_cmd] = run_relax;
668    jump_table[mmode + no_boundary_cmd] = run_relax;
669    any_mode(ignore_spaces_cmd,run_ignore_spaces);
670    jump_table[vmode + stop_cmd] = run_stop;
671    jump_table[vmode + math_char_num_cmd] = run_non_math_math;
672    jump_table[vmode + math_given_cmd] = run_non_math_math;
673    jump_table[vmode + xmath_given_cmd] = run_non_math_math;
674    jump_table[hmode + math_char_num_cmd] = run_math_char_num;
675    jump_table[hmode + math_given_cmd] = run_math_given;
676    jump_table[hmode + xmath_given_cmd] = run_xmath_given;
677
678    jump_table[vmode + vmove_cmd] = report_illegal_case;
679    jump_table[hmode + hmove_cmd] = report_illegal_case;
680    jump_table[mmode + hmove_cmd] = report_illegal_case;
681    any_mode(last_item_cmd, report_illegal_case);
682    jump_table[vmode + vadjust_cmd] = report_illegal_case;
683    jump_table[vmode + ital_corr_cmd] = report_illegal_case;
684    non_math(eq_no_cmd,report_illegal_case);
685    any_mode(mac_param_cmd,report_illegal_case);
686
687    non_math(sup_mark_cmd, insert_dollar_sign);
688    non_math(sub_mark_cmd, insert_dollar_sign);
689    non_math(super_sub_script_cmd, insert_dollar_sign);
690    non_math(math_comp_cmd, insert_dollar_sign);
691    non_math(delim_num_cmd, insert_dollar_sign);
692    non_math(left_right_cmd, insert_dollar_sign);
693    non_math(above_cmd, insert_dollar_sign);
694    non_math(radical_cmd, insert_dollar_sign);
695    non_math(math_style_cmd, insert_dollar_sign);
696    non_math(math_choice_cmd, insert_dollar_sign);
697    non_math(vcenter_cmd, insert_dollar_sign);
698    non_math(non_script_cmd, insert_dollar_sign);
699    non_math(mkern_cmd, insert_dollar_sign);
700    non_math(limit_switch_cmd, insert_dollar_sign);
701    non_math(mskip_cmd, insert_dollar_sign);
702    non_math(math_accent_cmd, insert_dollar_sign);
703    jump_table[mmode + endv_cmd] =  insert_dollar_sign;
704    jump_table[mmode + par_end_cmd] =  insert_dollar_sign_par_end;
705    jump_table[mmode + stop_cmd] =  insert_dollar_sign;
706    jump_table[mmode + vskip_cmd] =  insert_dollar_sign;
707    jump_table[mmode + un_vbox_cmd] =  insert_dollar_sign;
708    jump_table[mmode + valign_cmd] =  insert_dollar_sign;
709    jump_table[mmode + hrule_cmd] =  insert_dollar_sign;
710    jump_table[vmode + hrule_cmd] = run_rule;
711    jump_table[hmode + vrule_cmd] = run_rule;
712    jump_table[mmode + vrule_cmd] = run_rule;
713    jump_table[vmode + vskip_cmd] = append_glue;
714    jump_table[hmode + hskip_cmd] = append_glue;
715    jump_table[mmode + hskip_cmd] = append_glue;
716    jump_table[mmode + mskip_cmd] = append_glue;
717    any_mode(kern_cmd, append_kern);
718    jump_table[mmode + mkern_cmd] = append_kern;
719    non_math(left_brace_cmd, run_left_brace);
720    any_mode(begin_group_cmd,run_begin_group);
721    any_mode(end_group_cmd, run_end_group);
722    any_mode(right_brace_cmd, handle_right_brace);
723    jump_table[vmode + hmove_cmd] = run_move;
724    jump_table[hmode + vmove_cmd] = run_move;
725    jump_table[mmode + vmove_cmd] = run_move;
726    any_mode(leader_ship_cmd, run_leader_ship);
727    any_mode(make_box_cmd, run_make_box);
728    any_mode(assign_box_dir_cmd, run_box_dir);
729    jump_table[vmode + start_par_cmd] = run_start_par_vmode;
730    jump_table[hmode + start_par_cmd] = run_start_par;
731    jump_table[mmode + start_par_cmd] = run_start_par;
732    jump_table[vmode + letter_cmd] = run_new_graf;
733    jump_table[vmode + other_char_cmd] = run_new_graf;
734    jump_table[vmode + char_num_cmd] = run_new_graf;
735    jump_table[vmode + char_given_cmd] = run_new_graf;
736    jump_table[vmode + char_ghost_cmd] = run_new_graf;
737    jump_table[vmode + math_shift_cmd] = run_new_graf;
738    jump_table[vmode + math_shift_cs_cmd] = run_new_graf;
739    jump_table[vmode + un_hbox_cmd] = run_new_graf;
740    jump_table[vmode + vrule_cmd] = run_new_graf;
741    jump_table[vmode + accent_cmd] = run_new_graf;
742    jump_table[vmode + discretionary_cmd] = run_new_graf;
743    jump_table[vmode + hskip_cmd] = run_new_graf;
744    jump_table[vmode + valign_cmd] = run_new_graf;
745    jump_table[vmode + ex_space_cmd] = run_new_graf;
746    jump_table[vmode + no_boundary_cmd] = run_new_graf;
747    jump_table[vmode + par_end_cmd] = run_par_end_vmode;
748    jump_table[hmode + par_end_cmd] = run_par_end_hmode;
749    jump_table[hmode + stop_cmd] = head_for_vmode;
750    jump_table[hmode + vskip_cmd] = head_for_vmode;
751    jump_table[hmode + hrule_cmd] = head_for_vmode;
752    jump_table[hmode + un_vbox_cmd] = head_for_vmode;
753    jump_table[hmode + halign_cmd] = head_for_vmode;
754    any_mode(insert_cmd,begin_insert_or_adjust);
755    jump_table[hmode + vadjust_cmd] = begin_insert_or_adjust;
756    jump_table[mmode + vadjust_cmd] = begin_insert_or_adjust;
757    any_mode(mark_cmd, handle_mark);
758    any_mode(break_penalty_cmd, append_penalty);
759    any_mode(remove_item_cmd, delete_last);
760    jump_table[vmode + un_vbox_cmd] = unpackage;
761    jump_table[hmode + un_hbox_cmd] = unpackage;
762    jump_table[mmode + un_hbox_cmd] = unpackage;
763    jump_table[hmode + ital_corr_cmd] = append_italic_correction;
764    jump_table[mmode + ital_corr_cmd] = append_italic_correction_mmode;
765    jump_table[hmode + discretionary_cmd] = append_discretionary;
766    jump_table[mmode + discretionary_cmd] = append_discretionary;
767    any_mode(assign_local_box_cmd, run_local_box);
768    jump_table[hmode + accent_cmd] = make_accent;
769    any_mode(car_ret_cmd,align_error);
770    any_mode(tab_mark_cmd,align_error);
771    any_mode(no_align_cmd,no_align_error);
772    any_mode(omit_cmd, omit_error);
773    jump_table[vmode + halign_cmd] = init_align;
774    jump_table[hmode + valign_cmd] = init_align;
775    jump_table[mmode + halign_cmd] = run_halign_mmode;
776    jump_table[vmode + endv_cmd] = do_endv;
777    jump_table[hmode + endv_cmd] = do_endv;
778    any_mode(end_cs_name_cmd, cs_error);
779    jump_table[hmode + math_shift_cmd] = init_math;
780    jump_table[hmode + math_shift_cs_cmd] = init_math;
781    jump_table[mmode + eq_no_cmd] = run_eq_no;
782    jump_table[mmode + left_brace_cmd] = math_left_brace;
783    jump_table[mmode + letter_cmd] = run_letter_mmode;
784    jump_table[mmode + other_char_cmd] = run_letter_mmode;
785    jump_table[mmode + char_given_cmd] = run_letter_mmode;
786    jump_table[mmode + char_num_cmd] = run_char_num_mmode;
787    jump_table[mmode + math_char_num_cmd] = run_math_char_num_mmode;
788    jump_table[mmode + math_given_cmd] = run_math_given_mmode;
789    jump_table[mmode + xmath_given_cmd] = run_xmath_given_mmode;
790    jump_table[mmode + delim_num_cmd] = run_delim_num;
791    jump_table[mmode + math_comp_cmd] = math_math_comp;
792    jump_table[mmode + limit_switch_cmd] = math_limit_switch;
793    jump_table[mmode + radical_cmd] = math_radical;
794    jump_table[mmode + accent_cmd] = math_ac;
795    jump_table[mmode + math_accent_cmd] = math_ac;
796    jump_table[mmode + vcenter_cmd] = run_vcenter;
797    jump_table[mmode + math_style_cmd] = run_math_style;
798    jump_table[mmode + non_script_cmd] = run_non_script;
799    jump_table[mmode + math_choice_cmd] = run_math_choice;
800    jump_table[mmode + above_cmd] = math_fraction;
801    jump_table[mmode + sub_mark_cmd] = sub_sup;
802    jump_table[mmode + sup_mark_cmd] = sub_sup;
803    jump_table[mmode + super_sub_script_cmd] = sub_sup;
804    jump_table[mmode + left_right_cmd] = math_left_right;
805    jump_table[mmode + math_shift_cmd] = run_math_shift;
806    jump_table[mmode + math_shift_cs_cmd] = run_math_shift;
807    any_mode(toks_register_cmd, prefixed_command);
808    any_mode(assign_toks_cmd, prefixed_command);
809    any_mode(assign_int_cmd, prefixed_command);
810    any_mode(assign_attr_cmd, prefixed_command);
811    any_mode(assign_dir_cmd, prefixed_command);
812    any_mode(assign_dimen_cmd, prefixed_command);
813    any_mode(assign_glue_cmd, prefixed_command);
814    any_mode(assign_mu_glue_cmd, prefixed_command);
815    any_mode(assign_font_dimen_cmd, prefixed_command);
816    any_mode(assign_font_int_cmd, prefixed_command);
817    any_mode(set_aux_cmd, prefixed_command);
818    any_mode(set_prev_graf_cmd, prefixed_command);
819    any_mode(set_page_dimen_cmd, prefixed_command);
820    any_mode(set_page_int_cmd, prefixed_command);
821    any_mode(set_box_dimen_cmd, prefixed_command);
822    any_mode(set_tex_shape_cmd, prefixed_command);
823    any_mode(set_etex_shape_cmd, prefixed_command);
824    any_mode(def_char_code_cmd, prefixed_command);
825    any_mode(def_del_code_cmd, prefixed_command);
826    any_mode(extdef_math_code_cmd, prefixed_command);
827    any_mode(extdef_del_code_cmd, prefixed_command);
828    any_mode(def_family_cmd, prefixed_command);
829    any_mode(set_math_param_cmd, prefixed_command);
830    any_mode(set_font_cmd, prefixed_command);
831    any_mode(def_font_cmd, prefixed_command);
832    any_mode(letterspace_font_cmd, prefixed_command);
833    any_mode(pdf_copy_font_cmd, prefixed_command);
834    any_mode(register_cmd, prefixed_command);
835    any_mode(advance_cmd, prefixed_command);
836    any_mode(multiply_cmd, prefixed_command);
837    any_mode(divide_cmd, prefixed_command);
838    any_mode(prefix_cmd, prefixed_command);
839    any_mode(let_cmd, prefixed_command);
840    any_mode(shorthand_def_cmd, prefixed_command);
841    any_mode(read_to_cs_cmd, prefixed_command);
842    any_mode(def_cmd, prefixed_command);
843    any_mode(set_box_cmd, prefixed_command);
844    any_mode(hyph_data_cmd, prefixed_command);
845    any_mode(set_interaction_cmd, prefixed_command);
846    any_mode(after_assignment_cmd,run_after_assignment);
847    any_mode(after_group_cmd,run_after_group);
848    any_mode(in_stream_cmd,open_or_close_in);
849    any_mode(message_cmd,issue_message);
850    any_mode(case_shift_cmd, shift_case);
851    any_mode(xray_cmd, show_whatever);
852    any_mode(extension_cmd, run_extension);
853}
854
855@ And here is |main_control| itself.  It is quite short nowadays.
856
857@c
858void main_control(void)
859{
860    main_control_state = goto_next;
861    init_main_control () ;
862
863    if (equiv(every_job_loc) != null)
864        begin_token_list(equiv(every_job_loc), every_job_text);
865
866    while (1) {
867	if (main_control_state == goto_skip_token)
868            main_control_state = goto_next; /* reset */
869        else
870            get_x_token();
871
872        /* Give diagnostic information, if requested */
873        /* When a new token has just been fetched at |big_switch|, we have an
874           ideal place to monitor \TeX's activity. */
875        if (interrupt != 0 && OK_to_interrupt) {
876            back_input();
877            check_interrupt();
878            continue;
879        }
880        if (int_par(tracing_commands_code) > 0)
881            show_cur_cmd_chr();
882
883        (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */
884
885        if (main_control_state == goto_return) {
886	    return;
887        }
888    }
889    return; /* not reached */
890}
891
892@ @c
893void app_space(void)
894{                               /* handle spaces when |space_factor<>1000| */
895    halfword q;                 /* glue node */
896    if ((space_factor >= 2000) && (xspace_skip != zero_glue)) {
897        q = new_param_glue(xspace_skip_code);
898    } else {
899        if (space_skip != zero_glue) {
900            main_p = new_spec(space_skip);
901        } else {
902            main_p = new_spec(zero_glue);
903            width(main_p) = space(cur_font);
904            stretch(main_p) = space_stretch(cur_font);
905            shrink(main_p) = space_shrink(cur_font);
906        }
907        /* Modify the glue specification in |main_p| according to the space factor */
908        if (space_factor >= 2000)
909            width(main_p) = width(main_p) + extra_space(cur_font);
910        stretch(main_p) = xn_over_d(stretch(main_p), space_factor, 1000);
911        shrink(main_p) = xn_over_d(shrink(main_p), 1000, space_factor);
912
913        q = new_glue(main_p);
914        glue_ref_count(main_p) = null;
915    }
916    couple_nodes(tail, q);
917    tail = q;
918}
919
920@ @c
921void insert_dollar_sign(void)
922{
923    back_input();
924    cur_tok = math_shift_token + '$';
925    print_err("Missing $ inserted");
926    help2("I've inserted a begin-math/end-math symbol since I think",
927          "you left one out. Proceed, with fingers crossed.");
928    ins_error();
929}
930
931@  We can silently ignore  \.{\\par}s in a math formula.
932
933@c
934void insert_dollar_sign_par_end(void)
935{
936    if (!int_par(suppress_mathpar_error_code)) {
937        insert_dollar_sign() ;
938    }
939}
940
941
942
943
944
945@ The `|you_cant|' procedure prints a line saying that the current command
946is illegal in the current mode; it identifies these things symbolically.
947
948@c
949void you_cant(void)
950{
951    print_err("You can't use `");
952    print_cmd_chr((quarterword) cur_cmd, cur_chr);
953    print_in_mode(mode);
954}
955
956@
957When erroneous situations arise, \TeX\ usually issues an error message
958specific to the particular error. For example, `\.{\\noalign}' should
959not appear in any mode, since it is recognized by the |align_peek| routine
960in all of its legitimate appearances; a special error message is given
961when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate
962error message is simply that the user is not allowed to do what he or she
963has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode,
964and `\.{\\lower}' only in non-vertical modes.  Such cases are enumerated
965here and in the other sections referred to under `See also \dots.'
966
967@c
968void report_illegal_case(void)
969{
970    you_cant();
971    help4("Sorry, but I'm not programmed to handle this case;",
972          "I'll just pretend that you didn''t ask for it.",
973          "If you're in the wrong mode, you might be able to",
974          "return to the right one by typing `I}' or `I$' or `I\\par'.");
975    error();
976}
977
978
979@ Some operations are allowed only in privileged modes, i.e., in cases
980that |mode>0|. The |privileged| function is used to detect violations
981of this rule; it issues an error message and returns |false| if the
982current |mode| is negative.
983
984@c
985boolean privileged(void)
986{
987    if (mode > 0) {
988        return true;
989    } else {
990        report_illegal_case();
991        return false;
992    }
993}
994
995
996@ We don't want to leave |main_control| immediately when a |stop| command
997is sensed, because it may be necessary to invoke an \.{\\output} routine
998several times before things really grind to a halt. (The output routine
999might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.)
1000Therefore |its_all_over| is |true| only when the current page
1001and contribution list are empty, and when the last output was not a
1002``dead cycle.''
1003
1004@c
1005boolean its_all_over(void)
1006{                               /* do this when \.{\\end} or \.{\\dump} occurs */
1007    if (privileged()) {
1008        if ((page_head == page_tail) && (head == tail) && (dead_cycles == 0)) {
1009            return true;
1010        }
1011        back_input();           /* we will try to end again after ejecting residual material */
1012        tail_append(new_null_box());
1013        width(tail) = hsize;
1014        tail_append(new_glue(fill_glue));
1015        tail_append(new_penalty(-010000000000));
1016        lua_node_filter_s(buildpage_filter_callback,lua_key_index(end));
1017        build_page();           /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */
1018    }
1019    return false;
1020}
1021
1022
1023@ The |hskip| and |vskip| command codes are used for control sequences
1024like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}.
1025The difference is in the value of |cur_chr|.
1026
1027All the work relating to glue creation has been relegated to the
1028following subroutine. It does not call |build_page|, because it is
1029used in at least one place where that would be a mistake.
1030
1031@c
1032void append_glue(void)
1033{
1034    int s;                      /* modifier of skip command */
1035    s = cur_chr;
1036    switch (s) {
1037    case fil_code:
1038        cur_val = fil_glue;
1039        break;
1040    case fill_code:
1041        cur_val = fill_glue;
1042        break;
1043    case ss_code:
1044        cur_val = ss_glue;
1045        break;
1046    case fil_neg_code:
1047        cur_val = fil_neg_glue;
1048        break;
1049    case skip_code:
1050        scan_glue(glue_val_level);
1051        break;
1052    case mskip_code:
1053        scan_glue(mu_val_level);
1054        break;
1055    }                           /* now |cur_val| points to the glue specification */
1056    tail_append(new_glue(cur_val));
1057    if (s >= skip_code) {
1058        decr(glue_ref_count(cur_val));
1059        if (s > skip_code)
1060            subtype(tail) = mu_glue;
1061    }
1062}
1063
1064@ @c
1065void append_kern(void)
1066{
1067    int s;                      /* |subtype| of the kern node */
1068    s = cur_chr;
1069    scan_dimen((s == mu_glue), false, false);
1070    tail_append(new_kern(cur_val));
1071    subtype(tail) = (quarterword) s;
1072}
1073
1074
1075@ We have to deal with errors in which braces and such things are not
1076properly nested. Sometimes the user makes an error of commission by
1077inserting an extra symbol, but sometimes the user makes an error of omission.
1078\TeX\ can't always tell one from the other, so it makes a guess and tries
1079to avoid getting into a loop.
1080
1081The |off_save| routine is called when the current group code is wrong. It tries
1082to insert something into the user's input that will help clean off
1083the top level.
1084
1085@c
1086void off_save(void)
1087{
1088    halfword p, q;              /* inserted token */
1089    if (cur_group == bottom_level) {
1090        /* Drop current token and complain that it was unmatched */
1091        print_err("Extra ");
1092        print_cmd_chr((quarterword) cur_cmd, cur_chr);
1093        help1("Things are pretty mixed up, but I think the worst is over.");
1094        error();
1095
1096    } else {
1097        back_input();
1098        p = get_avail();
1099        set_token_link(temp_token_head, p);
1100        print_err("Missing ");
1101        /* Prepare to insert a token that matches |cur_group|, and print what it is */
1102        /* At this point, |link(temp_token_head)=p|, a pointer to an empty one-word node. */
1103        switch (cur_group) {
1104        case semi_simple_group:
1105            set_token_info(p, cs_token_flag + frozen_end_group);
1106            tprint_esc("endgroup");
1107            break;
1108        case math_shift_group:
1109            set_token_info(p, math_shift_token + '$');
1110            print_char('$');
1111            break;
1112        case math_left_group:
1113            set_token_info(p, cs_token_flag + frozen_right);
1114            q = get_avail();
1115            set_token_link(p, q);
1116            p = token_link(p);
1117            set_token_info(p, other_token + '.');
1118            tprint_esc("right.");
1119            break;
1120        default:
1121            set_token_info(p, right_brace_token + '}');
1122            print_char('}');
1123            break;
1124        }
1125
1126        tprint(" inserted");
1127        ins_list(token_link(temp_token_head));
1128        help5("I've inserted something that you may have forgotten.",
1129              "(See the <inserted text> above.)",
1130              "With luck, this will get me unwedged. But if you",
1131              "really didn't forget anything, try typing `2' now; then",
1132              "my insertion and my current dilemma will both disappear.");
1133        error();
1134    }
1135}
1136
1137
1138@ The routine for a |right_brace| character branches into many subcases,
1139since a variety of things may happen, depending on |cur_group|. Some
1140types of groups are not supposed to be ended by a right brace; error
1141messages are given in hopes of pinpointing the problem. Most branches
1142of this routine will be filled in later, when we are ready to understand
1143them; meanwhile, we must prepare ourselves to deal with such errors.
1144
1145@c
1146void handle_right_brace(void)
1147{
1148    halfword p, q;              /* for short-term use */
1149    scaled d;                   /* holds |split_max_depth| in |insert_group| */
1150    int f;                      /* holds |floating_penalty| in |insert_group| */
1151    p = null;
1152    switch (cur_group) {
1153    case simple_group:
1154        fixup_directions();
1155        break;
1156    case bottom_level:
1157        print_err("Too many }'s");
1158        help2("You've closed more groups than you opened.",
1159              "Such booboos are generally harmless, so keep going.");
1160        error();
1161        break;
1162    case semi_simple_group:
1163    case math_shift_group:
1164    case math_left_group:
1165        extra_right_brace();
1166        break;
1167    case hbox_group:
1168        /* When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or
1169           \.{\\vtop} construction, the |package| routine comes into action. We might
1170           also have to finish a paragraph that hasn't ended. */
1171        package(0);
1172        break;
1173    case adjusted_hbox_group:
1174        adjust_tail = adjust_head;
1175        pre_adjust_tail = pre_adjust_head;
1176        package(0);
1177        break;
1178    case vbox_group:
1179        end_graf(vbox_group);
1180        package(0);
1181        break;
1182    case vtop_group:
1183        end_graf(vtop_group);
1184        package(vtop_code);
1185        break;
1186    case insert_group:
1187        end_graf(insert_group);
1188        q = split_top_skip;
1189        add_glue_ref(q);
1190        d = split_max_depth;
1191        f = floating_penalty;
1192        unsave();
1193        save_ptr--;
1194        /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */
1195        p = vpack(vlink(head), 0, additional, -1);
1196        pop_nest();
1197        if (saved_type(0) == saved_insert) {
1198            tail_append(new_node(ins_node, saved_value(0)));
1199            height(tail) = height(p) + depth(p);
1200            ins_ptr(tail) = list_ptr(p);
1201            split_top_ptr(tail) = q;
1202            depth(tail) = d;
1203            float_cost(tail) = f;
1204        } else if (saved_type(0) == saved_adjust) {
1205            tail_append(new_node(adjust_node, saved_value(0)));
1206            adjust_ptr(tail) = list_ptr(p);
1207            delete_glue_ref(q);
1208        } else {
1209            confusion("insert_group");
1210        }
1211        list_ptr(p) = null;
1212        flush_node(p);
1213        if (nest_ptr == 0) {
1214            check_filter(insert);
1215            build_page();
1216        }
1217        break;
1218    case output_group:
1219	/* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */
1220	if (dir_level(text_dir_ptr) == cur_level) {
1221	    /* DIR: Remove from |text_dir_ptr| */
1222	    halfword text_dir_tmp = vlink(text_dir_ptr);
1223	    flush_node(text_dir_ptr);
1224	    text_dir_ptr = text_dir_tmp;
1225	}
1226        resume_after_output();
1227        break;
1228    case disc_group:
1229        build_discretionary();
1230        break;
1231    case local_box_group:
1232        build_local_box();
1233        break;
1234    case align_group:
1235        back_input();
1236        cur_tok = cs_token_flag + frozen_cr;
1237        print_err("Missing \\cr inserted");
1238        help1("I'm guessing that you meant to end an alignment here.");
1239        ins_error();
1240        break;
1241    case no_align_group:
1242        end_graf(no_align_group);
1243        unsave();
1244        align_peek();
1245        break;
1246    case vcenter_group:
1247        end_graf(vcenter_group);
1248        finish_vcenter();
1249        break;
1250    case math_choice_group:
1251        build_choices();
1252        break;
1253    case math_group:
1254        close_math_group(p);
1255        break;
1256    default:
1257        confusion("rightbrace");
1258        break;
1259    }
1260}
1261
1262@ @c
1263void extra_right_brace(void)
1264{
1265    print_err("Extra }, or forgotten ");
1266    switch (cur_group) {
1267    case semi_simple_group:
1268        tprint_esc("endgroup");
1269        break;
1270    case math_shift_group:
1271        print_char('$');
1272        break;
1273    case math_left_group:
1274        tprint_esc("right");
1275        break;
1276    }
1277    help5("I've deleted a group-closing symbol because it seems to be",
1278          "spurious, as in `$x}$'. But perhaps the } is legitimate and",
1279          "you forgot something else, as in `\\hbox{$x}'. In such cases",
1280          "the way to recover is to insert both the forgotten and the",
1281          "deleted material, e.g., by typing `I$}'.");
1282    error();
1283    incr(align_state);
1284}
1285
1286
1287@ Here is where we clear the parameters that are supposed to revert to their
1288default values after every paragraph and when internal vertical mode is entered.
1289
1290@c
1291void normal_paragraph(void)
1292{
1293    if (looseness != 0)
1294        eq_word_define(int_base + looseness_code, 0);
1295    if (hang_indent != 0)
1296        eq_word_define(dimen_base + hang_indent_code, 0);
1297    if (hang_after != 1)
1298        eq_word_define(int_base + hang_after_code, 1);
1299    if (par_shape_ptr != null)
1300        eq_define(par_shape_loc, shape_ref_cmd, null);
1301    if (inter_line_penalties_ptr != null)
1302        eq_define(inter_line_penalties_loc, shape_ref_cmd, null);
1303}
1304
1305
1306@ The global variable |cur_box| will point to a newly-made box. If the box
1307is void, we will have |cur_box=null|. Otherwise we will have
1308|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node|
1309case can occur only with leaders.
1310
1311@c
1312halfword cur_box;               /* box to be placed into its context */
1313
1314
1315@ The |box_end| procedure does the right thing with |cur_box|, if
1316|box_context| represents the context as explained above.
1317
1318@c
1319void box_end(int box_context)
1320{
1321    if (box_context < box_flag) {
1322        /* Append box |cur_box| to the current list, shifted by |box_context| */
1323        /*
1324           The global variable |adjust_tail| will be non-null if and only if the
1325           current box might include adjustments that should be appended to the
1326           current vertical list.
1327         */
1328        if (cur_box != null) {
1329            shift_amount(cur_box) = box_context;
1330            if (abs(mode) == vmode) {
1331                if (pre_adjust_tail != null) {
1332                    if (pre_adjust_head != pre_adjust_tail)
1333                        append_list(pre_adjust_head, pre_adjust_tail);
1334                    pre_adjust_tail = null;
1335                }
1336                append_to_vlist(cur_box);
1337                if (adjust_tail != null) {
1338                    if (adjust_head != adjust_tail)
1339                        append_list(adjust_head, adjust_tail);
1340                    adjust_tail = null;
1341                }
1342	        if (mode > 0) {
1343                    check_filter(box);
1344                    build_page();
1345                }
1346            } else {
1347                if (abs(mode) == hmode)
1348                    space_factor = 1000;
1349                else
1350                    cur_box = new_sub_box(cur_box);
1351                couple_nodes(tail, cur_box);
1352                tail = cur_box;
1353            }
1354        }
1355
1356    } else if (box_context < ship_out_flag) {
1357        /* Store |cur_box| in a box register */
1358        if (box_context < global_box_flag)
1359            eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box);
1360        else
1361            geq_define(box_base + box_context - global_box_flag, box_ref_cmd,
1362                       cur_box);
1363
1364    } else if (cur_box != null) {
1365        if (box_context > ship_out_flag) {
1366            /* Append a new leader node that uses |cur_box| */
1367            /* Get the next non-blank non-relax... */
1368            do {
1369                get_x_token();
1370            } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
1371
1372            if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) ||
1373                ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) {
1374                append_glue();
1375                subtype(tail) =
1376                    (quarterword) (box_context - (leader_flag - a_leaders));
1377                leader_ptr(tail) = cur_box;
1378            } else {
1379                print_err("Leaders not followed by proper glue");
1380                help3
1381                    ("You should say `\\leaders <box or rule><hskip or vskip>'.",
1382                     "I found the <box or rule>, but there's no suitable",
1383                     "<hskip or vskip>, so I'm ignoring these leaders.");
1384                back_error();
1385                flush_node_list(cur_box);
1386            }
1387
1388        } else
1389            ship_out(static_pdf, cur_box, SHIPPING_PAGE);
1390    }
1391}
1392
1393@ the next input should specify a box or perhaps a rule
1394
1395@c
1396void scan_box(int box_context)
1397{
1398    /* Get the next non-blank non-relax... */
1399    do {
1400        get_x_token();
1401    } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
1402
1403    if (cur_cmd == make_box_cmd) {
1404        begin_box(box_context);
1405    } else if ((box_context >= leader_flag)
1406               && ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd))) {
1407        cur_box = scan_rule_spec();
1408        box_end(box_context);
1409    } else {
1410        print_err("A <box> was supposed to be here");
1411        help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or",
1412              "something like that. So you might find something missing in",
1413              "your output. But keep trying; you can fix this later.");
1414        back_error();
1415    }
1416}
1417
1418@ @c
1419void new_graf(boolean indented)
1420{
1421    halfword p, q, dir_graf_tmp;
1422    halfword dir_rover;
1423    prev_graf = 0;
1424    if ((mode == vmode) || (head != tail)) {
1425        tail_append(new_param_glue(par_skip_code));
1426    }
1427    push_nest();
1428    mode = hmode;
1429    space_factor = 1000;
1430    /* LOCAL: Add local paragraph node */
1431    tail_append(make_local_par_node());
1432
1433    if (indented) {
1434        p = new_null_box();
1435        box_dir(p) = par_direction;
1436        width(p) = par_indent;
1437        subtype(p) = HLIST_SUBTYPE_INDENT;
1438        q = tail;
1439        tail_append(p);
1440    } else {
1441        q = tail;
1442    }
1443    dir_rover = text_dir_ptr;
1444    while (dir_rover != null) {
1445        if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction)) {
1446            dir_graf_tmp = new_dir(dir_dir(dir_rover));
1447            try_couple_nodes(dir_graf_tmp,vlink(q));
1448            couple_nodes(q,dir_graf_tmp);
1449        }
1450        dir_rover = vlink(dir_rover);
1451    }
1452    q = head;
1453    while (vlink(q) != null)
1454        q = vlink(q);
1455    tail = q;
1456    if (every_par != null)
1457        begin_token_list(every_par, every_par_text);
1458    if (nest_ptr == 1) {
1459        check_filter(new_graf);
1460        build_page();           /* put |par_skip| glue on current page */
1461    }
1462}
1463
1464@ @c
1465void indent_in_hmode(void)
1466{
1467    halfword p;
1468    if (cur_chr > 0) {          /* \.{\\indent} */
1469        p = new_null_box();
1470        width(p) = par_indent;
1471        if (abs(mode) == hmode)
1472            space_factor = 1000;
1473        else
1474            p = new_sub_box(p);
1475        tail_append(p);
1476    }
1477}
1478
1479@ @c
1480void head_for_vmode(void)
1481{
1482    if (mode < 0) {
1483        if (cur_cmd != hrule_cmd) {
1484            off_save();
1485        } else {
1486            print_err("You can't use `\\hrule' here except with leaders");
1487            help2("To put a horizontal rule in an hbox or an alignment,",
1488                  "you should use \\leaders or \\hrulefill (see The TeXbook).");
1489            error();
1490        }
1491    } else {
1492        back_input();
1493        cur_tok = par_token;
1494        back_input();
1495        token_type = inserted;
1496    }
1497}
1498
1499
1500@ TODO (BUG?): |dir_save| would have been set by |line_break| by means
1501of |post_line_break|, but this is not done right now, as it introduces
1502pretty heavy memory leaks. This means the current code is probably
1503wrong in some way that relates to in-paragraph displays.
1504
1505@c
1506void end_graf(int line_break_context)
1507{
1508    if (mode == hmode) {
1509        if ((head == tail) || (vlink(head) == tail)) {
1510            if (vlink(head) == tail)
1511                flush_node(vlink(head));
1512            pop_nest();         /* null paragraphs are ignored, all contain a |local_paragraph| node */
1513        } else {
1514            line_break(false, line_break_context);
1515        }
1516        if (dir_save != null) {
1517            flush_node_list(dir_save);
1518            dir_save = null;
1519        }
1520        normal_paragraph();
1521        error_count = 0;
1522    }
1523}
1524
1525
1526@ @c
1527void begin_insert_or_adjust(void)
1528{
1529    if (cur_cmd != vadjust_cmd) {
1530        scan_register_num();
1531        if (cur_val == output_box) {
1532            print_err("You can't \\insert");
1533            print_int(output_box);
1534            help1("I'm changing to \\insert0; box \\outputbox is special.");
1535            error();
1536            cur_val = 0;
1537        }
1538        set_saved_record(0, saved_insert, 0, cur_val);
1539    } else if (scan_keyword("pre")) {
1540        set_saved_record(0, saved_adjust, 0, 1);
1541    } else {
1542        set_saved_record(0, saved_adjust, 0, 0);
1543    }
1544    save_ptr++;
1545    new_save_level(insert_group);
1546    scan_left_brace();
1547    normal_paragraph();
1548    push_nest();
1549    mode = -vmode;
1550    prev_depth = pdf_ignored_dimen;
1551}
1552
1553
1554@ I (TH)'ve renamed the |make_mark| procedure to this, because if the
1555current chr code is 1, then the actual command was \.{\\clearmarks},
1556which does not generate a mark node but instead destroys the current
1557mark tokenlists.
1558
1559@c
1560void handle_mark(void)
1561{
1562    halfword p;                 /* new node */
1563    halfword c;                 /* the mark class */
1564    if (cur_chr == clear_marks_code) {
1565        scan_mark_num();
1566        c = cur_val;
1567        delete_top_mark(c);
1568        delete_bot_mark(c);
1569        delete_first_mark(c);
1570        delete_split_first_mark(c);
1571        delete_split_bot_mark(c);
1572    } else {
1573        if (cur_chr == 0) {
1574            c = 0;
1575        } else {
1576            scan_mark_num();
1577            c = cur_val;
1578            if (c > biggest_used_mark)
1579                biggest_used_mark = c;
1580        }
1581        p = scan_toks(false, true);
1582        p = new_node(mark_node, 0);     /* the |subtype| is not used */
1583        mark_class(p) = c;
1584        mark_ptr(p) = def_ref;
1585        couple_nodes(tail, p);
1586        tail = p;
1587    }
1588}
1589
1590
1591@ @c
1592void append_penalty(void)
1593{
1594    scan_int();
1595    tail_append(new_penalty(cur_val));
1596    if (mode == vmode) {
1597        check_filter(penalty);
1598        build_page();
1599    }
1600}
1601
1602
1603@ When |delete_last| is called, |cur_chr| is the |type| of node that
1604will be deleted, if present.
1605
1606The |remove_item| command removes a penalty, kern, or glue node if it
1607appears at the tail of the current list, using a brute-force linear scan.
1608Like \.{\\lastbox}, this command is not allowed in vertical mode (except
1609internal vertical mode), since the current list in vertical mode is sent
1610to the page builder.  But if we happen to be able to implement it in
1611vertical mode, we do.
1612
1613@c
1614void delete_last(void)
1615{
1616    halfword p, q;              /* run through the current list */
1617    if ((mode == vmode) && (tail == head)) {
1618        /* Apologize for inability to do the operation now,
1619           unless \.{\\unskip} follows non-glue */
1620        if ((cur_chr != glue_node) || (last_glue != max_halfword)) {
1621            you_cant();
1622            if (cur_chr == kern_node) {
1623                help2
1624                    ("Sorry...I usually can't take things from the current page.",
1625                     "Try `I\\kern-\\lastkern' instead.");
1626            } else if (cur_chr != glue_node) {
1627                help2
1628                    ("Sorry...I usually can't take things from the current page.",
1629                     "Perhaps you can make the output routine do it.");
1630            } else {
1631                help2
1632                    ("Sorry...I usually can't take things from the current page.",
1633                     "Try `I\\vskip-\\lastskip' instead.");
1634            }
1635            error();
1636        }
1637
1638    } else {
1639        /* todo: clean this up */
1640        if (!is_char_node(tail)) {
1641            if (type(tail) == cur_chr) {
1642                q = head;
1643                do {
1644                    p = q;
1645                    if (!is_char_node(q)) {
1646                        if (type(q) == disc_node) {
1647                            if (p == tail)
1648                                return;
1649                        }
1650                    }
1651                    q = vlink(p);
1652                } while (q != tail);
1653                vlink(p) = null;
1654                flush_node_list(tail);
1655                tail = p;
1656            }
1657        }
1658    }
1659}
1660
1661@ @c
1662void unpackage(void)
1663{
1664    halfword p;                 /* the box */
1665    halfword r;                 /* to remove marginal kern nodes */
1666    int c;                      /* should we copy? */
1667    halfword s;                 /* for varmem assignment */
1668    if (cur_chr > copy_code) {
1669        /* Handle saved items and |goto done| */
1670        try_couple_nodes(tail, disc_ptr[cur_chr]);
1671        disc_ptr[cur_chr] = null;
1672        goto DONE;
1673    }
1674    c = cur_chr;
1675    scan_register_num();
1676    p = box(cur_val);
1677    if (p == null)
1678        return;
1679    if ((abs(mode) == mmode)
1680        || ((abs(mode) == vmode) && (type(p) != vlist_node))
1681        || ((abs(mode) == hmode) && (type(p) != hlist_node))) {
1682        print_err("Incompatible list can't be unboxed");
1683        help3("Sorry, Pandora. (You sneaky devil.)",
1684              "I refuse to unbox an \\hbox in vertical mode or vice versa.",
1685              "And I can't open any boxes in math mode.");
1686        error();
1687        return;
1688    }
1689    if (c == copy_code) {
1690        s = copy_node_list(list_ptr(p));
1691        try_couple_nodes(tail,s);
1692    } else {
1693        try_couple_nodes(tail,list_ptr(p));
1694        box(cur_val) = null;
1695        list_ptr(p) = null;
1696        flush_node(p);
1697    }
1698  DONE:
1699    while (vlink(tail) != null) {
1700        r = vlink(tail);
1701        if (!is_char_node(r) && (type(r) == margin_kern_node)) {
1702            try_couple_nodes(tail,vlink(r));
1703            flush_node(r);
1704        }
1705        tail = vlink(tail);
1706    }
1707}
1708
1709@
1710Italic corrections are converted to kern nodes when the |ital_corr| command
1711follows a character. In math mode the same effect is achieved by appending
1712a kern of zero here, since italic corrections are supplied later.
1713
1714@c
1715void append_italic_correction(void)
1716{
1717    halfword p;                 /* |char_node| at the tail of the current list */
1718    internal_font_number f;     /* the font in the |char_node| */
1719    if (tail != head) {
1720        if (is_char_node(tail))
1721            p = tail;
1722        else
1723            return;
1724        f = font(p);
1725        tail_append(new_kern(char_italic(f, character(p))));
1726        subtype(tail) = explicit;
1727    }
1728}
1729
1730
1731@ @c
1732void append_local_box(int kind)
1733{
1734    incr(save_ptr);
1735    set_saved_record(-1, saved_boxtype, 0, kind);
1736    new_save_level(local_box_group);
1737    scan_left_brace();
1738    push_nest();
1739    mode = -hmode;
1740    space_factor = 1000;
1741}
1742
1743
1744@ Discretionary nodes are easy in the common case `\.{\\-}', but in the
1745general case we must process three braces full of items.
1746
1747The space factor does not change when we append a discretionary node,
1748but it starts out as 1000 in the subsidiary lists.
1749
1750@c
1751void append_discretionary(void)
1752{
1753    int c;
1754    tail_append(new_disc());
1755    subtype(tail) = (quarterword) cur_chr;
1756    if (cur_chr == explicit_disc) {
1757        c = get_pre_hyphen_char(cur_lang);
1758        if (c != 0) {
1759            vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c);
1760            alink(vlink(pre_break(tail))) = pre_break(tail);
1761            tlink(pre_break(tail)) = vlink(pre_break(tail));
1762        }
1763        c = get_post_hyphen_char(cur_lang);
1764        if (c != 0) {
1765            vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c);
1766            alink(vlink(post_break(tail))) = post_break(tail);
1767            tlink(post_break(tail)) = vlink(post_break(tail));
1768        }
1769    } else {
1770        incr(save_ptr);
1771        set_saved_record(-1, saved_disc, 0, 0);
1772        new_save_level(disc_group);
1773        scan_left_brace();
1774        push_nest();
1775        mode = -hmode;
1776        space_factor = 1000;
1777    }
1778}
1779
1780@ The test for |p != null| ensures that empty \.{\\localleftbox} and
1781    \.{\\localrightbox} commands are not applied.
1782
1783@c
1784void build_local_box(void)
1785{
1786    halfword p;
1787    int kind;
1788    unsave();
1789    assert(saved_type(-1) == saved_boxtype);
1790    kind = saved_value(-1);
1791    decr(save_ptr);
1792    p = vlink(head);
1793    pop_nest();
1794    if (p != null)
1795        p = hpack(p, 0, additional, -1);
1796    if (kind == 0)
1797        eq_define(local_left_box_base, box_ref_cmd, p);
1798    else
1799        eq_define(local_right_box_base, box_ref_cmd, p);
1800    if (abs(mode) == hmode) {
1801        /* LOCAL: Add local paragraph node */
1802        tail_append(make_local_par_node());
1803    }
1804    eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits + 1);
1805}
1806
1807
1808@ The three discretionary lists are constructed somewhat as if they were
1809hboxes. A~subroutine called |build_discretionary| handles the transitions.
1810(This is sort of fun.)
1811
1812@c
1813void build_discretionary(void)
1814{
1815    halfword p, q;              /* for link manipulation */
1816    int n;                      /* length of discretionary list */
1817    unsave();
1818    /* Prune the current list, if necessary, until it contains only
1819       |char_node|, |kern_node|, |hlist_node|, |vlist_node| and
1820       |rule_node| items; set |n| to the length of the list,
1821       and set |q| to the lists tail */
1822    /* During this loop, |p=vlink(q)| and there are |n| items preceding |p|. */
1823    q = head;
1824    p = vlink(q);
1825    n = 0;
1826    while (p != null) {
1827        if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) {
1828            print_err("Improper discretionary list");
1829            help1("Discretionary lists must contain only boxes and kerns.");
1830            error();
1831            begin_diagnostic();
1832            tprint_nl("The following discretionary sublist has been deleted:");
1833            show_box(p);
1834            end_diagnostic(true);
1835            flush_node_list(p);
1836            vlink(q) = null;
1837            break;
1838        }
1839        alink(p) = q;
1840        q = p;
1841        p = vlink(q);
1842        incr(n);
1843    }
1844
1845    p = vlink(head);
1846    pop_nest();
1847    assert(saved_type(-1) == saved_disc);
1848    switch (saved_value(-1)) {
1849    case 0:
1850        if (n > 0) {
1851            vlink(pre_break(tail)) = p;
1852            alink(p) = pre_break(tail);
1853            tlink(pre_break(tail)) = q;
1854        }
1855        break;
1856    case 1:
1857        if (n > 0) {
1858            vlink(post_break(tail)) = p;
1859            alink(p) = post_break(tail);
1860            tlink(post_break(tail)) = q;
1861        }
1862        break;
1863    case 2:
1864        /* Attach list |p| to the current list, and record its length;
1865           then finish up and |return| */
1866        if ((n > 0) && (abs(mode) == mmode)) {
1867            print_err("Illegal math \\discretionary");
1868            help2("Sorry: The third part of a discretionary break must be",
1869                  "empty, in math formulas. I had to delete your third part.");
1870            flush_node_list(p);
1871            error();
1872        } else {
1873            if (n > 0) {
1874                vlink(no_break(tail)) = p;
1875                alink(p) = no_break(tail);
1876                tlink(no_break(tail)) = q;
1877            }
1878        }
1879        decr(save_ptr);
1880        return;
1881        break;
1882    }                           /* there are no other cases */
1883    set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1));
1884    new_save_level(disc_group);
1885    scan_left_brace();
1886    push_nest();
1887    mode = -hmode;
1888    space_factor = 1000;
1889}
1890
1891
1892@ The positioning of accents is straightforward but tedious. Given an accent
1893of width |a|, designed for characters of height |x| and slant |s|;
1894and given a character of width |w|, height |h|, and slant |t|: We will shift
1895the accent down by |x-h|, and we will insert kern nodes that have the effect of
1896centering the accent over the character and shifting the accent to the
1897right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$.  If either character is
1898absent from the font, we will simply use the other, without shifting.
1899
1900@c
1901void make_accent(void)
1902{
1903    double s, t;                /* amount of slant */
1904    halfword p, q, r;           /* character, box, and kern nodes */
1905    internal_font_number f;     /* relevant font */
1906    scaled a, h, x, w, delta;   /* heights and widths, as explained above */
1907    scan_char_num();
1908    f = equiv(cur_font_loc);
1909    p = new_glyph(f, cur_val);
1910    if (p != null) {
1911        x = x_height(f);
1912        s = float_cast(slant(f)) / float_constant(65536);       /* real division */
1913        a = glyph_width(p);
1914        do_assignments();
1915        /* Create a character node |q| for the next character,
1916           but set |q:=null| if problems arise */
1917        q = null;
1918        f = equiv(cur_font_loc);
1919        if ((cur_cmd == letter_cmd) ||
1920            (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) {
1921            q = new_glyph(f, cur_chr);
1922        } else if (cur_cmd == char_num_cmd) {
1923            scan_char_num();
1924            q = new_glyph(f, cur_val);
1925        } else {
1926            back_input();
1927        }
1928
1929        if (q != null) {
1930            /* Append the accent with appropriate kerns, then set |p:=q| */
1931            /* The kern nodes appended here must be distinguished from other kerns, lest
1932               they be wiped away by the hyphenation algorithm or by a previous line break.
1933
1934               The two kerns are computed with (machine-dependent) |real| arithmetic, but
1935               their sum is machine-independent; the net effect is machine-independent,
1936               because the user cannot remove these nodes nor access them via \.{\\lastkern}.
1937             */
1938            t = float_cast(slant(f)) / float_constant(65536);   /* real division */
1939            w = glyph_width(q);
1940            h = glyph_height(q);
1941            if (h != x) {       /* the accent must be shifted up or down */
1942                p = hpack(p, 0, additional, -1);
1943                shift_amount(p) = x - h;
1944            }
1945            delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s);       /* real multiplication */
1946            r = new_kern(delta);
1947            subtype(r) = acc_kern;
1948            couple_nodes(tail, r);
1949            couple_nodes(r, p);
1950            tail = new_kern(-a - delta);
1951            subtype(tail) = acc_kern;
1952            couple_nodes(p, tail);
1953            p = q;
1954
1955        }
1956        couple_nodes(tail, p);
1957        tail = p;
1958        space_factor = 1000;
1959    }
1960}
1961
1962
1963@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner
1964into |main_control|, it might be that the user has foolishly inserted
1965one of them into something that has nothing to do with alignment. But it is
1966far more likely that a left brace or right brace has been omitted, since
1967|get_next| takes actions appropriate to alignment only when `\.{\\cr}'
1968or `\.{\\span}' or tab marks occur with |align_state=0|. The following
1969program attempts to make an appropriate recovery.
1970
1971@c
1972void align_error(void)
1973{
1974    if (abs(align_state) > 2) {
1975        /* Express consternation over the fact that no alignment is in progress */
1976        print_err("Misplaced ");
1977        print_cmd_chr((quarterword) cur_cmd, cur_chr);
1978        if (cur_tok == tab_token + '&') {
1979            help6("I can't figure out why you would want to use a tab mark",
1980                  "here. If you just want an ampersand, the remedy is",
1981                  "simple: Just type `I\\&' now. But if some right brace",
1982                  "up above has ended a previous alignment prematurely,",
1983                  "you're probably due for more error messages, and you",
1984                  "might try typing `S' now just to see what is salvageable.");
1985        } else {
1986            help5("I can't figure out why you would want to use a tab mark",
1987                  "or \\cr or \\span just now. If something like a right brace",
1988                  "up above has ended a previous alignment prematurely,",
1989                  "you're probably due for more error messages, and you",
1990                  "might try typing `S' now just to see what is salvageable.");
1991        }
1992        error();
1993
1994    } else {
1995        back_input();
1996        if (align_state < 0) {
1997            print_err("Missing { inserted");
1998            incr(align_state);
1999            cur_tok = left_brace_token + '{';
2000        } else {
2001            print_err("Missing } inserted");
2002            decr(align_state);
2003            cur_tok = right_brace_token + '}';
2004        }
2005        help3("I've put in what seems to be necessary to fix",
2006              "the current column of the current alignment.",
2007              "Try to go on, since this might almost work.");
2008        ins_error();
2009    }
2010}
2011
2012
2013@ The help messages here contain a little white lie, since \.{\\noalign}
2014and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
2015
2016@c
2017void no_align_error(void)
2018{
2019    print_err("Misplaced \\noalign");
2020    help2("I expect to see \\noalign only after the \\cr of",
2021          "an alignment. Proceed, and I'll ignore this case.");
2022    error();
2023}
2024
2025void omit_error(void)
2026{
2027    print_err("Misplaced \\omit");
2028    help2("I expect to see \\omit only after tab marks or the \\cr of",
2029          "an alignment. Proceed, and I'll ignore this case.");
2030    error();
2031}
2032
2033
2034@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}.
2035Let's take a look at what happens when they are used correctly.
2036
2037An |align_group| code is supposed to remain on the |save_stack|
2038during an entire alignment, until |fin_align| removes it.
2039
2040A devious user might force an |endv| command to occur just about anywhere;
2041we must defeat such hacks.
2042
2043@c
2044void do_endv(void)
2045{
2046    base_ptr = input_ptr;
2047    input_stack[base_ptr] = cur_input;
2048    while ((input_stack[base_ptr].index_field != v_template) &&
2049           (input_stack[base_ptr].loc_field == null) &&
2050           (input_stack[base_ptr].state_field == token_list))
2051        decr(base_ptr);
2052    if ((input_stack[base_ptr].index_field != v_template) ||
2053        (input_stack[base_ptr].loc_field != null) ||
2054        (input_stack[base_ptr].state_field != token_list))
2055        fatal_error("(interwoven alignment preambles are not allowed)");
2056    /*.interwoven alignment preambles... */
2057    if (cur_group == align_group) {
2058        end_graf(align_group);
2059        if (fin_col())
2060            fin_row();
2061    } else {
2062        off_save();
2063    }
2064}
2065
2066@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
2067
2068@c
2069void cs_error(void)
2070{
2071    print_err("Extra \\endcsname");
2072    help1("I'm ignoring this, since I wasn't doing a \\csname.");
2073    error();
2074}
2075
2076
2077@
2078  Assignments to values in |eqtb| can be global or local. Furthermore, a
2079  control sequence can be defined to be `\.{\\long}', `\.{\\protected}',
2080  or `\.{\\outer}', and it might or might not be expanded. The prefixes
2081  `\.{\\global}', `\.{\\long}', `\.{\\protected}',
2082  and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric
2083  codes, making it possible to accumulate the union of all specified prefixes
2084  by adding the corresponding codes.  (PASCAL's |set| operations could also
2085  have been used.)
2086
2087  Every prefix, and every command code that might or might not be prefixed,
2088  calls the action procedure |prefixed_command|. This routine accumulates
2089  a sequence of prefixes until coming to a non-prefix, then it carries out
2090  the command.
2091
2092
2093
2094@ If the user says, e.g., `\.{\\global\\global}', the redundancy is
2095silently accepted.
2096
2097
2098@ The different types of code values have different legal ranges; the
2099following program is careful to check each case properly.
2100
2101@c
2102#define check_def_code(A) do {						\
2103	if (((cur_val<0)&&(p<(A)))||(cur_val>n)) {			\
2104	    print_err("Invalid code (");				\
2105	    print_int(cur_val);						\
2106	    if (p<(A))							\
2107		tprint("), should be in the range 0..");		\
2108	    else							\
2109		tprint("), should be at most ");			\
2110	    print_int(n);						\
2111	    help1("I'm going to use 0 instead of that illegal code value."); \
2112	    error();							\
2113	    cur_val=0;							\
2114	}								\
2115    } while (0)
2116
2117
2118@ @c
2119void prefixed_command(void)
2120{
2121    int a;                      /* accumulated prefix codes so far */
2122    internal_font_number f;     /* identifies a font */
2123    halfword j;                 /* index into a \.{\\parshape} specification */
2124    halfword p, q;              /* for temporary short-term use */
2125    int n;                      /* ditto */
2126    boolean e;                  /* should a definition be expanded? or was \.{\\let} not done? */
2127    mathcodeval mval;           /* for handling of \.{\\mathchardef}s */
2128    a = 0;
2129    while (cur_cmd == prefix_cmd) {
2130        if (!odd(a / cur_chr))
2131            a = a + cur_chr;
2132        /* Get the next non-blank non-relax... */
2133        do {
2134            get_x_token();
2135        } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
2136
2137        if (cur_cmd <= max_non_prefixed_command) {
2138            /* Discard erroneous prefixes and |return| */
2139            print_err("You can't use a prefix with `");
2140            print_cmd_chr((quarterword) cur_cmd, cur_chr);
2141            print_char('\'');
2142            help2
2143                ("I'll pretend you didn't say \\long or \\outer or \\global or",
2144                 "\\protected.");
2145            back_error();
2146            return;
2147        }
2148        if (int_par(tracing_commands_code) > 2)
2149            show_cur_cmd_chr();
2150    }
2151    /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */
2152    if (a >= 8) {
2153        j = protected_token;
2154        a = a - 8;
2155    } else {
2156        j = 0;
2157    }
2158    if ((cur_cmd != def_cmd) && ((a % 4 != 0) || (j != 0))) {
2159        print_err("You can't use `\\long' or `\\outer' or `\\protected' with `");
2160        print_cmd_chr((quarterword) cur_cmd, cur_chr);
2161        print_char('\'');
2162        help1("I'll pretend you didn't say \\long or \\outer or \\protected here.");
2163        error();
2164    }
2165
2166    /* Adjust for the setting of \.{\\globaldefs} */
2167    if (global_defs != 0) {
2168        if (global_defs < 0) {
2169            if (is_global(a))
2170                a = a - 4;
2171        } else {
2172            if (!is_global(a))
2173                a = a + 4;
2174        }
2175    }
2176    switch (cur_cmd) {
2177    case set_font_cmd:
2178        /* Here's an example of the way many of the following routines operate.
2179           (Unfortunately, they aren't all as simple as this.) */
2180        define(cur_font_loc, data_cmd, cur_chr);
2181        break;
2182    case def_cmd:
2183        /* When a |def| command has been scanned,
2184           |cur_chr| is odd if the definition is supposed to be global, and
2185           |cur_chr>=2| if the definition is supposed to be expanded. */
2186
2187        if (odd(cur_chr) && !is_global(a) && (global_defs >= 0))
2188            a = a + 4;
2189        e = (cur_chr >= 2);
2190        get_r_token();
2191        p = cur_cs;
2192        q = scan_toks(true, e);
2193        if (j != 0) {
2194            q = get_avail();
2195            set_token_info(q, j);
2196            set_token_link(q, token_link(def_ref));
2197            set_token_link(def_ref, q);
2198        }
2199        define(p, call_cmd + (a % 4), def_ref);
2200        break;
2201    case let_cmd:
2202        n = cur_chr;
2203        get_r_token();
2204        p = cur_cs;
2205        if (n == normal) {
2206            do {
2207                get_token();
2208            } while (cur_cmd == spacer_cmd);
2209            if (cur_tok == other_token + '=') {
2210                get_token();
2211                if (cur_cmd == spacer_cmd)
2212                    get_token();
2213            }
2214        } else {
2215            get_token();
2216            q = cur_tok;
2217            get_token();
2218            back_input();
2219            cur_tok = q;
2220            back_input();       /* look ahead, then back up */
2221        }                       /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */
2222        if (cur_cmd >= call_cmd)
2223            add_token_ref(cur_chr);
2224        define(p, cur_cmd, cur_chr);
2225        break;
2226    case shorthand_def_cmd:
2227        /* We temporarily define |p| to be |relax|, so that an occurrence of |p|
2228           while scanning the definition will simply stop the scanning instead of
2229           producing an ``undefined control sequence'' error or expanding the
2230           previous meaning.  This allows, for instance, `\.{\\chardef\\foo=123\\foo}'.
2231         */
2232        n = cur_chr;
2233        get_r_token();
2234        p = cur_cs;
2235        define(p, relax_cmd, too_big_char);
2236        scan_optional_equals();
2237        switch (n) {
2238        case char_def_code:
2239            scan_char_num();
2240            define(p, char_given_cmd, cur_val);
2241            break;
2242        case math_char_def_code:
2243            mval = scan_mathchar(tex_mathcode);
2244            cur_val =
2245                (mval.class_value * 16 + mval.family_value) * 256 +
2246                mval.character_value;
2247            define(p, math_given_cmd, cur_val);
2248            break;
2249        case xmath_char_def_code:
2250            mval = scan_mathchar(xetex_mathcode);
2251            cur_val =
2252                (mval.class_value + (8 * mval.family_value)) * (65536 * 32) +
2253                mval.character_value;
2254            define(p, xmath_given_cmd, cur_val);
2255            break;
2256        case umath_char_def_code:
2257            mval = scan_mathchar(xetexnum_mathcode);
2258            cur_val =
2259                (mval.class_value + (8 * mval.family_value)) * (65536 * 32) +
2260                mval.character_value;
2261            define(p, xmath_given_cmd, cur_val);
2262            break;
2263        default:
2264            scan_register_num();
2265            switch (n) {
2266            case count_def_code:
2267                define(p, assign_int_cmd, count_base + cur_val);
2268                break;
2269            case attribute_def_code:
2270                define(p, assign_attr_cmd, attribute_base + cur_val);
2271                break;
2272            case dimen_def_code:
2273                define(p, assign_dimen_cmd, scaled_base + cur_val);
2274                break;
2275            case skip_def_code:
2276                define(p, assign_glue_cmd, skip_base + cur_val);
2277                break;
2278            case mu_skip_def_code:
2279                define(p, assign_mu_glue_cmd, mu_skip_base + cur_val);
2280                break;
2281            case toks_def_code:
2282                define(p, assign_toks_cmd, toks_base + cur_val);
2283                break;
2284            default:
2285                confusion("shorthand_def");
2286                break;
2287            }
2288            break;
2289        }
2290        break;
2291    case read_to_cs_cmd:
2292        j = cur_chr;
2293        scan_int();
2294        n = cur_val;
2295        if (!scan_keyword("to")) {
2296            print_err("Missing `to' inserted");
2297            help2("You should have said `\\read<number> to \\cs'.",
2298                  "I'm going to look for the \\cs now.");
2299            error();
2300        }
2301        get_r_token();
2302        p = cur_cs;
2303        read_toks(n, p, j);
2304        define(p, call_cmd, cur_val);
2305        break;
2306    case toks_register_cmd:
2307    case assign_toks_cmd:
2308        /* The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive
2309           their values in the following way. (For safety's sake, we place an
2310           enclosing pair of braces around an \.{\\output} list.) */
2311        q = cur_cs;
2312        if (cur_cmd == toks_register_cmd) {
2313            scan_register_num();
2314            p = toks_base + cur_val;
2315        } else {
2316            p = cur_chr;        /* |p=every_par_loc| or |output_routine_loc| or \dots */
2317        }
2318        scan_optional_equals();
2319        /* Get the next non-blank non-relax non-call token */
2320        do {
2321            get_x_token();
2322        } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
2323
2324        if (cur_cmd != left_brace_cmd) {
2325            /* If the right-hand side is a token parameter
2326               or token register, finish the assignment and |goto done| */
2327            if (cur_cmd == toks_register_cmd) {
2328                scan_register_num();
2329                cur_cmd = assign_toks_cmd;
2330                cur_chr = toks_base + cur_val;
2331            }
2332            if (cur_cmd == assign_toks_cmd) {
2333                q = equiv(cur_chr);
2334                if (q == null) {
2335                    define(p, undefined_cs_cmd, null);
2336                } else {
2337                    add_token_ref(q);
2338                    define(p, call_cmd, q);
2339                }
2340                goto DONE;
2341            }
2342        }
2343        back_input();
2344        cur_cs = q;
2345        q = scan_toks(false, false);
2346        if (token_link(def_ref) == null) {      /* empty list: revert to the default */
2347            define(p, undefined_cs_cmd, null);
2348            free_avail(def_ref);
2349        } else {
2350            if (p == output_routine_loc) {      /* enclose in curlies */
2351                p = get_avail();
2352                set_token_link(q, p);
2353                p = output_routine_loc;
2354                q = token_link(q);
2355                set_token_info(q, right_brace_token + '}');
2356                q = get_avail();
2357                set_token_info(q, left_brace_token + '{');
2358                set_token_link(q, token_link(def_ref));
2359                set_token_link(def_ref, q);
2360            }
2361            define(p, call_cmd, def_ref);
2362        }
2363        break;
2364    case assign_int_cmd:
2365        /* Similar routines are used to assign values to the numeric parameters. */
2366        p = cur_chr;
2367        scan_optional_equals();
2368        scan_int();
2369        assign_internal_value(a, p, cur_val);
2370        break;
2371    case assign_attr_cmd:
2372        p = cur_chr;
2373        scan_optional_equals();
2374        scan_int();
2375        if ((p - attribute_base) > max_used_attr)
2376            max_used_attr = (p - attribute_base);
2377        attr_list_cache = cache_disabled;
2378        word_define(p, cur_val);
2379        break;
2380    case assign_dir_cmd:
2381        /* DIR: Assign direction codes */
2382        scan_direction();
2383        switch (cur_chr) {
2384        case int_base + page_direction_code:
2385            eq_word_define(int_base + page_direction_code, cur_val);
2386            break;
2387        case int_base + body_direction_code:
2388            eq_word_define(int_base + body_direction_code, cur_val);
2389            break;
2390        case int_base + par_direction_code:
2391            eq_word_define(int_base + par_direction_code, cur_val);
2392            break;
2393        case int_base + math_direction_code:
2394            eq_word_define(int_base + math_direction_code, cur_val);
2395            break;
2396        case int_base + text_direction_code:
2397#if 0
2398    /* various tests hint that this is unnecessary and
2399     * sometimes even produces weird results, eg
2400     *  (\hbox{\textdir TRT ABC\textdir TLT DEF})
2401     * becomes
2402     *  (DEFCBA)
2403     * in the output
2404     */
2405            if ((no_local_dirs > 0) && (abs(mode) == hmode)) {
2406                /* DIR: Add local dir node */
2407                tail_append(new_dir(text_direction));
2408            }
2409#endif
2410            update_text_dir_ptr(cur_val);
2411            if (abs(mode) == hmode) {
2412                /* DIR: Add local dir node */
2413                tail_append(new_dir(cur_val));
2414                dir_level(tail) = cur_level;
2415            }
2416            eq_word_define(int_base + text_direction_code, cur_val);
2417            eq_word_define(int_base + no_local_dirs_code, no_local_dirs + 1);
2418            break;
2419        }
2420        break;
2421    case assign_dimen_cmd:
2422        p = cur_chr;
2423        scan_optional_equals();
2424        scan_normal_dimen();
2425        assign_internal_value(a, p, cur_val);
2426        break;
2427    case assign_glue_cmd:
2428    case assign_mu_glue_cmd:
2429        p = cur_chr;
2430        n = cur_cmd;
2431        scan_optional_equals();
2432        if (n == assign_mu_glue_cmd)
2433            scan_glue(mu_val_level);
2434        else
2435            scan_glue(glue_val_level);
2436        trap_zero_glue();
2437        define(p, glue_ref_cmd, cur_val);
2438        break;
2439    case def_char_code_cmd:
2440    case def_del_code_cmd:
2441        /* Let |n| be the largest legal code value, based on |cur_chr| */
2442        if (cur_chr == cat_code_base)
2443            n = max_char_code;
2444        else if (cur_chr == sf_code_base)
2445            n = 077777;
2446        else
2447            n = biggest_char;
2448
2449        p = cur_chr;
2450        if (cur_chr == math_code_base) {
2451            if (is_global(a))
2452                cur_val1 = level_one;
2453            else
2454                cur_val1 = cur_level;
2455            scan_extdef_math_code(cur_val1, tex_mathcode);
2456        } else if (cur_chr == lc_code_base) {
2457            scan_char_num();
2458            p = cur_val;
2459            scan_optional_equals();
2460            scan_int();
2461            check_def_code(lc_code_base);
2462            define_lc_code(p, cur_val);
2463        } else if (cur_chr == uc_code_base) {
2464            scan_char_num();
2465            p = cur_val;
2466            scan_optional_equals();
2467            scan_int();
2468            check_def_code(uc_code_base);
2469            define_uc_code(p, cur_val);
2470        } else if (cur_chr == sf_code_base) {
2471            scan_char_num();
2472            p = cur_val;
2473            scan_optional_equals();
2474            scan_int();
2475            check_def_code(sf_code_base);
2476            define_sf_code(p, cur_val);
2477        } else if (cur_chr == cat_code_base) {
2478            scan_char_num();
2479            p = cur_val;
2480            scan_optional_equals();
2481            scan_int();
2482            check_def_code(cat_code_base);
2483            define_cat_code(p, cur_val);
2484        } else if (cur_chr == del_code_base) {
2485            if (is_global(a))
2486                cur_val1 = level_one;
2487            else
2488                cur_val1 = cur_level;
2489            scan_extdef_del_code(cur_val1, tex_mathcode);
2490        }
2491        break;
2492    case extdef_math_code_cmd:
2493    case extdef_del_code_cmd:
2494        if (is_global(a))
2495            cur_val1 = level_one;
2496        else
2497            cur_val1 = cur_level;
2498        if (cur_chr == math_code_base)
2499            scan_extdef_math_code(cur_val1, xetex_mathcode);
2500        else if (cur_chr == math_code_base + 1)
2501            scan_extdef_math_code(cur_val1, xetexnum_mathcode);
2502        else if (cur_chr == del_code_base)
2503            scan_extdef_del_code(cur_val1, xetex_mathcode);
2504        else if (cur_chr == del_code_base + 1)
2505            scan_extdef_del_code(cur_val1, xetexnum_mathcode);
2506        break;
2507    case def_family_cmd:
2508        p = cur_chr;
2509        scan_math_family_int();
2510        cur_val1 = cur_val;
2511        scan_optional_equals();
2512        scan_font_ident();
2513        define_fam_fnt(cur_val1, p, cur_val);
2514        break;
2515    case set_math_param_cmd:
2516        p = cur_chr;
2517        get_token();
2518        if (cur_cmd != math_style_cmd) {
2519            print_err("Missing math style, treated as \\displaystyle");
2520            help1
2521                ("A style should have been here; I inserted `\\displaystyle'.");
2522            cur_val1 = display_style;
2523            back_error();
2524        } else {
2525            cur_val1 = cur_chr;
2526        }
2527        scan_optional_equals();
2528        if (p < math_param_first_mu_glue) {
2529            if (p == math_param_radical_degree_raise)
2530                scan_int();
2531            else
2532                scan_dimen(false, false, false);
2533        } else {
2534            scan_glue(mu_val_level);
2535            trap_zero_glue();
2536            if (cur_val == glue_par(thin_mu_skip_code))
2537                cur_val = thin_mu_skip_code;
2538            else if (cur_val == glue_par(med_mu_skip_code))
2539                cur_val = med_mu_skip_code;
2540            else if (cur_val == glue_par(thick_mu_skip_code))
2541                cur_val = thick_mu_skip_code;
2542        }
2543        define_math_param(p, cur_val1, cur_val);
2544        break;
2545    case register_cmd:
2546    case advance_cmd:
2547    case multiply_cmd:
2548    case divide_cmd:
2549        do_register_command(a);
2550        break;
2551    case set_box_cmd:
2552        /* The processing of boxes is somewhat different, because we may need
2553           to scan and create an entire box before we actually change the value
2554           of the old one. */
2555        scan_register_num();
2556        if (is_global(a))
2557            n = global_box_flag + cur_val;
2558        else
2559            n = box_flag + cur_val;
2560        scan_optional_equals();
2561        if (set_box_allowed) {
2562            scan_box(n);
2563        } else {
2564            print_err("Improper \\setbox");
2565            help2("Sorry, \\setbox is not allowed after \\halign in a display,",
2566                  "or between \\accent and an accented character.");
2567            error();
2568        }
2569        break;
2570    case set_aux_cmd:
2571        /* The |space_factor| or |prev_depth| settings are changed when a |set_aux|
2572           command is sensed. Similarly, |prev_graf| is changed in the presence of
2573           |set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of
2574           |set_page_int|. These definitions are always global. */
2575        alter_aux();
2576        break;
2577    case set_prev_graf_cmd:
2578        alter_prev_graf();
2579        break;
2580    case set_page_dimen_cmd:
2581        alter_page_so_far();
2582        break;
2583    case set_page_int_cmd:
2584        alter_integer();
2585        break;
2586    case set_box_dimen_cmd:
2587        /* When some dimension of a box register is changed, the change isn't exactly
2588           global; but \TeX\ does not look at the \.{\\global} switch. */
2589        alter_box_dimen();
2590        break;
2591    case set_tex_shape_cmd:
2592        q = cur_chr;
2593        scan_optional_equals();
2594        scan_int();
2595        n = cur_val;
2596        if (n <= 0) {
2597            p = null;
2598        } else {
2599            p = new_node(shape_node, 2 * (n + 1) + 1);
2600            vinfo(p + 1) = n;
2601            for (j = 1; j <= n; j++) {
2602                scan_normal_dimen();
2603                varmem[p + 2 * j].cint = cur_val;       /* indentation */
2604                scan_normal_dimen();
2605                varmem[p + 2 * j + 1].cint = cur_val;   /* width */
2606            }
2607        }
2608        define(q, shape_ref_cmd, p);
2609        break;
2610    case set_etex_shape_cmd:
2611        q = cur_chr;
2612        scan_optional_equals();
2613        scan_int();
2614        n = cur_val;
2615        if (n <= 0) {
2616            p = null;
2617        } else {
2618            n = (cur_val / 2) + 1;
2619            p = new_node(shape_node, 2 * n + 1 + 1);
2620            vinfo(p + 1) = n;
2621            n = cur_val;
2622            varmem[p + 2].cint = n;     /* number of penalties */
2623            for (j = p + 3; j <= p + n + 2; j++) {
2624                scan_int();
2625                varmem[j].cint = cur_val;       /* penalty values */
2626            }
2627            if (!odd(n))
2628                varmem[p + n + 3].cint = 0;     /* unused */
2629        }
2630        define(q, shape_ref_cmd, p);
2631        break;
2632    case hyph_data_cmd:
2633        /* All of \TeX's parameters are kept in |eqtb| except the font information,
2634           the interaction mode, and the hyphenation tables; these are strictly global.
2635         */
2636        switch (cur_chr) {
2637        case 0:
2638            new_hyph_exceptions();
2639            break;
2640        case 1:
2641            new_patterns();
2642            break;
2643        case 2:
2644            new_pre_hyphen_char();
2645            break;
2646        case 3:
2647            new_post_hyphen_char();
2648            break;
2649        case 4:
2650            new_pre_exhyphen_char();
2651            break;
2652        case 5:
2653            new_post_exhyphen_char();
2654            break;
2655        }
2656        break;
2657    case assign_font_dimen_cmd:
2658        set_font_dimen();
2659        break;
2660    case assign_font_int_cmd:
2661        n = cur_chr;
2662        scan_font_ident();
2663        f = cur_val;
2664        if (n == no_lig_code) {
2665            set_no_ligatures(f);
2666        } else if (n < lp_code_base) {
2667            scan_optional_equals();
2668            scan_int();
2669            if (n == 0)
2670                set_hyphen_char(f, cur_val);
2671            else
2672                set_skew_char(f, cur_val);
2673        } else {
2674            scan_char_num();
2675            p = cur_val;
2676            scan_optional_equals();
2677            scan_int();
2678            switch (n) {
2679            case lp_code_base:
2680                set_lp_code(f, p, cur_val);
2681                break;
2682            case rp_code_base:
2683                set_rp_code(f, p, cur_val);
2684                break;
2685            case ef_code_base:
2686                set_ef_code(f, p, cur_val);
2687                break;
2688            case tag_code:
2689                set_tag_code(f, p, cur_val);
2690                break;
2691            }
2692        }
2693        break;
2694    case def_font_cmd:
2695        /* Here is where the information for a new font gets loaded. */
2696        tex_def_font((small_number) a);
2697        break;
2698    case letterspace_font_cmd:
2699        new_letterspaced_font((small_number) a);
2700        break;
2701    case pdf_copy_font_cmd:
2702        make_font_copy((small_number) a);
2703        break;
2704    case set_interaction_cmd:
2705        new_interaction();
2706        break;
2707    default:
2708        confusion("prefix");
2709        break;
2710    }                           /* end of Assignments cases */
2711  DONE:
2712    /* Insert a token saved by \.{\\afterassignment}, if any */
2713    if (after_token != 0) {
2714        cur_tok = after_token;
2715        back_input();
2716        after_token = 0;
2717    }
2718}
2719
2720@ @c
2721void fixup_directions(void)
2722{
2723    int temp_no_whatsits;
2724    int temp_no_dirs;
2725    int temporary_dir;
2726    temp_no_whatsits = no_local_whatsits;
2727    temp_no_dirs = no_local_dirs;
2728    temporary_dir = text_direction;
2729    if (dir_level(text_dir_ptr) == cur_level) {
2730        /* DIR: Remove from |text_dir_ptr| */
2731        halfword text_dir_tmp = vlink(text_dir_ptr);
2732        flush_node(text_dir_ptr);
2733        text_dir_ptr = text_dir_tmp;
2734
2735    }
2736    unsave();
2737    if (abs(mode) == hmode) {
2738        if (temp_no_dirs != 0) {
2739            /* DIR: Add local dir node */
2740            tail_append(new_dir(text_direction));
2741
2742            dir_dir(tail) = temporary_dir - 64;
2743        }
2744        if (temp_no_whatsits != 0) {
2745            /* LOCAL: Add local paragraph node */
2746            tail_append(make_local_par_node());
2747
2748        }
2749    }
2750}
2751
2752
2753@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or
2754something similar, the |get_r_token| routine will substitute a special
2755control sequence for a token that is not redefinable.
2756
2757@c
2758void get_r_token(void)
2759{
2760  RESTART:
2761    do {
2762        get_token();
2763    } while (cur_tok == space_token);
2764    if ((cur_cs == 0) || (cur_cs > eqtb_top) ||
2765        ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) {
2766        print_err("Missing control sequence inserted");
2767        help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",
2768              "I've inserted an inaccessible control sequence so that your",
2769              "definition will be completed without mixing me up too badly.",
2770              "You can recover graciously from this error, if you're",
2771              "careful; see exercise 27.2 in The TeXbook.");
2772        if (cur_cs == 0)
2773            back_input();
2774        cur_tok = cs_token_flag + frozen_protection;
2775        ins_error();
2776        goto RESTART;
2777    }
2778}
2779
2780@ @c
2781void assign_internal_value(int a, halfword p, int val)
2782{
2783    halfword n;
2784    if ((p >= int_base) && (p < attribute_base)) {
2785        switch ((p - int_base)) {
2786        case cat_code_table_code:
2787            if (valid_catcode_table(val)) {
2788                if (val != int_par(cat_code_table_code))
2789                    word_define(p, val);
2790            } else {
2791                print_err("Invalid \\catcode table");
2792                help2
2793                    ("You can only switch to a \\catcode table that is initialized",
2794                     "using \\savecatcodetable or \\initcatcodetable, or to table 0");
2795                error();
2796            }
2797            break;
2798        case output_box_code:
2799            if ((val > 65535) | (val < 0)) {
2800                print_err("Invalid \\outputbox");
2801                help1
2802                    ("The value for \\outputbox has to be between 0 and 65535.");
2803                error();
2804            } else {
2805                word_define(p, val);
2806            }
2807            break;
2808        case new_line_char_code:
2809            if (val > 127) {
2810                print_err("Invalid \\newlinechar");
2811                help2
2812                    ("The value for \\newlinechar has to be no higher than 127.",
2813                     "Your invalid assignment will be ignored.");
2814                error();
2815            } else {
2816                word_define(p, val);
2817            }
2818            break;
2819        case end_line_char_code:
2820            if (val > 127) {
2821                print_err("Invalid \\endlinechar");
2822                help2
2823                    ("The value for \\endlinechar has to be no higher than 127.",
2824                     "Your invalid assignment will be ignored.");
2825                error();
2826            } else {
2827                word_define(p, val);
2828            }
2829            break;
2830        case pdf_compress_level_code:
2831            static_pdf->compress_level = val;
2832            word_define(p, val);
2833            break;
2834        case pdf_objcompresslevel_code:
2835            static_pdf->objcompresslevel = val;
2836            word_define(p, val);
2837            break;
2838        case language_code:
2839            if (val < 0) {
2840	        word_define(int_base + cur_lang_code, -1);
2841                word_define(p, -1);
2842            } else if (val > 16383) {
2843                print_err("Invalid \\language");
2844                help2
2845                    ("The absolute value for \\language has to be no higher than 16383.",
2846                     "Your invalid assignment will be ignored.");
2847                error();
2848            } else {
2849	        word_define(int_base + cur_lang_code, val);
2850                word_define(p, val);
2851            }
2852            break;
2853        default:
2854            word_define(p, val);
2855            break;
2856        }
2857        /* If we are defining subparagraph penalty levels while we are
2858           in hmode, then we put out a whatsit immediately, otherwise
2859           we leave it alone.  This mechanism might not be sufficiently
2860           powerful, and some other algorithm, searching down the stack,
2861           might be necessary.  Good first step. */
2862        if ((abs(mode) == hmode) &&
2863            ((p == (int_base + local_inter_line_penalty_code)) ||
2864             (p == (int_base + local_broken_penalty_code)))) {
2865            /* LOCAL: Add local paragraph node */
2866            tail_append(make_local_par_node());
2867
2868            eq_word_define(int_base + no_local_whatsits_code,
2869                           no_local_whatsits + 1);
2870        }
2871
2872    } else if ((p >= dimen_base) && (p <= eqtb_size)) {
2873        if (p == (dimen_base + page_left_offset_code)) {
2874            n = val - one_true_inch;
2875            word_define(dimen_base + h_offset_code, n);
2876        } else if (p == (dimen_base + h_offset_code)) {
2877            n = val + one_true_inch;
2878            word_define(dimen_base + page_left_offset_code, n);
2879        } else if (p == (dimen_base + page_top_offset_code)) {
2880            n = val - one_true_inch;
2881            word_define(dimen_base + v_offset_code, n);
2882        } else if (p == (dimen_base + v_offset_code)) {
2883            n = val + one_true_inch;
2884            word_define(dimen_base + page_top_offset_code, n);
2885        }
2886        word_define(p, val);
2887
2888    } else if ((p >= local_base) && (p < toks_base)) {  /* internal locals  */
2889        define(p, call_cmd, val);
2890    } else {
2891        confusion("assign internal value");
2892    }
2893}
2894
2895
2896@ When a glue register or parameter becomes zero, it will always point to
2897|zero_glue| because of the following procedure. (Exception: The tabskip
2898glue isn't trapped while preambles are being scanned.)
2899
2900@c
2901void trap_zero_glue(void)
2902{
2903    if ((width(cur_val) == 0) && (stretch(cur_val) == 0)
2904        && (shrink(cur_val) == 0)) {
2905        add_glue_ref(zero_glue);
2906        delete_glue_ref(cur_val);
2907        cur_val = zero_glue;
2908    }
2909}
2910
2911@ We use the fact that |register<advance<multiply<divide|
2912
2913@c
2914void do_register_command(int a)
2915{
2916    halfword l, q, r, s;        /* for list manipulation */
2917    int p;                      /* type of register involved */
2918    q = cur_cmd;
2919    /* Compute the register location |l| and its type |p|; but |return| if invalid */
2920    /* Here we use the fact that the consecutive codes |int_val..mu_val| and
2921       |assign_int..assign_mu_glue| correspond to each other nicely. */
2922    l = 0;
2923    if (q != register_cmd) {
2924        get_x_token();
2925        if ((cur_cmd >= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) {
2926            l = cur_chr;
2927            p = cur_cmd - assign_int_cmd;
2928            goto FOUND;
2929        }
2930        if (cur_cmd != register_cmd) {
2931            print_err("You can't use `");
2932            print_cmd_chr((quarterword) cur_cmd, cur_chr);
2933            tprint("' after ");
2934            print_cmd_chr((quarterword) q, 0);
2935            help1("I'm forgetting what you said and not changing anything.");
2936            error();
2937            return;
2938        }
2939    }
2940    p = cur_chr;
2941    scan_register_num();
2942    if (p == int_val_level)
2943        l = cur_val + count_base;
2944    else if (p == attr_val_level)
2945        l = cur_val + attribute_base;
2946    else if (p == dimen_val_level)
2947        l = cur_val + scaled_base;
2948    else if (p == glue_val_level)
2949        l = cur_val + skip_base;
2950    else if (p == mu_val_level)
2951        l = cur_val + mu_skip_base;
2952
2953  FOUND:
2954    if (q == register_cmd) {
2955        scan_optional_equals();
2956    } else if (scan_keyword("by")) {
2957        ;                       /* optional `\.{by}' */
2958    }
2959    arith_error = false;
2960    if (q < multiply_cmd) {
2961        /* Compute result of |register| or |advance|, put it in |cur_val| */
2962        if (p < glue_val_level) {
2963            if ((p == int_val_level) || (p == attr_val_level))
2964                scan_int();
2965            else
2966                scan_normal_dimen();
2967            if (q == advance_cmd)
2968                cur_val = cur_val + eqtb[l].cint;
2969        } else {
2970            scan_glue(p);
2971            if (q == advance_cmd) {
2972                /* Compute the sum of two glue specs */
2973                q = new_spec(cur_val);
2974                r = equiv(l);
2975                delete_glue_ref(cur_val);
2976                width(q) = width(q) + width(r);
2977                if (stretch(q) == 0) {
2978                    stretch_order(q) = normal;
2979                }
2980                if (stretch_order(q) == stretch_order(r)) {
2981                    stretch(q) = stretch(q) + stretch(r);
2982                } else if ((stretch_order(q) < stretch_order(r))
2983                           && (stretch(r) != 0)) {
2984                    stretch(q) = stretch(r);
2985                    stretch_order(q) = stretch_order(r);
2986                }
2987                if (shrink(q) == 0) {
2988                    shrink_order(q) = normal;
2989                }
2990                if (shrink_order(q) == shrink_order(r)) {
2991                    shrink(q) = shrink(q) + shrink(r);
2992                } else if ((shrink_order(q) < shrink_order(r))
2993                           && (shrink(r) != 0)) {
2994                    shrink(q) = shrink(r);
2995                    shrink_order(q) = shrink_order(r);
2996                }
2997                cur_val = q;
2998            }
2999        }
3000
3001    } else {
3002        /* Compute result of |multiply| or |divide|, put it in |cur_val| */
3003        scan_int();
3004        if (p < glue_val_level) {
3005            if (q == multiply_cmd) {
3006                if ((p == int_val_level) || (p == attr_val_level)) {
3007                    cur_val = mult_integers(eqtb[l].cint, cur_val);
3008                } else {
3009                    cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0);
3010                }
3011            } else {
3012                cur_val = x_over_n(eqtb[l].cint, cur_val);
3013            }
3014        } else {
3015            s = equiv(l);
3016            r = new_spec(s);
3017            if (q == multiply_cmd) {
3018                width(r) = nx_plus_y(width(s), cur_val, 0);
3019                stretch(r) = nx_plus_y(stretch(s), cur_val, 0);
3020                shrink(r) = nx_plus_y(shrink(s), cur_val, 0);
3021            } else {
3022                width(r) = x_over_n(width(s), cur_val);
3023                stretch(r) = x_over_n(stretch(s), cur_val);
3024                shrink(r) = x_over_n(shrink(s), cur_val);
3025            }
3026            cur_val = r;
3027        }
3028
3029    }
3030    if (arith_error) {
3031        print_err("Arithmetic overflow");
3032        help2("I can't carry out that multiplication or division,",
3033              "since the result is out of range.");
3034        if (p >= glue_val_level)
3035            delete_glue_ref(cur_val);
3036        error();
3037        return;
3038    }
3039    if (p < glue_val_level) {
3040        if (p == attr_val_level) {
3041            if ((l - attribute_base) > max_used_attr)
3042                max_used_attr = (l - attribute_base);
3043            attr_list_cache = cache_disabled;
3044        }
3045        if ((p == int_val_level) || (p == dimen_val_level))
3046            assign_internal_value(a, l, cur_val);
3047        else
3048            word_define(l, cur_val);
3049    } else {
3050        trap_zero_glue();
3051        define(l, glue_ref_cmd, cur_val);
3052    }
3053}
3054
3055
3056@ @c
3057void alter_aux(void)
3058{
3059    halfword c;                 /* |hmode| or |vmode| */
3060    if (cur_chr != abs(mode)) {
3061        report_illegal_case();
3062    } else {
3063        c = cur_chr;
3064        scan_optional_equals();
3065        if (c == vmode) {
3066            scan_normal_dimen();
3067            prev_depth = cur_val;
3068        } else {
3069            scan_int();
3070            if ((cur_val <= 0) || (cur_val > 32767)) {
3071                print_err("Bad space factor");
3072                help1("I allow only values in the range 1..32767 here.");
3073                int_error(cur_val);
3074            } else {
3075                space_factor = cur_val;
3076            }
3077        }
3078    }
3079}
3080
3081@ @c
3082void alter_prev_graf(void)
3083{
3084    int p;                      /* index into |nest| */
3085    p = nest_ptr;
3086    while (abs(nest[p].mode_field) != vmode)
3087        decr(p);
3088    scan_optional_equals();
3089    scan_int();
3090    if (cur_val < 0) {
3091        print_err("Bad \\prevgraf");
3092        help1("I allow only nonnegative values here.");
3093        int_error(cur_val);
3094    } else {
3095        nest[p].pg_field = cur_val;
3096    }
3097}
3098
3099@ @c
3100void alter_page_so_far(void)
3101{
3102    int c;                      /* index into |page_so_far| */
3103    c = cur_chr;
3104    scan_optional_equals();
3105    scan_normal_dimen();
3106    page_so_far[c] = cur_val;
3107}
3108
3109@ @c
3110void alter_integer(void)
3111{
3112    int c;                      /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */
3113    c = cur_chr;
3114    scan_optional_equals();
3115    scan_int();
3116    if (c == 0) {
3117        dead_cycles = cur_val;
3118    } else if (c == 2) {
3119        if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) {
3120            print_err("Bad interaction mode");
3121            help2("Modes are 0=batch, 1=nonstop, 2=scroll, and",
3122                  "3=errorstop. Proceed, and I'll ignore this case.");
3123            int_error(cur_val);
3124        } else {
3125            cur_chr = cur_val;
3126            new_interaction();
3127        }
3128    } else {
3129        insert_penalties = cur_val;
3130    }
3131}
3132
3133@ @c
3134void alter_box_dimen(void)
3135{
3136    int c;                      /* |width_offset| or |height_offset| or |depth_offset| */
3137    int b;                      /* box number */
3138    c = cur_chr;
3139    scan_register_num();
3140    b = cur_val;
3141    scan_optional_equals();
3142    scan_normal_dimen();
3143    if (box(b) != null)
3144        varmem[box(b) + c].cint = cur_val;
3145}
3146
3147
3148@ @c
3149void new_interaction(void)
3150{
3151    print_ln();
3152    interaction = cur_chr;
3153    if (interaction == batch_mode)
3154        kpse_make_tex_discard_errors = 1;
3155    else
3156        kpse_make_tex_discard_errors = 0;
3157    fixup_selector(log_opened_global);
3158}
3159
3160
3161@ The \.{\\afterassignment} command puts a token into the global
3162variable |after_token|. This global variable is examined just after
3163every assignment has been performed.
3164
3165@c
3166halfword after_token;           /* zero, or a saved token */
3167
3168
3169@ Here is a procedure that might be called `Get the next non-blank non-relax
3170non-call non-assignment token'.
3171
3172@c
3173void do_assignments(void)
3174{
3175    while (true) {
3176        /* Get the next non-blank non-relax... */
3177        do {
3178            get_x_token();
3179        } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
3180
3181        if (cur_cmd <= max_non_prefixed_command)
3182            return;
3183        set_box_allowed = false;
3184        prefixed_command();
3185        set_box_allowed = true;
3186    }
3187}
3188
3189@ @c
3190void open_or_close_in(void)
3191{
3192    int c;                      /* 1 for \.{\\openin}, 0 for \.{\\closein} */
3193    int n;                      /* stream number */
3194    char *fn;
3195    c = cur_chr;
3196    scan_four_bit_int();
3197    n = cur_val;
3198    if (read_open[n] != closed) {
3199        lua_a_close_in(read_file[n], (n + 1));
3200        read_open[n] = closed;
3201    }
3202    if (c != 0) {
3203        scan_optional_equals();
3204        do {
3205            get_x_token();
3206        } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
3207        back_input();
3208        if (cur_cmd != left_brace_cmd) {
3209            scan_file_name();   /* set |cur_name| to desired file name */
3210            if (cur_ext == get_nullstr())
3211                cur_ext = maketexstring(".tex");
3212        } else {
3213            scan_file_name_toks();
3214        }
3215        fn = pack_file_name(cur_name, cur_area, cur_ext);
3216        if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) {
3217            read_open[n] = just_open;
3218        }
3219    }
3220}
3221
3222@ @c
3223boolean long_help_seen;         /* has the long \.{\\errmessage} help been used? */
3224
3225void issue_message(void)
3226{
3227    int old_setting;            /* holds |selector| setting */
3228    int c;                      /* identifies \.{\\message} and \.{\\errmessage} */
3229    str_number s;               /* the message */
3230    c = cur_chr;
3231    (void) scan_toks(false, true);
3232    old_setting = selector;
3233    selector = new_string;
3234    token_show(def_ref);
3235    selector = old_setting;
3236    flush_list(def_ref);
3237    str_room(1);
3238    s = make_string();
3239    if (c == 0) {
3240        /* Print string |s| on the terminal */
3241        if (term_offset + (int) str_length(s) > max_print_line - 2)
3242            print_ln();
3243        else if ((term_offset > 0) || (file_offset > 0))
3244            print_char(' ');
3245        print(s);
3246        update_terminal();
3247
3248    } else {
3249        /* Print string |s| as an error message */
3250        /* If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined
3251           \.{\\errhelp}, we don't want to give a long help message each time. So we
3252           give a verbose explanation only once. */
3253        print_err("");
3254        print(s);
3255        if (err_help != null) {
3256            use_err_help = true;
3257        } else if (long_help_seen) {
3258            help1("(That was another \\errmessage.)");
3259        } else {
3260            if (interaction < error_stop_mode)
3261                long_help_seen = true;
3262            help4("This error message was generated by an \\errmessage",
3263                  "command, so I can't give any explicit help.",
3264                  "Pretend that you're Hercule Poirot: Examine all clues,",
3265                  "and deduce the truth by order and method.");
3266        }
3267        error();
3268        use_err_help = false;
3269
3270    }
3271    flush_str(s);
3272}
3273
3274
3275@ The |error| routine calls on |give_err_help| if help is requested from
3276the |err_help| parameter.
3277
3278@c
3279void give_err_help(void)
3280{
3281    token_show(err_help);
3282}
3283
3284
3285
3286@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by
3287building a token list and then changing the cases of the letters in it.
3288
3289@c
3290void shift_case(void)
3291{
3292    halfword b;                 /* |lc_code_base| or |uc_code_base| */
3293    halfword p;                 /* runs through the token list */
3294    halfword t;                 /* token */
3295    halfword c;                 /* character code */
3296    halfword i;                 /* inbetween */
3297    b = cur_chr;
3298    p = scan_toks(false, false);
3299    p = token_link(def_ref);
3300    while (p != null) {
3301        /* Change the case of the token in |p|, if a change is appropriate */
3302        /*
3303           When the case of a |chr_code| changes, we don't change the |cmd|.
3304           We also change active characters.
3305         */
3306        t = token_info(p);
3307        if (t < cs_token_flag) {
3308            c = t % STRING_OFFSET;
3309            if (b == uc_code_base)
3310                i = get_uc_code(c);
3311            else
3312                i = get_lc_code(c);
3313            if (i != 0)
3314                set_token_info(p, t - c + i);
3315        } else if (is_active_cs(cs_text(t - cs_token_flag))) {
3316            c = active_cs_value(cs_text(t - cs_token_flag));
3317            if (b == uc_code_base)
3318                i = get_uc_code(c);
3319            else
3320                i = get_lc_code(c);
3321            if (i != 0)
3322                set_token_info(p, active_to_cs(i, true) + cs_token_flag);
3323        }
3324        p = token_link(p);
3325    }
3326    back_list(token_link(def_ref));
3327    free_avail(def_ref);        /* omit reference count */
3328}
3329
3330
3331@ We come finally to the last pieces missing from |main_control|, namely the
3332`\.{\\show}' commands that are useful when debugging.
3333
3334@c
3335void show_whatever(void)
3336{
3337    halfword p;                 /* tail of a token list to show */
3338    int t;                      /* type of conditional being shown */
3339    int m;                      /* upper bound on |fi_or_else| codes */
3340    int l;                      /* line where that conditional began */
3341    int n;                      /* level of \.{\\if...\\fi} nesting */
3342    switch (cur_chr) {
3343    case show_lists:
3344        begin_diagnostic();
3345        show_activities();
3346        break;
3347    case show_box_code:
3348        /* Show the current contents of a box */
3349        scan_register_num();
3350        begin_diagnostic();
3351        tprint_nl("> \\box");
3352        print_int(cur_val);
3353        print_char('=');
3354        if (box(cur_val) == null)
3355            tprint("void");
3356        else
3357            show_box(box(cur_val));
3358        break;
3359    case show_code:
3360        /* Show the current meaning of a token, then |goto common_ending| */
3361        get_token();
3362        if (interaction == error_stop_mode)
3363            wake_up_terminal();
3364        tprint_nl("> ");
3365        if (cur_cs != 0) {
3366            sprint_cs(cur_cs);
3367            print_char('=');
3368        }
3369        print_meaning();
3370        goto COMMON_ENDING;
3371        break;
3372        /* Cases for |show_whatever| */
3373    case show_groups:
3374        begin_diagnostic();
3375        show_save_groups();
3376        break;
3377    case show_ifs:
3378        begin_diagnostic();
3379        tprint_nl("");
3380        print_ln();
3381        if (cond_ptr == null) {
3382            tprint_nl("### ");
3383            tprint("no active conditionals");
3384        } else {
3385            p = cond_ptr;
3386            n = 0;
3387            do {
3388                incr(n);
3389                p = vlink(p);
3390            } while (p != null);
3391            p = cond_ptr;
3392            t = cur_if;
3393            l = if_line;
3394            m = if_limit;
3395            do {
3396                tprint_nl("### level ");
3397                print_int(n);
3398                tprint(": ");
3399                print_cmd_chr(if_test_cmd, t);
3400                if (m == fi_code)
3401                    tprint_esc("else");
3402                print_if_line(l);
3403                decr(n);
3404                t = if_limit_subtype(p);
3405                l = if_line_field(p);
3406                m = if_limit_type(p);
3407                p = vlink(p);
3408            } while (p != null);
3409        }
3410        break;
3411
3412    default:
3413        /* Show the current value of some parameter or register,
3414           then |goto common_ending| */
3415        p = the_toks();
3416        if (interaction == error_stop_mode)
3417            wake_up_terminal();
3418        tprint_nl("> ");
3419        token_show(temp_token_head);
3420        flush_list(token_link(temp_token_head));
3421        goto COMMON_ENDING;
3422        break;
3423    }
3424    /* Complete a potentially long \.{\\show} command */
3425    end_diagnostic(true);
3426    print_err("OK");
3427    if (selector == term_and_log) {
3428        if (tracing_online <= 0) {
3429            selector = term_only;
3430            tprint(" (see the transcript file)");
3431            selector = term_and_log;
3432        }
3433    }
3434
3435  COMMON_ENDING:
3436    if (interaction < error_stop_mode) {
3437        help0();
3438        decr(error_count);
3439    } else if (tracing_online > 0) {
3440        help3("This isn't an error message; I'm just \\showing something.",
3441              "Type `I\\show...' to show more (e.g., \\show\\cs,",
3442              "\\showthe\\count10, \\showbox255, \\showlists).");
3443    } else {
3444        help5("This isn't an error message; I'm just \\showing something.",
3445              "Type `I\\show...' to show more (e.g., \\show\\cs,",
3446              "\\showthe\\count10, \\showbox255, \\showlists).",
3447              "And type `I\\tracingonline=1\\show...' to show boxes and",
3448              "lists on your terminal as well as in the transcript file.");
3449    }
3450    error();
3451}
3452
3453
3454@ @c
3455void initialize(void)
3456{                               /* this procedure gets things started properly */
3457    int k;                      /* index into |mem|, |eqtb|, etc. */
3458    /* Initialize whatever \TeX\ might access */
3459    /* Set initial values of key variables */
3460    initialize_errors();
3461    initialize_arithmetic();
3462    max_used_attr = -1;
3463    attr_list_cache = cache_disabled;
3464    initialize_nesting();
3465
3466    /* Start a new current page */
3467    page_contents = empty;
3468    page_tail = page_head;
3469#if 0
3470    vlink(page_head) = null;
3471#endif
3472    last_glue = max_halfword;
3473    last_penalty = 0;
3474    last_kern = 0;
3475    last_node_type = -1;
3476    page_depth = 0;
3477    page_max_depth = 0;
3478
3479    initialize_equivalents();
3480    no_new_control_sequence = true;     /* new identifiers are usually forbidden */
3481    init_primitives();
3482
3483    mag_set = 0;
3484    initialize_marks();
3485    initialize_read();
3486
3487    assert(static_pdf == NULL);
3488    static_pdf = init_pdf_struct(static_pdf);
3489
3490    format_ident = 0;
3491    format_name = get_nullstr();
3492    for (k = 0; k <= 17; k++)
3493        write_open[k] = false;
3494    initialize_directions();
3495    seconds_and_micros(epochseconds, microseconds);
3496    init_start_time(static_pdf);
3497
3498    edit_name_start = 0;
3499    stop_at_space = true;
3500
3501    if (ini_version) {
3502        /* Initialize table entries (done by \.{INITEX} only) */
3503
3504        init_node_mem(500);
3505        initialize_tokens();
3506        /* Initialize the special list heads and constant nodes */
3507        initialize_alignments();
3508        initialize_buildpage();
3509
3510        initialize_active();
3511
3512        set_eq_type(undefined_control_sequence, undefined_cs_cmd);
3513        set_equiv(undefined_control_sequence, null);
3514        set_eq_level(undefined_control_sequence, level_zero);
3515        for (k = null_cs; k <= (eqtb_top - 1); k++)
3516            eqtb[k] = eqtb[undefined_control_sequence];
3517        set_equiv(glue_base, zero_glue);
3518        set_eq_level(glue_base, level_one);
3519        set_eq_type(glue_base, glue_ref_cmd);
3520        for (k = glue_base + 1; k <= local_base - 1; k++)
3521            eqtb[k] = eqtb[glue_base];
3522        glue_ref_count(zero_glue) =
3523            glue_ref_count(zero_glue) + local_base - glue_base;
3524
3525        par_shape_ptr = null;
3526        set_eq_type(par_shape_loc, shape_ref_cmd);
3527        set_eq_level(par_shape_loc, level_one);
3528        for (k = etex_pen_base; k <= (etex_pens - 1); k++)
3529            eqtb[k] = eqtb[par_shape_loc];
3530        for (k = output_routine_loc; k <= toks_base + biggest_reg; k++)
3531            eqtb[k] = eqtb[undefined_control_sequence];
3532        box(0) = null;
3533        set_eq_type(box_base, box_ref_cmd);
3534        set_eq_level(box_base, level_one);
3535        for (k = box_base + 1; k <= (box_base + biggest_reg); k++)
3536            eqtb[k] = eqtb[box_base];
3537        cur_font = null_font;
3538        set_eq_type(cur_font_loc, data_cmd);
3539        set_eq_level(cur_font_loc, level_one);
3540        set_equiv(cat_code_base, 0);
3541        set_eq_type(cat_code_base, data_cmd);
3542        set_eq_level(cat_code_base, level_one);
3543        eqtb[internal_math_param_base] = eqtb[cat_code_base];
3544        eqtb[lc_code_base] = eqtb[cat_code_base];
3545        eqtb[uc_code_base] = eqtb[cat_code_base];
3546        eqtb[sf_code_base] = eqtb[cat_code_base];
3547        eqtb[math_code_base] = eqtb[cat_code_base];
3548        cat_code_table = 0;
3549        initialize_math_codes();
3550        initialize_text_codes();
3551        initex_cat_codes(0);
3552        for (k = '0'; k <= '9'; k++)
3553            set_math_code(k, tex_mathcode, var_code, 0, k, level_one);
3554        for (k = 'A'; k <= 'Z'; k++) {
3555            set_math_code(k, tex_mathcode, var_code, 1, k, level_one);
3556            set_math_code((k + 32), tex_mathcode, var_code, 1, (k + 32),
3557                          level_one);
3558            set_lc_code(k, k + 32, level_one);
3559            set_lc_code(k + 32, k + 32, level_one);
3560            set_uc_code(k, k, level_one);
3561            set_uc_code(k + 32, k, level_one);
3562            set_sf_code(k, 999, level_one);
3563        }
3564        for (k = int_base; k <= attribute_base - 1; k++)
3565            eqtb[k].cint = 0;
3566        for (k = attribute_base; k <= del_code_base - 1; k++)
3567            eqtb[k].cint = UNUSED_ATTRIBUTE;
3568        mag = 1000;
3569        tolerance = 10000;
3570        hang_after = 1;
3571        max_dead_cycles = 25;
3572        escape_char = '\\';
3573        end_line_char = carriage_return;
3574        set_del_code('.', tex_mathcode, 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */
3575        ex_hyphen_char = '-';
3576        output_box = 255;
3577        for (k = dimen_base; k <= eqtb_size; k++)
3578            eqtb[k].cint = 0;
3579        page_left_offset = one_inch;
3580        page_top_offset = one_inch;
3581        page_right_offset = one_inch;
3582        page_bottom_offset = one_inch;
3583        pdf_ignored_dimen = ignore_depth;
3584        pdf_each_line_height = pdf_ignored_dimen;
3585        pdf_each_line_depth = pdf_ignored_dimen;
3586        pdf_first_line_height = pdf_ignored_dimen;
3587        pdf_last_line_depth = pdf_ignored_dimen;
3588        ini_init_primitives();
3589        hash_used = frozen_control_sequence;    /* nothing is used */
3590        hash_high = 0;
3591        cs_count = 0;
3592        set_eq_type(frozen_dont_expand, dont_expand_cmd);
3593        cs_text(frozen_dont_expand) = maketexstring("notexpanded:");
3594        set_eq_type(frozen_primitive, ignore_spaces_cmd);
3595        set_equiv(frozen_primitive, 1);
3596        set_eq_level(frozen_primitive, level_one);
3597        cs_text(frozen_primitive) = maketexstring("primitive");
3598        create_null_font();
3599        font_bytes = 0;
3600        pdf_h_origin = one_inch;
3601        pdf_v_origin = one_inch;
3602        pdf_compress_level = 9;
3603        pdf_objcompresslevel = 0;
3604        pdf_decimal_digits = 3;
3605        pdf_image_resolution = 72;
3606        pdf_minor_version = 4;
3607        pdf_gamma = 1000;
3608        pdf_image_gamma = 2200;
3609        pdf_image_hicolor = 1;
3610        pdf_image_apply_gamma = 0;
3611        pdf_px_dimen = one_bp;
3612        pdf_draftmode = 0;
3613        cs_text(frozen_protection) = maketexstring("inaccessible");
3614        format_ident = maketexstring(" (INITEX)");
3615        cs_text(end_write) = maketexstring("endwrite");
3616        set_eq_level(end_write, level_one);
3617        set_eq_type(end_write, outer_call_cmd);
3618        set_equiv(end_write, null);
3619
3620    }
3621    synctexoffset = int_base + synctex_code;
3622
3623}
3624