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