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