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