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