3 % Copyright
2009-2010 Taco Hoekwater
<taco@@luatex.org
>
5 % This file is part of LuaTeX.
7 % LuaTeX is free software
; you can redistribute it and
/or modify it under
8 % the terms of the GNU General Public License as published by the Free
9 % Software Foundation
; either version
2 of the License
, or
(at your
10 % option
) any later version.
12 % LuaTeX is distributed in the hope that it will be useful
, but WITHOUT
13 % ANY WARRANTY
; without even the implied warranty of MERCHANTABILITY or
14 % FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 % License for more details.
17 % You should have received a copy of the GNU General Public License along
18 % with LuaTeX
; if not
, see
<http
://www.gnu.org
/licenses
/>.
23 #include
"lua/luatex-api.h"
25 /* these will move to equivalents.h
*/
28 #define lp_code_base
2
29 #define rp_code_base
3
30 #define ef_code_base
4
33 #define gp_code_base
7
35 #define prev_depth cur_list.prev_depth_field
36 #define space_factor cur_list.space_factor_field
37 #define par_shape_ptr equiv
(par_shape_loc
)
39 #define cur_lang int_par
(cur_lang_code
)
40 #define global_defs int_par
(global_defs_code
)
41 #define output_box int_par
(output_box_code
)
42 #define end_line_char int_par
(end_line_char_code
)
43 #define new_line_char int_par
(new_line_char_code
)
44 #define tracing_online int_par
(tracing_online_code
)
45 #define no_local_whatsits int_par
(no_local_whatsits_code
)
46 #define no_local_dirs int_par
(no_local_dirs_code
)
47 #define err_help equiv
(err_help_loc
)
48 #define every_par equiv
(every_par_loc
)
50 #define page_left_offset dimen_par
(page_left_offset_code
)
51 #define page_top_offset dimen_par
(page_top_offset_code
)
52 #define page_right_offset dimen_par
(page_right_offset_code
)
53 #define page_bottom_offset dimen_par
(page_bottom_offset_code
)
54 #define px_dimen dimen_par
(px_dimen_code
)
56 #define math_eqno_gap_step int_par
(math_eqno_gap_step_code
)
58 #define escape_char int_par
(escape_char_code
)
59 #define max_dead_cycles int_par
(max_dead_cycles_code
)
60 #define tolerance int_par
(tolerance_code
)
61 #define mag int_par
(mag_code
)
62 #define cat_code_table int_par
(cat_code_table_code
)
64 #define par_indent dimen_par
(par_indent_code
)
65 #define looseness int_par
(looseness_code
)
66 #define space_skip glue_par
(space_skip_code
)
67 #define xspace_skip glue_par
(xspace_skip_code
)
68 #define math_skip glue_par
(math_skip_code
)
69 #define every_vbox equiv
(every_vbox_loc
)
71 #define split_top_skip glue_par
(split_top_skip_code
)
72 #define split_max_depth dimen_par
(split_max_depth_code
)
74 #define hang_indent dimen_par
(hang_indent_code
)
75 #define hang_after int_par
(hang_after_code
)
76 #define inter_line_penalties_ptr equiv
(inter_line_penalties_loc
)
78 #define box
(A
) eqtb
[box_base
+(A
)].hh.rh
79 #define cur_font equiv
(cur_font_loc
)
80 #define hsize dimen_par
(hsize_code
)
81 #define ex_hyphen_char int_par
(ex_hyphen_char_code
)
82 #define floating_penalty int_par
(floating_penalty_code
)
84 #define mode cur_list.mode_field
85 #define tail cur_list.tail_field
86 #define head cur_list.head_field
87 #define prev_graf cur_list.pg_field
88 #define dir_save cur_list.dirs_field
90 #define check_filter
(A
) if
(!output_active
) lua_node_filter_s
(buildpage_filter_callback
,lua_key_index
(A
))
92 #define var_code
7 /* math code meaning ``use the current family''
*/
94 @ We come now to the |main_control| routine
, which contains the master
95 switch that causes all the various pieces of \TeX\ to do their things
,
98 In a sense
, this is the grand climax of the program
: It applies all the
99 tools that we have worked so hard to construct. In another sense
, this is
100 the messiest part of the program
: It necessarily refers to other pieces
101 of code all over the place
, so that a person can't fully understand what is
102 going on without paging back and forth to be reminded of conventions that
103 are defined elsewhere. We are now at the hub of the web
, the central nervous
104 system that touches most of the other parts and ties them together.
107 The structure of |main_control| itself is quite simple. There's a label
108 called |big_switch|
, at which point the next token of input is fetched
109 using |get_x_token|. Then the program branches at high speed into one of
110 about
100 possible directions
, based on the value of the current
111 mode and the newly fetched command code
; the sum |abs
(mode
)+cur_cmd|
112 indicates what to do next. For example
, the case `|vmode
+letter|' arises
113 when a letter occurs in vertical mode
(or internal vertical mode
); this
114 case leads to instructions that initialize a new paragraph and enter
117 The big |case| statement that contains this multiway switch has been labeled
118 |reswitch|
, so that the program can |goto reswitch| when the next token
119 has already been fetched. Most of the cases are quite short
; they call
120 an ``action procedure'' that does the work for that case
, and then they
121 either |goto reswitch| or they ``fall through'' to the end of the |case|
122 statement
, which returns control back to |big_switch|. Thus
, |main_control|
123 is not an extremely large procedure
, in spite of the multiplicity of things
124 it must do
; it is small enough to be handled by
PASCAL compilers that put
125 severe restrictions on procedure size.
126 @
!@^action procedure@
>
128 One case is singled out for special treatment
, because it accounts for most
129 of \TeX's activities in typical applications. The process of reading simple
130 text and converting it into |char_node| records
, while looking for ligatures
131 and kerns
, is part of \TeX's ``inner loop''
; the whole program runs
132 efficiently when its inner loop is fast
, so this part has been written
133 with particular care.
136 static halfword main_p
; /* temporary register for list manipulation
*/
137 static halfword main_s
; /* space factor value
*/
139 @ We leave the |space_factor| unchanged if |sf_code
(cur_chr
)=0|
; otherwise we
140 set it equal to |sf_code
(cur_chr
)|
, except that it should never change
141 from a value less than
1000 to a value exceeding
1000. The most common
142 case is |sf_code
(cur_chr
)=1000|
, so we want that case to be fast.
145 void adjust_space_factor
(void
)
147 main_s
= get_sf_code
(cur_chr
);
148 if
(main_s
== 1000) {
150 } else if
(main_s
< 1000) {
152 space_factor
= main_s
;
153 } else if
(space_factor
< 1000) {
156 space_factor
= main_s
;
160 @ From Knuth
: ``Having |font_glue| allocated for each text font saves
161 both time and memory.'' That may be true
, but it also punches through
162 the API wall for fonts
, so I removed that
-- Taco. But a bit of caching
163 is very welcome
, which is why I need to have the next two globals
:
166 internal_font_number space_spec_font
;
167 halfword space_spec_cache
;
169 @ To handle the execution state of |main_control|'s eternal loop
,
170 an extra global variable is used
, along with a macro to define
175 #define goto_skip_token
1
176 #define goto_return
2
178 static int main_control_state
;
180 @
* Main control helpers.
182 Here are all the functions that are called from |main_control| that
183 are not already defined elsewhere. For the moment
, this list simply
184 in the order that the appear in |init_main_control|
, below.
188 static void run_char_num
(void
) {
191 adjust_space_factor
();
192 tail_append
(new_char
(cur_font
, cur_chr
));
195 static void run_char
(void
) {
196 adjust_space_factor
();
197 tail_append
(new_char
(cur_font
, cur_chr
));
201 The occurrence of blank spaces is almost part of \TeX's inner loop
,
202 since we usually encounter about one space for every five non-blank characters.
203 Therefore |main_control| gives second-highest priority to ordinary spaces.
205 When a glue parameter like \.
{\\spaceskip
} is set to `\.
{0pt
}'
, we will
206 see to it later that the corresponding glue specification is precisely
207 |zero_glue|
, not merely a pointer to some specification that happens
208 to be full of zeroes. Therefore it is simple to test whether a glue parameter
212 static void run_app_space
(void
) {
213 int method
= int_par
(disable_space_code
) ;
215 /* don't inject anything
, not even zero skip
*/
216 } else if
(method
== 2) {
217 temp_ptr
= new_glue
(zero_glue
);
218 couple_nodes
(tail
,temp_ptr
);
220 } else if
((abs
(mode
) + cur_cmd
== hmode
+ spacer_cmd
) && (!(space_factor == 1000))) {
223 /* Append a normal inter-word space to the current list
*/
224 if
(space_skip
== zero_glue
) {
225 /* Find the glue specification
, |main_p|
, for
226 text spaces in the current font
*/
227 if
(cur_font
!= space_spec_font
) {
228 if
(space_spec_cache
!= zero_glue
)
229 delete_glue_ref
(space_spec_cache
);
230 space_spec_cache
= new_spec
(zero_glue
);
231 width
(space_spec_cache
) = space
(cur_font
);
232 stretch
(space_spec_cache
) = space_stretch
(cur_font
);
233 shrink
(space_spec_cache
) = space_shrink
(cur_font
);
234 space_spec_font
= cur_font
;
236 main_p
= space_spec_cache
;
237 temp_ptr
= new_glue
(main_p
);
239 temp_ptr
= new_param_glue
(space_skip_code
);
241 couple_nodes
(tail
,temp_ptr
);
246 @ Append a |boundary_node|
248 static void run_no_boundary
(void
) {
250 n
= new_node
(boundary_node
,cancel_boundary
);
251 couple_nodes
(tail
, n
);
254 static void run_boundary
(void
) {
256 n
= new_node
(boundary_node
,user_boundary
);
258 boundary_value
(n
) = cur_val
;
259 couple_nodes
(tail
, n
);
264 static void run_char_ghost
(void
) {
268 if
((cur_cmd
== letter_cmd
) ||
(cur_cmd
== other_char_cmd
)
269 ||
(cur_cmd
== char_given_cmd
) ||
(cur_cmd
== char_num_cmd
)) {
270 halfword p
= new_glyph
(get_cur_font
(), cur_chr
);
274 set_is_rightghost
(p
);
281 static void run_relax
(void
) {
285 @ |ignore_spaces| is a special case
: after it has acted
, |get_x_token| has already
286 fetched the next token from the input
, so that operation in |main_control|
290 static void run_ignore_spaces
(void
) {
292 /* Get the next non-blank non-call...
*/
295 } while
(cur_cmd
== spacer_cmd
);
296 main_control_state
= goto_skip_token
;
298 int t
= scanner_status
;
299 scanner_status
= normal
;
302 cur_cs
= prim_lookup
(cs_text
(cur_cs
));
303 if
(cur_cs
!= undefined_primitive
) {
304 cur_cmd
= get_prim_eq_type
(cur_cs
);
305 cur_chr
= get_prim_equiv
(cur_cs
);
306 cur_tok
= (cur_cmd
* STRING_OFFSET
) + cur_chr
;
307 main_control_state
= goto_skip_token
;
312 @ |stop| is the second special case. We want |main_control| to return to its caller
313 if there is nothing left to do.
316 static void run_stop
(void
) {
318 main_control_state
= goto_return
; /* this is the only way out
*/
322 static void run_non_math_math
(void
) {
328 static void run_math_char_num
(void
) {
329 mathcodeval mval
; /* to build up an argument to |set_math_char|
*/
331 mval
= scan_mathchar
(tex_mathcode
);
332 else if
(cur_chr
== 1)
333 mval
= scan_mathchar
(umath_mathcode
);
335 mval
= scan_mathchar
(umathnum_mathcode
);
336 math_char_in_text
(mval
);
340 static void run_math_given
(void
) {
341 mathcodeval mval
; /* to build up an argument to |set_math_char|
*/
342 mval
= mathchar_from_integer
(cur_chr
, tex_mathcode
);
343 math_char_in_text
(mval
);
346 static void run_xmath_given
(void
) {
347 mathcodeval mval
; /* to build up an argument to |set_math_char|
*/
348 mval
= mathchar_from_integer
(cur_chr
, umath_mathcode
);
349 math_char_in_text
(mval
);
352 @ The most important parts of |main_control| are concerned with \TeX's
353 chief mission of box-making. We need to control the activities that put
354 entries on vlists and hlists
, as well as the activities that convert
355 those lists into boxes. All of the necessary machinery has already been
356 developed
; it remains for us to ``push the buttons'' at the right times.
358 As an introduction to these routines
, let's consider one of the simplest
359 cases
: What happens when `\.
{\\hrule
}' occurs in vertical mode
, or
360 `\.
{\\vrule
}' in horizontal mode or math mode? The code in |main_control|
361 is short
, since the |scan_rule_spec| routine already does most of what is
362 required
; thus
, there is no need for a special action procedure.
364 Note that baselineskip calculations are disabled after a rule in vertical
365 mode
, by setting |prev_depth
:=ignore_depth|.
368 static void run_rule
(void
) {
369 tail_append
(scan_rule_spec
());
370 if
(abs
(mode
) == vmode
)
371 prev_depth
= ignore_depth
;
372 else if
(abs
(mode
) == hmode
)
377 Many of the actions related to box-making are triggered by the appearance
378 of braces in the input. For example
, when the user says `\.
{\\hbox
}
379 \.
{to
} \.
{100pt\
{$\langle\
,\hbox
{hlist
}\
,\rangle$\
}}' in vertical mode
,
380 the information about the box size
(100pt
, |exactly|
) is put onto |save_stack|
381 with a level boundary word just above it
, and |cur_group
:=adjusted_hbox_group|
;
382 \TeX\ enters restricted horizontal mode to process the hlist. The right
383 brace eventually causes |save_stack| to be restored to its former state
,
384 at which time the information about the box size
(100pt
, |exactly|
) is
385 available once again
; a box is packaged and we leave restricted horizontal
386 mode
, appending the new box to the current list of the enclosing mode
387 (in this case to the current list of vertical mode
), followed by any
388 vertical adjustments that were removed from the box by |hpack|.
390 The next few sections of the program are therefore concerned with the
391 treatment of left and right curly braces.
393 If a left brace occurs in the middle of a page or paragraph
, it simply
394 introduces a new level of grouping
, and the matching right brace will not have
395 such a drastic effect. Such grouping affects neither the mode nor the
399 static void run_left_brace
(void
) {
400 new_save_level
(simple_group
);
401 eq_word_define
(int_base
+ no_local_whatsits_code
, 0);
402 eq_word_define
(int_base
+ no_local_dirs_code
, 0);
405 static void run_begin_group
(void
) {
406 new_save_level
(semi_simple_group
);
407 eq_word_define
(int_base
+ no_local_whatsits_code
, 0);
408 eq_word_define
(int_base
+ no_local_dirs_code
, 0);
411 static void run_end_group
(void
) {
412 if
(cur_group
== semi_simple_group
) {
419 @ Constructions that require a box are started by calling |scan_box| with
420 a specified context code. The |scan_box| routine verifies
421 that a |make_box| command comes next and then it calls |begin_box|.
424 static void run_move
(void
) {
434 static void run_leader_ship
(void
) {
435 scan_box
(leader_flag
- a_leaders
+ cur_chr
);
439 static void run_make_box
(void
) {
444 static void run_box_dir
(void
) {
446 cur_box
= box
(cur_val
);
447 scan_optional_equals
();
450 box_dir
(cur_box
) = cur_val
;
453 @ There is a really small patch to add a new primitive called
454 \.
{\\quitvmode
}. In vertical modes
, it is identical to \.
{\\indent
},
455 but in horizontal and math modes it is really a no-op
(as opposed to
456 \.
{\\indent
}, which executes the |indent_in_hmode| procedure
).
458 A paragraph begins when horizontal-mode material occurs in vertical mode
,
459 or when the paragraph is explicitly started by `\.
{\\quitvmode
}'
,
460 `\.
{\\indent
}' or `\.
{\\noindent
}'.
463 static void run_start_par_vmode
(void
) {
464 new_graf
((cur_chr
> 0));
468 static void run_start_par
(void
) {
474 static void run_new_graf
(void
) {
479 @ A paragraph ends when a |par_end| command is sensed
, or when we are in
480 horizontal mode when reaching the right brace of vertical-mode routines
481 like \.
{\\vbox
}, \.
{\\insert
}, or \.
{\\output
}.
484 static void run_par_end_vmode
(void
) {
487 check_filter
(vmode_par
);
493 static void run_par_end_hmode
(void
) {
495 off_save
(); /* this tries to recover from an alignment that didn't end properly
*/
496 end_graf
(bottom_level
); /* this takes us to the enclosing mode
, if |mode
>0|
*/
498 check_filter
(hmode_par
);
504 static void append_italic_correction_mmode
(void
) {
505 tail_append
(new_kern
(0)); /* what subtype to use
*/
509 static void run_local_box
(void
) {
510 append_local_box
(cur_chr
);
514 static void run_halign_mmode
(void
) {
516 if
(cur_group
== math_shift_group
)
524 static void run_eq_no
(void
) {
526 if
(cur_group
== math_shift_group
)
534 static void run_letter_mmode
(void
) {
535 set_math_char
(get_math_code
(cur_chr
));
539 static void run_char_num_mmode
(void
) {
542 set_math_char
(get_math_code
(cur_chr
));
546 static void run_math_char_num_mmode
(void
) {
547 mathcodeval mval
; /* to build up an argument to |set_math_char|
*/
549 mval
= scan_mathchar
(tex_mathcode
);
550 else if
(cur_chr
== 1)
551 mval
= scan_mathchar
(umath_mathcode
);
553 mval
= scan_mathchar
(umathnum_mathcode
);
558 static void run_math_given_mmode
(void
) {
559 mathcodeval mval
; /* to build up an argument to |set_math_char|
*/
560 mval
= mathchar_from_integer
(cur_chr
, tex_mathcode
);
564 static void run_xmath_given_mmode
(void
) {
565 mathcodeval mval
; /* to build up an argument to |set_math_char|
*/
566 mval
= mathchar_from_integer
(cur_chr
, umath_mathcode
);
571 static void run_delim_num
(void
) {
572 mathcodeval mval
; /* to build up an argument to |set_math_char|
*/
574 mval
= scan_delimiter_as_mathchar
(tex_mathcode
);
576 mval
= scan_delimiter_as_mathchar
(umath_mathcode
);
582 static void run_vcenter
(void
) {
583 scan_spec
(vcenter_group
);
587 prev_depth
= ignore_depth
;
588 if
(every_vbox
!= null
)
589 begin_token_list
(every_vbox
, every_vbox_text
);
593 static void run_math_style
(void
) {
594 tail_append
(new_style
((small_number
) cur_chr
));
598 static void run_non_script
(void
) {
599 tail_append
(new_glue
(zero_glue
));
600 subtype
(tail
) = cond_math_glue
;
604 static void run_math_choice
(void
) {
612 static void run_math_shift
(void
) {
613 if
(cur_group
== math_shift_group
)
620 static void run_after_assignment
(void
) {
622 after_token
= cur_tok
;
626 static void run_after_group
(void
) {
628 save_for_after
(cur_tok
);
632 static void run_extension
(void
) {
636 static void run_normal
(void
) {
640 new_whatsit
(save_pos_node
);
642 case save_cat_code_table_code
:
644 if
((cur_val
< 0) ||
(cur_val
> 0x7FFF)) {
645 print_err
("Invalid \\catcode table");
646 help1
("All \\catcode table ids must be between 0 and 0x7FFF");
649 if
(cur_val
== cat_code_table
) {
650 print_err
("Invalid \\catcode table");
651 help1
("You cannot overwrite the current \\catcode table");
654 copy_cat_codes
(cat_code_table
, cur_val
);
658 case init_cat_code_table_code
:
660 if
((cur_val
< 0) ||
(cur_val
> 0x7FFF)) {
661 print_err
("Invalid \\catcode table");
662 help1
("All \\catcode table ids must be between 0 and 0x7FFF");
665 if
(cur_val
== cat_code_table
) {
666 print_err
("Invalid \\catcode table");
667 help1
("You cannot overwrite the current \\catcode table");
670 initex_cat_codes
(cur_val
);
674 case set_random_seed_code
:
675 /* Negative random seed values are silently converted to positive ones
*/
679 random_seed
= cur_val
;
680 init_randoms
(random_seed
);
683 new_whatsit
(late_lua_node
); /* type
== normal
*/
684 late_lua_name
(tail
) = scan_lua_state
();
685 (void
) scan_toks
(false
, false
);
686 late_lua_data
(tail
) = def_ref
;
688 case expand_font_code
:
699 this is experimental and not used for production
, only for testing and writing
704 static void run_option
(void
) {
707 case math_option_code
:
708 if
(scan_keyword
("old")) {
710 word_define
(int_base
+math_old_code
, cur_val
);
711 /* math_no_char_italic
= cur_val
; */
712 } else if
(scan_keyword
("noitaliccompensation")) {
714 word_define
(int_base
+math_no_italic_compensation_code
, cur_val
);
715 /* math_no_italic_compensation
= cur_val
; */
716 } else if
(scan_keyword
("nocharitalic")) {
718 word_define
(int_base
+math_no_char_italic_code
, cur_val
);
719 /* math_no_char_italic
= cur_val
; */
720 } else if
(scan_keyword
("useoldfractionscaling")) {
722 word_define
(int_base
+math_use_old_fraction_scaling_code
, cur_val
);
724 normal_warning
("mathoption","unknown key");
733 @ For mode-independent commands
, the following macro is useful.
735 Also
, there is a list of cases where the user has probably gotten into or out of math
736 mode by mistake. \TeX\ will insert a dollar sign and rescan the current token
, and
737 it makes sense ot have a macro for that as well.
740 #define any_mode
(A
,B
) jump_table
[vmode
+(A
)]=B
; jump_table
[hmode
+(A
)]=B
; jump_table
[mmode
+(A
)]=B
741 #define non_math
(A
,B
) jump_table
[vmode
+(A
)]=B
; jump_table
[hmode
+(A
)]=B
;
744 @ The |main_control| uses a jump table
, and |init_main_control| sets that table up.
746 typedef void
(*main_control_function
) (void
);
747 main_control_function
*jump_table
;
749 static void init_main_control
(void
) {
750 jump_table
= xmalloc
((mmode
+max_command_cmd
+1) * sizeof
(main_control_function
)) ;
752 jump_table
[hmode
+ char_num_cmd
] = run_char_num
;
753 jump_table
[hmode
+ letter_cmd
] = run_char
;
754 jump_table
[hmode
+ other_char_cmd
] = run_char
;
755 jump_table
[hmode
+ char_given_cmd
] = run_char
;
756 jump_table
[hmode
+ spacer_cmd
] = run_app_space
;
757 jump_table
[hmode
+ ex_space_cmd
] = run_app_space
;
758 jump_table
[mmode
+ ex_space_cmd
] = run_app_space
;
759 jump_table
[hmode
+ boundary_cmd
] = run_boundary
;
760 jump_table
[hmode
+ no_boundary_cmd
] = run_no_boundary
;
761 jump_table
[hmode
+ char_ghost_cmd
] = run_char_ghost
;
762 jump_table
[mmode
+ char_ghost_cmd
] = run_char_ghost
;
763 any_mode
(relax_cmd
, run_relax
);
764 jump_table
[vmode
+ spacer_cmd
] = run_relax
;
765 jump_table
[mmode
+ spacer_cmd
] = run_relax
;
766 jump_table
[mmode
+ boundary_cmd
] = run_relax
;
767 jump_table
[mmode
+ no_boundary_cmd
] = run_relax
;
768 any_mode
(ignore_spaces_cmd
,run_ignore_spaces
);
769 jump_table
[vmode
+ stop_cmd
] = run_stop
;
770 jump_table
[vmode
+ math_char_num_cmd
] = run_non_math_math
;
771 jump_table
[vmode
+ math_given_cmd
] = run_non_math_math
;
772 jump_table
[vmode
+ xmath_given_cmd
] = run_non_math_math
;
773 jump_table
[hmode
+ math_char_num_cmd
] = run_math_char_num
;
774 jump_table
[hmode
+ math_given_cmd
] = run_math_given
;
775 jump_table
[hmode
+ xmath_given_cmd
] = run_xmath_given
;
777 jump_table
[vmode
+ vmove_cmd
] = report_illegal_case
;
778 jump_table
[hmode
+ hmove_cmd
] = report_illegal_case
;
779 jump_table
[mmode
+ hmove_cmd
] = report_illegal_case
;
780 any_mode
(last_item_cmd
, report_illegal_case
);
781 jump_table
[vmode
+ vadjust_cmd
] = report_illegal_case
;
782 jump_table
[vmode
+ ital_corr_cmd
] = report_illegal_case
;
783 non_math
(eq_no_cmd
,report_illegal_case
);
784 any_mode
(mac_param_cmd
,report_illegal_case
);
786 non_math
(sup_mark_cmd
, insert_dollar_sign
);
787 non_math
(sub_mark_cmd
, insert_dollar_sign
);
788 non_math
(super_sub_script_cmd
, insert_dollar_sign
);
789 non_math
(math_comp_cmd
, insert_dollar_sign
);
790 non_math
(delim_num_cmd
, insert_dollar_sign
);
791 non_math
(left_right_cmd
, insert_dollar_sign
);
792 non_math
(above_cmd
, insert_dollar_sign
);
793 non_math
(radical_cmd
, insert_dollar_sign
);
794 non_math
(math_style_cmd
, insert_dollar_sign
);
795 non_math
(math_choice_cmd
, insert_dollar_sign
);
796 non_math
(vcenter_cmd
, insert_dollar_sign
);
797 non_math
(non_script_cmd
, insert_dollar_sign
);
798 non_math
(mkern_cmd
, insert_dollar_sign
);
799 non_math
(limit_switch_cmd
, insert_dollar_sign
);
800 non_math
(mskip_cmd
, insert_dollar_sign
);
801 non_math
(math_accent_cmd
, insert_dollar_sign
);
802 jump_table
[mmode
+ endv_cmd
] = insert_dollar_sign
;
803 jump_table
[mmode
+ par_end_cmd
] = insert_dollar_sign_par_end
;
804 jump_table
[mmode
+ stop_cmd
] = insert_dollar_sign
;
805 jump_table
[mmode
+ vskip_cmd
] = insert_dollar_sign
;
806 jump_table
[mmode
+ un_vbox_cmd
] = insert_dollar_sign
;
807 jump_table
[mmode
+ valign_cmd
] = insert_dollar_sign
;
808 jump_table
[mmode
+ hrule_cmd
] = insert_dollar_sign
;
809 jump_table
[mmode
+ no_hrule_cmd
] = insert_dollar_sign
;
810 jump_table
[vmode
+ hrule_cmd
] = run_rule
;
811 jump_table
[vmode
+ no_hrule_cmd
] = run_rule
;
812 jump_table
[hmode
+ vrule_cmd
] = run_rule
;
813 jump_table
[hmode
+ no_vrule_cmd
] = run_rule
;
814 jump_table
[mmode
+ vrule_cmd
] = run_rule
;
815 jump_table
[mmode
+ no_vrule_cmd
] = run_rule
;
816 jump_table
[vmode
+ vskip_cmd
] = append_glue
;
817 jump_table
[hmode
+ hskip_cmd
] = append_glue
;
818 jump_table
[mmode
+ hskip_cmd
] = append_glue
;
819 jump_table
[mmode
+ mskip_cmd
] = append_glue
;
820 any_mode
(kern_cmd
, append_kern
);
821 jump_table
[mmode
+ mkern_cmd
] = append_kern
;
822 non_math
(left_brace_cmd
, run_left_brace
);
823 any_mode
(begin_group_cmd
,run_begin_group
);
824 any_mode
(end_group_cmd
, run_end_group
);
825 any_mode
(right_brace_cmd
, handle_right_brace
);
826 jump_table
[vmode
+ hmove_cmd
] = run_move
;
827 jump_table
[hmode
+ vmove_cmd
] = run_move
;
828 jump_table
[mmode
+ vmove_cmd
] = run_move
;
829 any_mode
(leader_ship_cmd
, run_leader_ship
);
830 any_mode
(make_box_cmd
, run_make_box
);
831 any_mode
(assign_box_dir_cmd
, run_box_dir
);
832 jump_table
[vmode
+ start_par_cmd
] = run_start_par_vmode
;
833 jump_table
[hmode
+ start_par_cmd
] = run_start_par
;
834 jump_table
[mmode
+ start_par_cmd
] = run_start_par
;
835 jump_table
[vmode
+ letter_cmd
] = run_new_graf
;
836 jump_table
[vmode
+ other_char_cmd
] = run_new_graf
;
837 jump_table
[vmode
+ char_num_cmd
] = run_new_graf
;
838 jump_table
[vmode
+ char_given_cmd
] = run_new_graf
;
839 jump_table
[vmode
+ char_ghost_cmd
] = run_new_graf
;
840 jump_table
[vmode
+ math_shift_cmd
] = run_new_graf
;
841 jump_table
[vmode
+ math_shift_cs_cmd
] = run_new_graf
;
842 jump_table
[vmode
+ un_hbox_cmd
] = run_new_graf
;
843 jump_table
[vmode
+ vrule_cmd
] = run_new_graf
;
844 jump_table
[vmode
+ no_vrule_cmd
] = run_new_graf
;
845 jump_table
[vmode
+ accent_cmd
] = run_new_graf
;
846 jump_table
[vmode
+ discretionary_cmd
] = run_new_graf
;
847 jump_table
[vmode
+ hskip_cmd
] = run_new_graf
;
848 jump_table
[vmode
+ valign_cmd
] = run_new_graf
;
849 jump_table
[vmode
+ ex_space_cmd
] = run_new_graf
;
850 jump_table
[vmode
+ boundary_cmd
] = run_new_graf
;
851 jump_table
[vmode
+ no_boundary_cmd
] = run_new_graf
;
852 jump_table
[vmode
+ par_end_cmd
] = run_par_end_vmode
;
853 jump_table
[hmode
+ par_end_cmd
] = run_par_end_hmode
;
854 jump_table
[hmode
+ stop_cmd
] = head_for_vmode
;
855 jump_table
[hmode
+ vskip_cmd
] = head_for_vmode
;
856 jump_table
[hmode
+ hrule_cmd
] = head_for_vmode
;
857 jump_table
[hmode
+ no_hrule_cmd
] = head_for_vmode
;
858 jump_table
[hmode
+ un_vbox_cmd
] = head_for_vmode
;
859 jump_table
[hmode
+ halign_cmd
] = head_for_vmode
;
860 any_mode
(insert_cmd
,begin_insert_or_adjust
);
861 jump_table
[hmode
+ vadjust_cmd
] = begin_insert_or_adjust
;
862 jump_table
[mmode
+ vadjust_cmd
] = begin_insert_or_adjust
;
863 any_mode
(mark_cmd
, handle_mark
);
864 any_mode
(break_penalty_cmd
, append_penalty
);
865 any_mode
(remove_item_cmd
, delete_last
);
866 jump_table
[vmode
+ un_vbox_cmd
] = unpackage
;
867 jump_table
[hmode
+ un_hbox_cmd
] = unpackage
;
868 jump_table
[mmode
+ un_hbox_cmd
] = unpackage
;
869 jump_table
[hmode
+ ital_corr_cmd
] = append_italic_correction
;
870 jump_table
[mmode
+ ital_corr_cmd
] = append_italic_correction_mmode
;
871 jump_table
[hmode
+ discretionary_cmd
] = append_discretionary
;
872 jump_table
[mmode
+ discretionary_cmd
] = append_discretionary
;
873 any_mode
(assign_local_box_cmd
, run_local_box
);
874 jump_table
[hmode
+ accent_cmd
] = make_accent
;
875 any_mode
(car_ret_cmd
,align_error
);
876 any_mode
(tab_mark_cmd
,align_error
);
877 any_mode
(no_align_cmd
,no_align_error
);
878 any_mode
(omit_cmd
, omit_error
);
879 jump_table
[vmode
+ halign_cmd
] = init_align
;
880 jump_table
[hmode
+ valign_cmd
] = init_align
;
881 jump_table
[mmode
+ halign_cmd
] = run_halign_mmode
;
882 jump_table
[vmode
+ endv_cmd
] = do_endv
;
883 jump_table
[hmode
+ endv_cmd
] = do_endv
;
884 any_mode
(end_cs_name_cmd
, cs_error
);
885 jump_table
[hmode
+ math_shift_cmd
] = init_math
;
886 jump_table
[hmode
+ math_shift_cs_cmd
] = init_math
;
887 jump_table
[mmode
+ eq_no_cmd
] = run_eq_no
;
888 jump_table
[mmode
+ left_brace_cmd
] = math_left_brace
;
889 jump_table
[mmode
+ letter_cmd
] = run_letter_mmode
;
890 jump_table
[mmode
+ other_char_cmd
] = run_letter_mmode
;
891 jump_table
[mmode
+ char_given_cmd
] = run_letter_mmode
;
892 jump_table
[mmode
+ char_num_cmd
] = run_char_num_mmode
;
893 jump_table
[mmode
+ math_char_num_cmd
] = run_math_char_num_mmode
;
894 jump_table
[mmode
+ math_given_cmd
] = run_math_given_mmode
;
895 jump_table
[mmode
+ xmath_given_cmd
] = run_xmath_given_mmode
;
896 jump_table
[mmode
+ delim_num_cmd
] = run_delim_num
;
897 jump_table
[mmode
+ math_comp_cmd
] = math_math_comp
;
898 jump_table
[mmode
+ limit_switch_cmd
] = math_limit_switch
;
899 jump_table
[mmode
+ radical_cmd
] = math_radical
;
900 jump_table
[mmode
+ accent_cmd
] = math_ac
;
901 jump_table
[mmode
+ math_accent_cmd
] = math_ac
;
902 jump_table
[mmode
+ vcenter_cmd
] = run_vcenter
;
903 jump_table
[mmode
+ math_style_cmd
] = run_math_style
;
904 jump_table
[mmode
+ non_script_cmd
] = run_non_script
;
905 jump_table
[mmode
+ math_choice_cmd
] = run_math_choice
;
906 jump_table
[mmode
+ above_cmd
] = math_fraction
;
907 jump_table
[mmode
+ sub_mark_cmd
] = sub_sup
;
908 jump_table
[mmode
+ sup_mark_cmd
] = sub_sup
;
909 jump_table
[mmode
+ super_sub_script_cmd
] = sub_sup
;
910 jump_table
[mmode
+ left_right_cmd
] = math_left_right
;
911 jump_table
[mmode
+ math_shift_cmd
] = run_math_shift
;
912 jump_table
[mmode
+ math_shift_cs_cmd
] = run_math_shift
;
913 any_mode
(toks_register_cmd
, prefixed_command
);
914 any_mode
(assign_toks_cmd
, prefixed_command
);
915 any_mode
(assign_int_cmd
, prefixed_command
);
916 any_mode
(assign_attr_cmd
, prefixed_command
);
917 any_mode
(assign_dir_cmd
, prefixed_command
);
918 any_mode
(assign_dimen_cmd
, prefixed_command
);
919 any_mode
(assign_glue_cmd
, prefixed_command
);
920 any_mode
(assign_mu_glue_cmd
, prefixed_command
);
921 any_mode
(assign_font_dimen_cmd
, prefixed_command
);
922 any_mode
(assign_font_int_cmd
, prefixed_command
);
923 any_mode
(set_aux_cmd
, prefixed_command
);
924 any_mode
(set_prev_graf_cmd
, prefixed_command
);
925 any_mode
(set_page_dimen_cmd
, prefixed_command
);
926 any_mode
(set_page_int_cmd
, prefixed_command
);
927 any_mode
(set_box_dimen_cmd
, prefixed_command
);
928 any_mode
(set_tex_shape_cmd
, prefixed_command
);
929 any_mode
(set_etex_shape_cmd
, prefixed_command
);
930 any_mode
(def_char_code_cmd
, prefixed_command
);
931 any_mode
(def_del_code_cmd
, prefixed_command
);
932 any_mode
(extdef_math_code_cmd
, prefixed_command
);
933 any_mode
(extdef_del_code_cmd
, prefixed_command
);
934 any_mode
(def_family_cmd
, prefixed_command
);
935 any_mode
(set_math_param_cmd
, prefixed_command
);
936 any_mode
(set_font_cmd
, prefixed_command
);
937 any_mode
(def_font_cmd
, prefixed_command
);
938 any_mode
(letterspace_font_cmd
, prefixed_command
);
939 any_mode
(copy_font_cmd
, prefixed_command
);
940 any_mode
(set_font_id_cmd
, prefixed_command
);
941 any_mode
(register_cmd
, prefixed_command
);
942 any_mode
(advance_cmd
, prefixed_command
);
943 any_mode
(multiply_cmd
, prefixed_command
);
944 any_mode
(divide_cmd
, prefixed_command
);
945 any_mode
(prefix_cmd
, prefixed_command
);
946 any_mode
(let_cmd
, prefixed_command
);
947 any_mode
(shorthand_def_cmd
, prefixed_command
);
948 any_mode
(read_to_cs_cmd
, prefixed_command
);
949 any_mode
(def_cmd
, prefixed_command
);
950 any_mode
(set_box_cmd
, prefixed_command
);
951 any_mode
(hyph_data_cmd
, prefixed_command
);
952 any_mode
(set_interaction_cmd
, prefixed_command
);
953 any_mode
(after_assignment_cmd
,run_after_assignment
);
954 any_mode
(after_group_cmd
,run_after_group
);
955 any_mode
(in_stream_cmd
,open_or_close_in
);
956 any_mode
(message_cmd
,issue_message
);
957 any_mode
(case_shift_cmd
, shift_case
);
958 any_mode
(xray_cmd
, show_whatever
);
959 any_mode
(normal_cmd
, run_normal
);
960 any_mode
(extension_cmd
, run_extension
);
961 any_mode
(option_cmd
, run_option
);
964 @ And here is |main_control| itself. It is quite short nowadays.
967 void main_control
(void
)
969 main_control_state
= goto_next
;
970 init_main_control
() ;
972 if
(equiv
(every_job_loc
) != null
)
973 begin_token_list
(equiv
(every_job_loc
), every_job_text
);
976 if
(main_control_state
== goto_skip_token
)
977 main_control_state
= goto_next
; /* reset
*/
981 /* Give diagnostic information
, if requested
*/
982 /* When a new token has just been fetched at |big_switch|
, we have an
983 ideal place to monitor \TeX's activity.
*/
984 if
(interrupt
!= 0 && OK_to_interrupt) {
989 if
(int_par
(tracing_commands_code
) > 0)
992 (jump_table
[(abs
(mode
) + cur_cmd
)])(); /* run the command
*/
994 if
(main_control_state
== goto_return
) {
998 return
; /* not reached
*/
1002 void app_space
(void
)
1003 { /* handle spaces when |space_factor
<>1000|
*/
1004 halfword q
; /* glue node
*/
1005 if
((space_factor
>= 2000) && (xspace_skip != zero_glue)) {
1006 q
= new_param_glue
(xspace_skip_code
);
1008 if
(space_skip
!= zero_glue
) {
1009 main_p
= new_spec
(space_skip
);
1011 main_p
= new_spec
(zero_glue
);
1012 width
(main_p
) = space
(cur_font
);
1013 stretch
(main_p
) = space_stretch
(cur_font
);
1014 shrink
(main_p
) = space_shrink
(cur_font
);
1016 /* Modify the glue specification in |main_p| according to the space factor
*/
1017 if
(space_factor
>= 2000)
1018 width
(main_p
) = width
(main_p
) + extra_space
(cur_font
);
1019 stretch
(main_p
) = xn_over_d
(stretch
(main_p
), space_factor
, 1000);
1020 shrink
(main_p
) = xn_over_d
(shrink
(main_p
), 1000, space_factor
);
1021 q
= new_glue
(main_p
);
1022 glue_ref_count
(main_p
) = null
;
1024 couple_nodes
(tail
, q
);
1029 void insert_dollar_sign
(void
)
1032 cur_tok
= math_shift_token
+ '$'
;
1033 print_err
("Missing $ inserted");
1034 help2
("I've inserted a begin-math/end-math symbol since I think",
1035 "you left one out. Proceed, with fingers crossed.");
1039 @ We can silently ignore \.
{\\par
}s in a math formula.
1042 void insert_dollar_sign_par_end
(void
)
1044 if
(!int_par
(suppress_mathpar_error_code
)) {
1045 insert_dollar_sign
() ;
1049 @ The `|you_cant|' procedure prints a line saying that the current command
1050 is illegal in the current mode
; it identifies these things symbolically.
1055 print_err
("You can't use `");
1056 print_cmd_chr
((quarterword
) cur_cmd
, cur_chr
);
1057 print_in_mode
(mode
);
1061 When erroneous situations arise
, \TeX\ usually issues an error message
1062 specific to the particular error. For example
, `\.
{\\noalign
}' should
1063 not appear in any mode
, since it is recognized by the |align_peek| routine
1064 in all of its legitimate appearances
; a special error message is given
1065 when `\.
{\\noalign
}' occurs elsewhere. But sometimes the most appropriate
1066 error message is simply that the user is not allowed to do what he or she
1067 has attempted. For example
, `\.
{\\moveleft
}' is allowed only in vertical mode
,
1068 and `\.
{\\lower
}' only in non-vertical modes. Such cases are enumerated
1069 here and in the other sections referred to under `See also \dots.'
1072 void report_illegal_case
(void
)
1075 help4
("Sorry, but I'm not programmed to handle this case;",
1076 "I'll just pretend that you didn''t ask for it.",
1077 "If you're in the wrong mode, you might be able to",
1078 "return to the right one by typing `I}' or `I$' or `I\\par'.");
1082 @ Some operations are allowed only in privileged modes
, i.e.
, in cases
1083 that |mode
>0|. The |privileged| function is used to detect violations
1084 of this rule
; it issues an error message and returns |false| if the
1085 current |mode| is negative.
1088 boolean privileged
(void
)
1093 report_illegal_case
();
1098 @ We don't want to leave |main_control| immediately when a |stop| command
1099 is sensed
, because it may be necessary to invoke an \.
{\\output
} routine
1100 several times before things really grind to a halt.
(The output routine
1101 might even say `\.
{\\gdef\\end\
{...\
}}'
, to prolong the life of the job.
)
1102 Therefore |its_all_over| is |true| only when the current page
1103 and contribution list are empty
, and when the last output was not a
1107 boolean its_all_over
(void
)
1108 { /* do this when \.
{\\end
} or \.
{\\dump
} occurs
*/
1110 if
((page_head
== page_tail
) && (head == tail) && (dead_cycles == 0)) {
1113 back_input
(); /* we will try to end again after ejecting residual material
*/
1114 tail_append
(new_null_box
());
1115 width
(tail
) = hsize
;
1116 tail_append
(new_glue
(fill_glue
));
1117 tail_append
(new_penalty
(-010000000000));
1118 lua_node_filter_s
(buildpage_filter_callback
,lua_key_index
(end
));
1119 build_page
(); /* append \.
{\\hbox to \\hsize\
{\
}\\vfill\\penalty-'
10000000000} */
1125 @ The |hskip| and |vskip| command codes are used for control sequences
1126 like \.
{\\hss
} and \.
{\\vfil
} as well as for \.
{\\hskip
} and \.
{\\vskip
}.
1127 The difference is in the value of |cur_chr|.
1129 All the work relating to glue creation has been relegated to the
1130 following subroutine. It does not call |build_page|
, because it is
1131 used in at least one place where that would be a mistake.
1134 void append_glue
(void
)
1136 int s
; /* modifier of skip command
*/
1143 cur_val
= fill_glue
;
1149 cur_val
= fil_neg_glue
;
1152 scan_glue
(glue_val_level
);
1155 scan_glue
(mu_val_level
);
1157 } /* now |cur_val| points to the glue specification
*/
1158 tail_append
(new_glue
(cur_val
));
1159 if
(s
>= skip_code
) {
1160 decr
(glue_ref_count
(cur_val
));
1162 subtype
(tail
) = mu_glue
;
1167 void append_kern
(void
)
1169 int s
; /* |subtype| of the kern node
*/
1171 scan_dimen
((s
== mu_glue
), false
, false
);
1172 tail_append
(new_kern
(cur_val
));
1173 subtype
(tail
) = (quarterword
) s
;
1176 @ We have to deal with errors in which braces and such things are not
1177 properly nested. Sometimes the user makes an error of commission by
1178 inserting an extra symbol
, but sometimes the user makes an error of omission.
1179 \TeX\ can't always tell one from the other
, so it makes a guess and tries
1180 to avoid getting into a loop.
1182 The |off_save| routine is called when the current group code is wrong. It tries
1183 to insert something into the user's input that will help clean off
1189 halfword p
, q
; /* inserted token
*/
1190 if
(cur_group
== bottom_level
) {
1191 /* Drop current token and complain that it was unmatched
*/
1192 print_err
("Extra ");
1193 print_cmd_chr
((quarterword
) cur_cmd
, cur_chr
);
1194 help1
("Things are pretty mixed up, but I think the worst is over.");
1200 set_token_link
(temp_token_head
, p
);
1201 print_err
("Missing ");
1202 /* Prepare to insert a token that matches |cur_group|
, and print what it is
*/
1203 /* At this point
, |link
(temp_token_head
)=p|
, a pointer to an empty one-word node.
*/
1204 switch
(cur_group
) {
1205 case semi_simple_group
:
1206 set_token_info
(p
, cs_token_flag
+ frozen_end_group
);
1207 tprint_esc
("endgroup");
1209 case math_shift_group
:
1210 set_token_info
(p
, math_shift_token
+ '$'
);
1213 case math_left_group
:
1214 set_token_info
(p
, cs_token_flag
+ frozen_right
);
1216 set_token_link
(p
, q
);
1218 set_token_info
(p
, other_token
+ '.'
);
1219 tprint_esc
("right.");
1222 set_token_info
(p
, right_brace_token
+ '
}'
);
1226 tprint
(" inserted");
1227 ins_list
(token_link
(temp_token_head
));
1228 help5
("I've inserted something that you may have forgotten.",
1229 "(See the <inserted text> above.)",
1230 "With luck, this will get me unwedged. But if you",
1231 "really didn't forget anything, try typing `2' now; then",
1232 "my insertion and my current dilemma will both disappear.");
1237 @ The routine for a |right_brace| character branches into many subcases
,
1238 since a variety of things may happen
, depending on |cur_group|. Some
1239 types of groups are not supposed to be ended by a right brace
; error
1240 messages are given in hopes of pinpointing the problem. Most branches
1241 of this routine will be filled in later
, when we are ready to understand
1242 them
; meanwhile
, we must prepare ourselves to deal with such errors.
1245 void handle_right_brace
(void
)
1247 halfword p
, q
; /* for short-term use
*/
1248 scaled d
; /* holds |split_max_depth| in |insert_group|
*/
1249 int f
; /* holds |floating_penalty| in |insert_group|
*/
1251 switch
(cur_group
) {
1256 print_err
("Too many }'s");
1257 help2
("You've closed more groups than you opened.",
1258 "Such booboos are generally harmless, so keep going.");
1261 case semi_simple_group
:
1262 case math_shift_group
:
1263 case math_left_group
:
1264 extra_right_brace
();
1267 /* When the right brace occurs at the end of an \.
{\\hbox
} or \.
{\\vbox
} or
1268 \.
{\\vtop
} construction
, the |package| routine comes into action. We might
1269 also have to finish a paragraph that hasn't ended.
*/
1272 case adjusted_hbox_group
:
1273 adjust_tail
= adjust_head
;
1274 pre_adjust_tail
= pre_adjust_head
;
1278 end_graf
(vbox_group
);
1282 end_graf
(vtop_group
);
1286 end_graf
(insert_group
);
1289 d
= split_max_depth
;
1290 f
= floating_penalty
;
1293 /* now |saved_value
(0)| is the insertion number
, or the |vadjust| subtype
*/
1294 p
= vpack
(vlink
(head
), 0, additional
, -1);
1296 if
(saved_type
(0) == saved_insert
) {
1297 tail_append
(new_node
(ins_node
, saved_value
(0)));
1298 height
(tail
) = height
(p
) + depth
(p
);
1299 ins_ptr
(tail
) = list_ptr
(p
);
1300 split_top_ptr
(tail
) = q
;
1302 float_cost
(tail
) = f
;
1303 } else if
(saved_type
(0) == saved_adjust
) {
1304 tail_append
(new_node
(adjust_node
, saved_value
(0)));
1305 adjust_ptr
(tail
) = list_ptr
(p
);
1308 confusion
("insert_group");
1312 if
(nest_ptr
== 0) {
1313 check_filter
(insert
);
1318 /* this is needed in case the \.
{\\output
} executes a \.
{\\textdir
} command.
*/
1319 if
(dir_level
(text_dir_ptr
) == cur_level
) {
1320 /* DIR: Remove from |text_dir_ptr|
*/
1321 halfword text_dir_tmp
= vlink
(text_dir_ptr
);
1322 flush_node
(text_dir_ptr
);
1323 text_dir_ptr
= text_dir_tmp
;
1325 resume_after_output
();
1328 build_discretionary
();
1330 case local_box_group
:
1335 cur_tok
= cs_token_flag
+ frozen_cr
;
1336 print_err
("Missing \\cr inserted");
1337 help1
("I'm guessing that you meant to end an alignment here.");
1340 case no_align_group
:
1341 end_graf
(no_align_group
);
1346 end_graf
(vcenter_group
);
1349 case math_choice_group
:
1353 close_math_group
(p
);
1356 confusion
("rightbrace");
1362 void extra_right_brace
(void
)
1364 print_err
("Extra }, or forgotten ");
1365 switch
(cur_group
) {
1366 case semi_simple_group
:
1367 tprint_esc
("endgroup");
1369 case math_shift_group
:
1372 case math_left_group
:
1373 tprint_esc
("right");
1376 help5
("I've deleted a group-closing symbol because it seems to be",
1377 "spurious, as in `$x}$'. But perhaps the } is legitimate and",
1378 "you forgot something else, as in `\\hbox{$x}'. In such cases",
1379 "the way to recover is to insert both the forgotten and the",
1380 "deleted material, e.g., by typing `I$}'.");
1385 @ Here is where we clear the parameters that are supposed to revert to their
1386 default values after every paragraph and when internal vertical mode is entered.
1389 void normal_paragraph
(void
)
1392 eq_word_define
(int_base
+ looseness_code
, 0);
1393 if
(hang_indent
!= 0)
1394 eq_word_define
(dimen_base
+ hang_indent_code
, 0);
1395 if
(hang_after
!= 1)
1396 eq_word_define
(int_base
+ hang_after_code
, 1);
1397 if
(par_shape_ptr
!= null
)
1398 eq_define
(par_shape_loc
, shape_ref_cmd
, null
);
1399 if
(inter_line_penalties_ptr
!= null
)
1400 eq_define
(inter_line_penalties_loc
, shape_ref_cmd
, null
);
1403 @ The global variable |cur_box| will point to a newly-made box. If the box
1404 is void
, we will have |cur_box
=null|. Otherwise we will have
1405 |type
(cur_box
)=hlist_node| or |vlist_node| or |rule_node|
; the |rule_node|
1406 case can occur only with leaders.
1409 halfword cur_box
; /* box to be placed into its context
*/
1411 @ The |box_end| procedure does the right thing with |cur_box|
, if
1412 |box_context| represents the context as explained above.
1415 void box_end
(int box_context
)
1417 if
(box_context
< box_flag
) {
1418 /* Append box |cur_box| to the current list
, shifted by |box_context|
*/
1420 The global variable |adjust_tail| will be non-null if and only if the
1421 current box might include adjustments that should be appended to the
1422 current vertical list.
1424 if
(cur_box
!= null
) {
1425 shift_amount
(cur_box
) = box_context
;
1426 if
(abs
(mode
) == vmode
) {
1427 if
(pre_adjust_tail
!= null
) {
1428 if
(pre_adjust_head
!= pre_adjust_tail
)
1429 append_list
(pre_adjust_head
, pre_adjust_tail
);
1430 pre_adjust_tail
= null
;
1432 append_to_vlist
(cur_box
,lua_key_index
(box
));
1433 if
(adjust_tail
!= null
) {
1434 if
(adjust_head
!= adjust_tail
)
1435 append_list
(adjust_head
, adjust_tail
);
1443 if
(abs
(mode
) == hmode
)
1444 space_factor
= 1000;
1446 cur_box
= new_sub_box
(cur_box
);
1447 couple_nodes
(tail
, cur_box
);
1451 } else if
(box_context
< ship_out_flag
) {
1452 /* Store |cur_box| in a box register
*/
1453 if
(box_context
< global_box_flag
)
1454 eq_define
(box_base
+ box_context
- box_flag
, box_ref_cmd
, cur_box
);
1456 geq_define
(box_base
+ box_context
- global_box_flag
, box_ref_cmd
, cur_box
);
1457 } else if
(cur_box
!= null
) {
1458 if
(box_context
> ship_out_flag
) {
1459 /* Append a new leader node that uses |cur_box|
*/
1460 /* Get the next non-blank non-relax...
*/
1463 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
1464 if
(((cur_cmd
== hskip_cmd
) && (abs(mode) != vmode)) ||
1465 ((cur_cmd
== vskip_cmd
) && (abs(mode) == vmode))) {
1467 subtype
(tail
) = (quarterword
) (box_context
- (leader_flag
- a_leaders
));
1468 leader_ptr
(tail
) = cur_box
;
1470 print_err
("Leaders not followed by proper glue");
1472 ("You should say `\\leaders <box or rule><hskip or vskip>'.",
1473 "I found the <box or rule>, but there's no suitable",
1474 "<hskip or vskip>, so I'm ignoring these leaders.");
1476 flush_node_list
(cur_box
);
1479 ship_out
(static_pdf
, cur_box
, SHIPPING_PAGE
);
1484 @ the next input should specify a box or perhaps a rule
1487 void scan_box
(int box_context
)
1489 /* Get the next non-blank non-relax...
*/
1492 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
1493 if
(cur_cmd
== make_box_cmd
) {
1494 begin_box
(box_context
);
1495 } else if
((box_context
>= leader_flag
) &&
1496 ((cur_cmd
== hrule_cmd
) ||
(cur_cmd
== vrule_cmd
) ||
1497 (cur_cmd
== no_hrule_cmd
) ||
(cur_cmd
== no_vrule_cmd
))) {
1498 cur_box
= scan_rule_spec
();
1499 box_end
(box_context
);
1501 print_err
("A <box> was supposed to be here");
1502 help3
("I was expecting to see \\hbox or \\vbox or \\copy or \\box or",
1503 "something like that. So you might find something missing in",
1504 "your output. But keep trying; you can fix this later.");
1510 void new_graf
(boolean indented
)
1512 halfword p
, q
, dir_graf_tmp
;
1515 if
((mode
== vmode
) ||
(head
!= tail
)) {
1516 tail_append
(new_param_glue
(par_skip_code
));
1520 space_factor
= 1000;
1521 /* LOCAL
: Add local paragraph node
*/
1522 tail_append
(make_local_par_node
());
1525 box_dir
(p
) = par_direction
;
1526 width
(p
) = par_indent
;
1527 subtype
(p
) = indent_list
;
1533 dir_rover
= text_dir_ptr
;
1534 while
(dir_rover
!= null
) {
1535 if
((vlink
(dir_rover
) != null
) ||
(dir_dir
(dir_rover
) != par_direction
)) {
1536 dir_graf_tmp
= new_dir
(dir_dir
(dir_rover
));
1537 try_couple_nodes
(dir_graf_tmp
,vlink
(q
));
1538 couple_nodes
(q
,dir_graf_tmp
);
1540 dir_rover
= vlink
(dir_rover
);
1543 while
(vlink
(q
) != null
)
1546 if
(every_par
!= null
)
1547 begin_token_list
(every_par
, every_par_text
);
1548 if
(nest_ptr
== 1) {
1549 check_filter
(new_graf
);
1550 build_page
(); /* put |par_skip| glue on current page
*/
1555 void indent_in_hmode
(void
)
1558 if
(cur_chr
> 0) { /* \.
{\\indent
} */
1560 width
(p
) = par_indent
;
1561 if
(abs
(mode
) == hmode
)
1562 space_factor
= 1000;
1570 void head_for_vmode
(void
)
1573 if
((cur_cmd
!= hrule_cmd
) && (cur_cmd != no_hrule_cmd)) {
1576 print_err
("You can't use `\\hrule' here except with leaders");
1577 help2
("To put a horizontal rule in an hbox or an alignment,",
1578 "you should use \\leaders or \\hrulefill (see The TeXbook).");
1583 cur_tok
= par_token
;
1585 token_type
= inserted
;
1589 @ TODO
(BUG?
): |dir_save| would have been set by |line_break| by means
1590 of |post_line_break|
, but this is not done right now
, as it introduces
1591 pretty heavy memory leaks. This means the current code is probably
1592 wrong in some way that relates to in-paragraph displays.
1595 void end_graf
(int line_break_context
)
1597 if
(mode
== hmode
) {
1598 if
((head
== tail
) ||
(vlink
(head
) == tail
)) {
1599 if
(vlink
(head
) == tail
)
1600 flush_node
(vlink
(head
));
1601 pop_nest
(); /* null paragraphs are ignored
, all contain a |local_paragraph| node
*/
1603 line_break
(false
, line_break_context
);
1605 if
(dir_save
!= null
) {
1606 flush_node_list
(dir_save
);
1615 void begin_insert_or_adjust
(void
)
1617 if
(cur_cmd
!= vadjust_cmd
) {
1618 scan_register_num
();
1619 if
(cur_val
== output_box
) {
1620 print_err
("You can't \\insert");
1621 print_int
(output_box
);
1622 help1
("I'm changing to \\insert0; box \\outputbox is special.");
1626 set_saved_record
(0, saved_insert
, 0, cur_val
);
1627 } else if
(scan_keyword
("pre")) {
1628 set_saved_record
(0, saved_adjust
, 0, 1);
1630 set_saved_record
(0, saved_adjust
, 0, 0);
1633 new_save_level
(insert_group
);
1638 prev_depth
= ignore_depth
;
1641 @ I
(TH
)'ve renamed the |make_mark| procedure to this
, because if the
1642 current chr code is
1, then the actual command was \.
{\\clearmarks
},
1643 which does not generate a mark node but instead destroys the current
1647 void handle_mark
(void
)
1649 halfword p
; /* new node
*/
1650 halfword c
; /* the mark class
*/
1651 if
(cur_chr
== clear_marks_code
) {
1656 delete_first_mark
(c
);
1657 delete_split_first_mark
(c
);
1658 delete_split_bot_mark
(c
);
1665 if
(c
> biggest_used_mark
)
1666 biggest_used_mark
= c
;
1668 p
= scan_toks
(false
, true
);
1669 p
= new_node
(mark_node
, 0); /* the |subtype| is not used
*/
1671 mark_ptr
(p
) = def_ref
;
1672 couple_nodes
(tail
, p
);
1678 void append_penalty
(void
)
1681 tail_append
(new_penalty
(cur_val
));
1682 if
(mode
== vmode
) {
1683 check_filter
(penalty
);
1688 @ When |delete_last| is called
, |cur_chr| is the |type| of node that
1689 will be deleted
, if present.
1691 The |remove_item| command removes a penalty
, kern
, or glue node if it
1692 appears at the tail of the current list
, using a brute-force linear scan.
1693 Like \.
{\\lastbox
}, this command is not allowed in vertical mode
(except
1694 internal vertical mode
), since the current list in vertical mode is sent
1695 to the page builder. But if we happen to be able to implement it in
1696 vertical mode
, we do.
1699 void delete_last
(void
)
1701 halfword p
, q
; /* run through the current list
*/
1702 if
((mode
== vmode
) && (tail == head)) {
1703 /* Apologize for inability to do the operation now
,
1704 unless \.
{\\unskip
} follows non-glue
*/
1705 if
((cur_chr
!= glue_node
) ||
(last_glue
!= max_halfword
)) {
1707 if
(cur_chr
== kern_node
) {
1709 ("Sorry...I usually can't take things from the current page.",
1710 "Try `I\\kern-\\lastkern' instead.");
1711 } else if
(cur_chr
!= glue_node
) {
1713 ("Sorry...I usually can't take things from the current page.",
1714 "Perhaps you can make the output routine do it.");
1717 ("Sorry...I usually can't take things from the current page.",
1718 "Try `I\\vskip-\\lastskip' instead.");
1723 /* todo
: clean this up
*/
1724 if
(!is_char_node
(tail
)) {
1725 if
(type
(tail
) == cur_chr
) {
1729 if
(!is_char_node
(q
)) {
1730 if
(type
(q
) == disc_node
) {
1736 } while
(q
!= tail
);
1738 flush_node_list
(tail
);
1746 void unpackage
(void
)
1748 halfword p
; /* the box
*/
1749 halfword r
; /* to remove marginal kern nodes
*/
1750 int c
; /* should we copy?
*/
1751 halfword s
; /* for varmem assignment
*/
1752 if
(cur_chr
> copy_code
) {
1753 /* Handle saved items and |goto done|
*/
1754 try_couple_nodes
(tail
, disc_ptr
[cur_chr
]);
1755 disc_ptr
[cur_chr
] = null
;
1759 scan_register_num
();
1763 if
((abs
(mode
) == mmode
)
1764 ||
((abs
(mode
) == vmode
) && (type(p) != vlist_node))
1765 ||
((abs
(mode
) == hmode
) && (type(p) != hlist_node))) {
1766 print_err
("Incompatible list can't be unboxed");
1767 help3
("Sorry, Pandora. (You sneaky devil.)",
1768 "I refuse to unbox an \\hbox in vertical mode or vice versa.",
1769 "And I can't open any boxes in math mode.");
1773 if
(c
== copy_code
) {
1774 s
= copy_node_list
(list_ptr
(p
));
1775 try_couple_nodes
(tail
,s
);
1777 try_couple_nodes
(tail
,list_ptr
(p
));
1778 box
(cur_val
) = null
;
1783 while
(vlink
(tail
) != null
) {
1785 if
(!is_char_node
(r
) && (type(r) == margin_kern_node)) {
1786 try_couple_nodes
(tail
,vlink
(r
));
1794 Italic corrections are converted to kern nodes when the |ital_corr| command
1795 follows a character. In math mode the same effect is achieved by appending
1796 a kern of zero here
, since italic corrections are supplied later.
1799 void append_italic_correction
(void
)
1801 halfword p
; /* |char_node| at the tail of the current list
*/
1802 internal_font_number f
; /* the font in the |char_node|
*/
1804 if
(is_char_node
(tail
))
1809 tail_append
(new_kern
(char_italic
(f
, character
(p
))));
1810 subtype
(tail
) = italic_kern
;
1815 void append_local_box
(int kind
)
1818 set_saved_record
(-1, saved_boxtype
, 0, kind
);
1819 new_save_level
(local_box_group
);
1823 space_factor
= 1000;
1826 @ Discretionary nodes are easy in the common case `\.
{\\
-}'
, but in the
1827 general case we must process three braces full of items.
1829 The space factor does not change when we append a discretionary node
,
1830 but it starts out as
1000 in the subsidiary lists.
1833 void append_discretionary
(void
)
1836 tail_append
(new_disc
());
1837 subtype
(tail
) = (quarterword
) cur_chr
;
1838 if
(cur_chr
== explicit_disc
) {
1840 c
= get_pre_hyphen_char
(cur_lang
);
1842 vlink
(pre_break
(tail
)) = new_char
(equiv
(cur_font_loc
), c
);
1843 alink
(vlink
(pre_break
(tail
))) = pre_break
(tail
);
1844 tlink
(pre_break
(tail
)) = vlink
(pre_break
(tail
));
1846 c
= get_post_hyphen_char
(cur_lang
);
1848 vlink
(post_break
(tail
)) = new_char
(equiv
(cur_font_loc
), c
);
1849 alink
(vlink
(post_break
(tail
))) = post_break
(tail
);
1850 tlink
(post_break
(tail
)) = vlink
(post_break
(tail
));
1852 disc_penalty
(tail
) = int_par
(ex_hyphen_penalty_code
);
1854 /* \discretionary
*/
1855 if
(scan_keyword
("penalty")) {
1857 disc_penalty
(tail
) = cur_val
;
1860 set_saved_record
(-1, saved_disc
, 0, 0);
1861 new_save_level
(disc_group
);
1865 space_factor
= 1000;
1866 /* already preset
: disc_penalty
(tail
) = int_par
(hyphen_penalty_code
); */
1870 @ The test for |p
!= null| ensures that empty \.
{\\localleftbox
} and
1871 \.
{\\localrightbox
} commands are not applied.
1874 void build_local_box
(void
)
1879 assert
(saved_type
(-1) == saved_boxtype
);
1880 kind
= saved_value
(-1);
1885 p
= hpack
(p
, 0, additional
, -1);
1887 eq_define
(local_left_box_base
, box_ref_cmd
, p
);
1889 eq_define
(local_right_box_base
, box_ref_cmd
, p
);
1890 if
(abs
(mode
) == hmode
) {
1891 /* LOCAL
: Add local paragraph node
*/
1892 tail_append
(make_local_par_node
());
1894 eq_word_define
(int_base
+ no_local_whatsits_code
, no_local_whatsits
+ 1);
1897 @ The three discretionary lists are constructed somewhat as if they were
1898 hboxes. A~subroutine called |build_discretionary| handles the transitions.
1899 (This is sort of fun.
)
1902 void build_discretionary
(void
)
1904 halfword p
, q
; /* for link manipulation
*/
1905 int n
; /* length of discretionary list
*/
1907 /* Prune the current list
, if necessary
, until it contains only
1908 |char_node|
, |kern_node|
, |hlist_node|
, |vlist_node| and
1909 |rule_node| items
; set |n| to the length of the list
,
1910 and set |q| to the lists tail
*/
1911 /* During this loop
, |p
=vlink
(q
)| and there are |n| items preceding |p|.
*/
1916 if
(!is_char_node
(p
) && type(p) > rule_node && type(p) != kern_node) {
1917 print_err
("Improper discretionary list");
1918 help1
("Discretionary lists must contain only boxes and kerns.");
1921 tprint_nl
("The following discretionary sublist has been deleted:");
1923 end_diagnostic
(true
);
1936 assert
(saved_type
(-1) == saved_disc
);
1937 switch
(saved_value
(-1)) {
1940 vlink
(pre_break
(tail
)) = p
;
1941 alink
(p
) = pre_break
(tail
);
1942 tlink
(pre_break
(tail
)) = q
;
1947 vlink
(post_break
(tail
)) = p
;
1948 alink
(p
) = post_break
(tail
);
1949 tlink
(post_break
(tail
)) = q
;
1953 /* Attach list |p| to the current list
, and record its length
;
1954 then finish up and |return|
*/
1955 if
((n
> 0) && (abs(mode) == mmode)) {
1956 print_err
("Illegal math \\discretionary");
1957 help2
("Sorry: The third part of a discretionary break must be",
1958 "empty, in math formulas. I had to delete your third part.");
1963 vlink
(no_break
(tail
)) = p
;
1964 alink
(p
) = no_break
(tail
);
1965 tlink
(no_break
(tail
)) = q
;
1971 } /* there are no other cases
*/
1972 set_saved_record
(-1, saved_disc
, 0, (saved_value
(-1) + 1));
1973 new_save_level
(disc_group
);
1977 space_factor
= 1000;
1980 @ The positioning of accents is straightforward but tedious. Given an accent
1981 of width |a|
, designed for characters of height |x| and slant |s|
;
1982 and given a character of width |w|
, height |h|
, and slant |t|
: We will shift
1983 the accent down by |x-h|
, and we will insert kern nodes that have the effect of
1984 centering the accent over the character and shifting the accent to the
1985 right by $\delta
={1\over2
}(w-a
)+h\cdot t-x\cdot s$. If either character is
1986 absent from the font
, we will simply use the other
, without shifting.
1989 void make_accent
(void
)
1991 double s
, t
; /* amount of slant
*/
1992 halfword p
, q
, r
; /* character
, box
, and kern nodes
*/
1993 internal_font_number f
; /* relevant font
*/
1994 scaled a
, h
, x
, w
, delta
; /* heights and widths
, as explained above
*/
1996 f
= equiv
(cur_font_loc
);
1997 p
= new_glyph
(f
, cur_val
);
2000 s
= float_cast
(slant
(f
)) / float_constant
(65536); /* real division
*/
2003 /* Create a character node |q| for the next character
,
2004 but set |q
:=null| if problems arise
*/
2006 f
= equiv
(cur_font_loc
);
2007 if
((cur_cmd
== letter_cmd
) ||
2008 (cur_cmd
== other_char_cmd
) ||
(cur_cmd
== char_given_cmd
)) {
2009 q
= new_glyph
(f
, cur_chr
);
2010 } else if
(cur_cmd
== char_num_cmd
) {
2012 q
= new_glyph
(f
, cur_val
);
2018 /* Append the accent with appropriate kerns
, then set |p
:=q|
*/
2019 /* The kern nodes appended here must be distinguished from other kerns
, lest
2020 they be wiped away by the hyphenation algorithm or by a previous line break.
2022 The two kerns are computed with
(machine-dependent
) |real| arithmetic
, but
2023 their sum is machine-independent
; the net effect is machine-independent
,
2024 because the user cannot remove these nodes nor access them via \.
{\\lastkern
}.
2026 t
= float_cast
(slant
(f
)) / float_constant
(65536); /* real division
*/
2028 h
= glyph_height
(q
);
2029 if
(h
!= x
) { /* the accent must be shifted up or down
*/
2030 p
= hpack
(p
, 0, additional
, -1);
2031 shift_amount
(p
) = x
- h
;
2033 delta
= round
(float_cast
(w
- a
) / float_constant
(2) + h
* t
- x
* s
); /* real multiplication
*/
2034 r
= new_kern
(delta
);
2035 subtype
(r
) = accent_kern
;
2036 couple_nodes
(tail
, r
);
2038 tail
= new_kern
(-a
- delta
);
2039 subtype
(tail
) = accent_kern
;
2040 couple_nodes
(p
, tail
);
2044 couple_nodes
(tail
, p
);
2046 space_factor
= 1000;
2050 @ When `\.
{\\cr
}' or `\.
{\\span
}' or a tab mark comes through the scanner
2051 into |main_control|
, it might be that the user has foolishly inserted
2052 one of them into something that has nothing to do with alignment. But it is
2053 far more likely that a left brace or right brace has been omitted
, since
2054 |get_next| takes actions appropriate to alignment only when `\.
{\\cr
}'
2055 or `\.
{\\span
}' or tab marks occur with |align_state
=0|. The following
2056 program attempts to make an appropriate recovery.
2059 void align_error
(void
)
2061 if
(abs
(align_state
) > 2) {
2062 /* Express consternation over the fact that no alignment is in progress
*/
2063 print_err
("Misplaced ");
2064 print_cmd_chr
((quarterword
) cur_cmd
, cur_chr
);
2065 if
(cur_tok
== tab_token
+ '
&') {
2066 help6
("I can't figure out why you would want to use a tab mark",
2067 "here. If you just want an ampersand, the remedy is",
2068 "simple: Just type `I\\&' now. But if some right brace",
2069 "up above has ended a previous alignment prematurely,",
2070 "you're probably due for more error messages, and you",
2071 "might try typing `S' now just to see what is salvageable.");
2073 help5
("I can't figure out why you would want to use a tab mark",
2074 "or \\cr or \\span just now. If something like a right brace",
2075 "up above has ended a previous alignment prematurely,",
2076 "you're probably due for more error messages, and you",
2077 "might try typing `S' now just to see what is salvageable.");
2083 if
(align_state
< 0) {
2084 print_err
("Missing { inserted");
2086 cur_tok
= left_brace_token
+ '
{'
;
2088 print_err
("Missing } inserted");
2090 cur_tok
= right_brace_token
+ '
}'
;
2092 help3
("I've put in what seems to be necessary to fix",
2093 "the current column of the current alignment.",
2094 "Try to go on, since this might almost work.");
2099 @ The help messages here contain a little white lie
, since \.
{\\noalign
}
2100 and \.
{\\omit
} are allowed also after `\.
{\\noalign\
{...\
}}'.
2103 void no_align_error
(void
)
2105 print_err
("Misplaced \\noalign");
2106 help2
("I expect to see \\noalign only after the \\cr of",
2107 "an alignment. Proceed, and I'll ignore this case.");
2111 void omit_error
(void
)
2113 print_err
("Misplaced \\omit");
2114 help2
("I expect to see \\omit only after tab marks or the \\cr of",
2115 "an alignment. Proceed, and I'll ignore this case.");
2119 @ We've now covered most of the abuses of \.
{\\halign
} and \.
{\\valign
}.
2120 Let's take a look at what happens when they are used correctly.
2122 An |align_group| code is supposed to remain on the |save_stack|
2123 during an entire alignment
, until |fin_align| removes it.
2125 A devious user might force an |endv| command to occur just about anywhere
;
2126 we must defeat such hacks.
2131 base_ptr
= input_ptr
;
2132 input_stack
[base_ptr
] = cur_input
;
2133 while
((input_stack
[base_ptr
].index_field
!= v_template
) &&
2134 (input_stack
[base_ptr
].loc_field
== null
) &&
2135 (input_stack
[base_ptr
].state_field
== token_list
))
2137 if
((input_stack
[base_ptr
].index_field
!= v_template
) ||
2138 (input_stack
[base_ptr
].loc_field
!= null
) ||
2139 (input_stack
[base_ptr
].state_field
!= token_list
))
2140 fatal_error
("(interwoven alignment preambles are not allowed)");
2141 /*.interwoven alignment preambles...
*/
2142 if
(cur_group
== align_group
) {
2143 end_graf
(align_group
);
2151 @ Finally
, \.
{\\endcsname
} is not supposed to get through to |main_control|.
2156 print_err
("Extra \\endcsname");
2157 help1
("I'm ignoring this, since I wasn't doing a \\csname.");
2162 Assignments to values in |eqtb| can be global or local. Furthermore
, a
2163 control sequence can be defined to be `\.
{\\long
}'
, `\.
{\\protected
}'
,
2164 or `\.
{\\outer
}'
, and it might or might not be expanded. The prefixes
2165 `\.
{\\global
}'
, `\.
{\\long
}'
, `\.
{\\protected
}'
,
2166 and `\.
{\\outer
}' can occur in any order. Therefore we assign binary numeric
2167 codes
, making it possible to accumulate the union of all specified prefixes
2168 by adding the corresponding codes.
(PASCAL's |set| operations could also
2171 Every prefix
, and every command code that might or might not be prefixed
,
2172 calls the action procedure |prefixed_command|. This routine accumulates
2173 a sequence of prefixes until coming to a non-prefix
, then it carries out
2176 @ If the user says
, e.g.
, `\.
{\\global\\global
}'
, the redundancy is
2180 @ The different types of code values have different legal ranges
; the
2181 following program is careful to check each case properly.
2184 #define check_def_code
(A
) do
{ \
2185 if
(((cur_val
<0)&&(p<(A)))||(cur_val>n)) { \
2186 print_err
("Invalid code ("); \
2187 print_int
(cur_val
); \
2189 tprint
("), should be in the range 0.."); \
2191 tprint
("), should be at most "); \
2193 help1
("I'm going to use 0 instead of that illegal code value."); \
2200 void prefixed_command
(void
)
2202 int a
; /* accumulated prefix codes so far
*/
2203 internal_font_number f
; /* identifies a font
*/
2204 halfword j
; /* index into a \.
{\\parshape
} specification
*/
2205 halfword p
, q
; /* for temporary short-term use
*/
2207 boolean e
; /* should a definition be expanded? or was \.
{\\let
} not done?
*/
2208 mathcodeval mval
; /* for handling of \.
{\\mathchardef
}s
*/
2210 while
(cur_cmd
== prefix_cmd
) {
2211 if
(!odd
(a
/ cur_chr
))
2213 /* Get the next non-blank non-relax...
*/
2216 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
2218 if
(cur_cmd
<= max_non_prefixed_command
) {
2219 /* Discard erroneous prefixes and |return|
*/
2220 print_err
("You can't use a prefix with `");
2221 print_cmd_chr
((quarterword
) cur_cmd
, cur_chr
);
2224 ("I'll pretend you didn't say \\long or \\outer or \\global or",
2229 if
(int_par
(tracing_commands_code
) > 2)
2232 /* Discard the prefixes \.
{\\long
} and \.
{\\outer
} if they are irrelevant
*/
2234 j
= protected_token
;
2239 if
((cur_cmd
!= def_cmd
) && ((a % 4 != 0) || (j != 0))) {
2240 print_err
("You can't use `\\long' or `\\outer' or `\\protected' with `");
2241 print_cmd_chr
((quarterword
) cur_cmd
, cur_chr
);
2243 help1
("I'll pretend you didn't say \\long or \\outer or \\protected here.");
2246 /* Adjust for the setting of \.
{\\globaldefs
} */
2247 if
(global_defs
!= 0) {
2248 if
(global_defs
< 0) {
2258 /* Here's an example of the way many of the following routines operate.
2259 (Unfortunately
, they aren't all as simple as this.
) */
2260 define
(cur_font_loc
, data_cmd
, cur_chr
);
2263 /* When a |def| command has been scanned
,
2264 |cur_chr| is odd if the definition is supposed to be global
, and
2265 |cur_chr
>=2| if the definition is supposed to be expanded.
*/
2267 if
(odd
(cur_chr
) && !is_global(a) && (global_defs >= 0))
2272 q
= scan_toks
(true
, e
);
2275 set_token_info
(q
, j
);
2276 set_token_link
(q
, token_link
(def_ref
));
2277 set_token_link
(def_ref
, q
);
2279 define
(p
, call_cmd
+ (a
% 4), def_ref
);
2288 } while
(cur_cmd
== spacer_cmd
);
2289 if
(cur_tok
== other_token
+ '
='
) {
2291 if
(cur_cmd
== spacer_cmd
)
2294 } else if
(n
== normal
+ 1) {
2303 /* look ahead
, then back up
*/
2304 /* note that |back_input| doesn't affect |cur_cmd|
, |cur_chr|
*/
2310 cur_cs
= active_to_cs
(cur_val
, true
);
2311 set_token_info
(cur_cs
, cur_cs
+ cs_token_flag
);
2315 } while
(cur_cmd
== spacer_cmd
);
2316 if
(cur_tok
== other_token
+ '
='
) {
2318 if
(cur_cmd
== spacer_cmd
)
2323 tex_error
("invalid number for \\letcharcode",NULL);
2326 if
(cur_cmd
>= call_cmd
)
2327 add_token_ref
(cur_chr
);
2328 define
(p
, cur_cmd
, cur_chr
);
2330 case shorthand_def_cmd
:
2331 /* We temporarily define |p| to be |relax|
, so that an occurrence of |p|
2332 while scanning the definition will simply stop the scanning instead of
2333 producing an ``undefined control sequence'' error or expanding the
2334 previous meaning. This allows
, for instance
, `\.
{\\chardef\\foo
=123\\foo
}'.
2339 define
(p
, relax_cmd
, too_big_char
);
2340 scan_optional_equals
();
2344 define
(p
, char_given_cmd
, cur_val
);
2346 case math_char_def_code
:
2347 mval
= scan_mathchar
(tex_mathcode
);
2349 (mval.class_value
* 16 + mval.family_value
) * 256 +
2350 mval.character_value
;
2351 define
(p
, math_given_cmd
, cur_val
);
2353 case xmath_char_def_code
:
2354 mval
= scan_mathchar
(umath_mathcode
);
2356 (mval.class_value
+ (8 * mval.family_value
)) * (65536 * 32) +
2357 mval.character_value
;
2358 define
(p
, xmath_given_cmd
, cur_val
);
2360 case umath_char_def_code
:
2361 mval
= scan_mathchar
(umathnum_mathcode
);
2363 (mval.class_value
+ (8 * mval.family_value
)) * (65536 * 32) +
2364 mval.character_value
;
2365 define
(p
, xmath_given_cmd
, cur_val
);
2368 scan_register_num
();
2370 case count_def_code
:
2371 define
(p
, assign_int_cmd
, count_base
+ cur_val
);
2373 case attribute_def_code
:
2374 define
(p
, assign_attr_cmd
, attribute_base
+ cur_val
);
2376 case dimen_def_code
:
2377 define
(p
, assign_dimen_cmd
, scaled_base
+ cur_val
);
2380 define
(p
, assign_glue_cmd
, skip_base
+ cur_val
);
2382 case mu_skip_def_code
:
2383 define
(p
, assign_mu_glue_cmd
, mu_skip_base
+ cur_val
);
2386 define
(p
, assign_toks_cmd
, toks_base
+ cur_val
);
2389 confusion
("shorthand_def");
2395 case read_to_cs_cmd
:
2399 if
(!scan_keyword
("to")) {
2400 print_err
("Missing `to' inserted");
2401 help2
("You should have said `\\read<number> to \\cs'.",
2402 "I'm going to look for the \\cs now.");
2408 define
(p
, call_cmd
, cur_val
);
2410 case toks_register_cmd
:
2411 case assign_toks_cmd
:
2412 /* The token-list parameters
, \.
{\\output
} and \.
{\\everypar
}, etc.
, receive
2413 their values in the following way.
(For safety's sake
, we place an
2414 enclosing pair of braces around an \.
{\\output
} list.
) */
2416 if
(cur_cmd
== toks_register_cmd
) {
2417 scan_register_num
();
2418 p
= toks_base
+ cur_val
;
2420 p
= cur_chr
; /* |p
=every_par_loc| or |output_routine_loc| or \dots
*/
2422 scan_optional_equals
();
2423 /* Get the next non-blank non-relax non-call token
*/
2426 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
2428 if
(cur_cmd
!= left_brace_cmd
) {
2429 /* If the right-hand side is a token parameter
2430 or token register
, finish the assignment and |goto done|
*/
2431 if
(cur_cmd
== toks_register_cmd
) {
2432 scan_register_num
();
2433 cur_cmd
= assign_toks_cmd
;
2434 cur_chr
= toks_base
+ cur_val
;
2436 if
(cur_cmd
== assign_toks_cmd
) {
2439 define
(p
, undefined_cs_cmd
, null
);
2442 define
(p
, call_cmd
, q
);
2449 q
= scan_toks
(false
, false
);
2450 if
(token_link
(def_ref
) == null
) { /* empty list
: revert to the default
*/
2451 define
(p
, undefined_cs_cmd
, null
);
2452 free_avail
(def_ref
);
2454 if
(p
== output_routine_loc
) { /* enclose in curlies
*/
2456 set_token_link
(q
, p
);
2457 p
= output_routine_loc
;
2459 set_token_info
(q
, right_brace_token
+ '
}'
);
2461 set_token_info
(q
, left_brace_token
+ '
{'
);
2462 set_token_link
(q
, token_link
(def_ref
));
2463 set_token_link
(def_ref
, q
);
2465 define
(p
, call_cmd
, def_ref
);
2468 case assign_int_cmd
:
2469 /* Similar routines are used to assign values to the numeric parameters.
*/
2471 scan_optional_equals
();
2473 assign_internal_value
(a
, p
, cur_val
);
2475 case assign_attr_cmd
:
2477 scan_optional_equals
();
2479 if
((p
- attribute_base
) > max_used_attr
)
2480 max_used_attr
= (p
- attribute_base
);
2481 attr_list_cache
= cache_disabled
;
2482 word_define
(p
, cur_val
);
2484 case assign_dir_cmd
:
2485 /* DIR: Assign direction codes
*/
2488 case int_base
+ page_direction_code
:
2489 eq_word_define
(int_base
+ page_direction_code
, cur_val
);
2491 case int_base
+ body_direction_code
:
2492 eq_word_define
(int_base
+ body_direction_code
, cur_val
);
2494 case int_base
+ par_direction_code
:
2495 eq_word_define
(int_base
+ par_direction_code
, cur_val
);
2497 case int_base
+ math_direction_code
:
2498 eq_word_define
(int_base
+ math_direction_code
, cur_val
);
2500 case int_base
+ text_direction_code
:
2502 /* various tests hint that this is unnecessary and
2503 * sometimes even produces weird results
, eg
2504 * (\hbox
{\textdir TRT ABC\textdir TLT
DEF})
2509 if
((no_local_dirs
> 0) && (abs(mode) == hmode)) {
2510 /* DIR: Add local dir node
*/
2511 tail_append
(new_dir
(text_direction
));
2514 update_text_dir_ptr
(cur_val
);
2515 if
(abs
(mode
) == hmode
) {
2516 /* DIR: Add local dir node
*/
2517 tail_append
(new_dir
(cur_val
));
2518 dir_level
(tail
) = cur_level
;
2520 eq_word_define
(int_base
+ text_direction_code
, cur_val
);
2521 eq_word_define
(int_base
+ no_local_dirs_code
, no_local_dirs
+ 1);
2525 case assign_dimen_cmd
:
2527 scan_optional_equals
();
2528 scan_normal_dimen
();
2529 assign_internal_value
(a
, p
, cur_val
);
2531 case assign_glue_cmd
:
2532 case assign_mu_glue_cmd
:
2535 scan_optional_equals
();
2536 if
(n
== assign_mu_glue_cmd
)
2537 scan_glue
(mu_val_level
);
2539 scan_glue
(glue_val_level
);
2541 define
(p
, glue_ref_cmd
, cur_val
);
2543 case def_char_code_cmd
:
2544 case def_del_code_cmd
:
2545 /* Let |n| be the largest legal code value
, based on |cur_chr|
*/
2546 if
(cur_chr
== cat_code_base
)
2548 else if
(cur_chr
== sf_code_base
)
2554 if
(cur_chr
== math_code_base
) {
2556 cur_val1
= level_one
;
2558 cur_val1
= cur_level
;
2559 scan_extdef_math_code
(cur_val1
, tex_mathcode
);
2560 } else if
(cur_chr
== lc_code_base
) {
2563 scan_optional_equals
();
2565 check_def_code
(lc_code_base
);
2566 define_lc_code
(p
, cur_val
);
2567 } else if
(cur_chr
== uc_code_base
) {
2570 scan_optional_equals
();
2572 check_def_code
(uc_code_base
);
2573 define_uc_code
(p
, cur_val
);
2574 } else if
(cur_chr
== sf_code_base
) {
2577 scan_optional_equals
();
2579 check_def_code
(sf_code_base
);
2580 define_sf_code
(p
, cur_val
);
2581 } else if
(cur_chr
== cat_code_base
) {
2584 scan_optional_equals
();
2586 check_def_code
(cat_code_base
);
2587 define_cat_code
(p
, cur_val
);
2588 } else if
(cur_chr
== del_code_base
) {
2590 cur_val1
= level_one
;
2592 cur_val1
= cur_level
;
2593 scan_extdef_del_code
(cur_val1
, tex_mathcode
);
2596 case extdef_math_code_cmd
:
2597 case extdef_del_code_cmd
:
2599 cur_val1
= level_one
;
2601 cur_val1
= cur_level
;
2602 if
(cur_chr
== math_code_base
)
2603 scan_extdef_math_code
(cur_val1
, umath_mathcode
);
2604 else if
(cur_chr
== math_code_base
+ 1)
2605 scan_extdef_math_code
(cur_val1
, umathnum_mathcode
);
2606 else if
(cur_chr
== del_code_base
)
2607 scan_extdef_del_code
(cur_val1
, umath_mathcode
);
2608 else if
(cur_chr
== del_code_base
+ 1)
2609 scan_extdef_del_code
(cur_val1
, umathnum_mathcode
);
2611 case def_family_cmd
:
2613 scan_math_family_int
();
2615 scan_optional_equals
();
2617 define_fam_fnt
(cur_val1
, p
, cur_val
);
2619 case set_math_param_cmd
:
2622 if
(cur_cmd
!= math_style_cmd
) {
2623 print_err
("Missing math style, treated as \\displaystyle");
2625 ("A style should have been here; I inserted `\\displaystyle'.");
2626 cur_val1
= display_style
;
2631 scan_optional_equals
();
2632 if
(p
< math_param_first_mu_glue
) {
2633 if
(p
== math_param_radical_degree_raise
)
2636 scan_dimen
(false
, false
, false
);
2638 scan_glue
(mu_val_level
);
2640 if
(cur_val
== glue_par
(thin_mu_skip_code
))
2641 cur_val
= thin_mu_skip_code
;
2642 else if
(cur_val
== glue_par
(med_mu_skip_code
))
2643 cur_val
= med_mu_skip_code
;
2644 else if
(cur_val
== glue_par
(thick_mu_skip_code
))
2645 cur_val
= thick_mu_skip_code
;
2647 define_math_param
(p
, cur_val1
, cur_val
);
2653 do_register_command
(a
);
2656 /* The processing of boxes is somewhat different
, because we may need
2657 to scan and create an entire box before we actually change the value
2659 scan_register_num
();
2661 n
= global_box_flag
+ cur_val
;
2663 n
= box_flag
+ cur_val
;
2664 scan_optional_equals
();
2665 if
(set_box_allowed
) {
2668 print_err
("Improper \\setbox");
2669 help2
("Sorry, \\setbox is not allowed after \\halign in a display,",
2670 "or between \\accent and an accented character.");
2675 /* The |space_factor| or |prev_depth| settings are changed when a |set_aux|
2676 command is sensed. Similarly
, |prev_graf| is changed in the presence of
2677 |set_prev_graf|
, and |dead_cycles| or |insert_penalties| in the presence of
2678 |set_page_int|. These definitions are always global.
*/
2681 case set_prev_graf_cmd
:
2684 case set_page_dimen_cmd
:
2685 alter_page_so_far
();
2687 case set_page_int_cmd
:
2690 case set_box_dimen_cmd
:
2691 /* When some dimension of a box register is changed
, the change isn't exactly
2692 global
; but \TeX\ does not look at the \.
{\\global
} switch.
*/
2695 case set_tex_shape_cmd
:
2697 scan_optional_equals
();
2703 p
= new_node
(shape_node
, 2 * (n
+ 1) + 1);
2705 for
(j
= 1; j
<= n
; j
++) {
2706 scan_normal_dimen
();
2707 varmem
[p
+ 2 * j
].cint
= cur_val
; /* indentation
*/
2708 scan_normal_dimen
();
2709 varmem
[p
+ 2 * j
+ 1].cint
= cur_val
; /* width
*/
2712 define
(q
, shape_ref_cmd
, p
);
2714 case set_etex_shape_cmd
:
2716 scan_optional_equals
();
2722 n
= (cur_val
/ 2) + 1;
2723 p
= new_node
(shape_node
, 2 * n
+ 1 + 1);
2726 varmem
[p
+ 2].cint
= n
; /* number of penalties
*/
2727 for
(j
= p
+ 3; j
<= p
+ n
+ 2; j
++) {
2729 varmem
[j
].cint
= cur_val
; /* penalty values
*/
2732 varmem
[p
+ n
+ 3].cint
= 0; /* unused
*/
2734 define
(q
, shape_ref_cmd
, p
);
2737 /* All of \TeX's parameters are kept in |eqtb| except the font information
,
2738 the interaction mode
, and the hyphenation tables
; these are strictly global.
2742 new_hyph_exceptions
();
2748 new_pre_hyphen_char
();
2751 new_post_hyphen_char
();
2754 new_pre_exhyphen_char
();
2757 new_post_exhyphen_char
();
2760 new_hyphenation_min
();
2767 case assign_font_dimen_cmd
:
2770 case assign_font_int_cmd
:
2774 if
(n
== no_lig_code
) {
2775 set_no_ligatures
(f
);
2776 } else if
(n
< lp_code_base
) {
2777 scan_optional_equals
();
2780 set_hyphen_char
(f
, cur_val
);
2782 set_skew_char
(f
, cur_val
);
2786 scan_optional_equals
();
2790 set_lp_code
(f
, p
, cur_val
);
2793 set_rp_code
(f
, p
, cur_val
);
2796 set_ef_code
(f
, p
, cur_val
);
2799 set_tag_code
(f
, p
, cur_val
);
2805 /* Here is where the information for a new font gets loaded.
*/
2806 tex_def_font
((small_number
) a
);
2808 case letterspace_font_cmd
:
2809 new_letterspaced_font
((small_number
) a
);
2812 make_font_copy
((small_number
) a
);
2814 case set_font_id_cmd
:
2816 if
(is_valid_font
(cur_val
))
2817 zset_cur_font
(cur_val
);
2819 case set_interaction_cmd
:
2823 confusion
("prefix");
2825 } /* end of Assignments cases
*/
2827 /* Insert a token saved by \.
{\\afterassignment
}, if any
*/
2828 if
(after_token
!= 0) {
2829 cur_tok
= after_token
;
2836 void fixup_directions
(void
)
2838 int temp_no_whatsits
= no_local_whatsits
;
2839 int temp_no_dirs
= no_local_dirs
;
2840 int temporary_dir
= text_direction
;
2841 if
(dir_level
(text_dir_ptr
) == cur_level
) {
2842 /* DIR: Remove from |text_dir_ptr|
*/
2843 halfword text_dir_tmp
= vlink
(text_dir_ptr
);
2844 flush_node
(text_dir_ptr
);
2845 text_dir_ptr
= text_dir_tmp
;
2848 if
(abs
(mode
) == hmode
) {
2849 if
(temp_no_dirs
!= 0) {
2850 /* DIR: Add local dir node
*/
2851 tail_append
(new_dir
(text_direction
));
2852 dir_dir
(tail
) = temporary_dir
- dir_swap
;
2854 if
(temp_no_whatsits
!= 0) {
2855 /* LOCAL
: Add local paragraph node
*/
2856 tail_append
(make_local_par_node
());
2861 @ When a control sequence is to be defined
, by \.
{\\def
} or \.
{\\let
} or
2862 something similar
, the |get_r_token| routine will substitute a special
2863 control sequence for a token that is not redefinable.
2866 void get_r_token
(void
)
2871 } while
(cur_tok
== space_token
);
2872 if
((cur_cs
== 0) ||
(cur_cs
> eqtb_top
) ||
2873 ((cur_cs
> frozen_control_sequence
) && (cur_cs <= eqtb_size))) {
2874 print_err
("Missing control sequence inserted");
2875 help5
("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",
2876 "I've inserted an inaccessible control sequence so that your",
2877 "definition will be completed without mixing me up too badly.",
2878 "You can recover graciously from this error, if you're",
2879 "careful; see exercise 27.2 in The TeXbook.");
2882 cur_tok
= cs_token_flag
+ frozen_protection
;
2889 void assign_internal_value
(int a
, halfword p
, int val
)
2892 if
((p
>= int_base
) && (p < attribute_base)) {
2893 switch
((p
- int_base
)) {
2894 case cat_code_table_code
:
2895 if
(valid_catcode_table
(val
)) {
2896 if
(val
!= int_par
(cat_code_table_code
))
2897 word_define
(p
, val
);
2899 print_err
("Invalid \\catcode table");
2901 ("You can only switch to a \\catcode table that is initialized",
2902 "using \\savecatcodetable or \\initcatcodetable, or to table 0");
2906 case output_box_code
:
2907 if
((val
> 65535) |
(val
< 0)) {
2908 print_err
("Invalid \\outputbox");
2910 ("The value for \\outputbox has to be between 0 and 65535.");
2913 word_define
(p
, val
);
2916 case new_line_char_code
:
2918 print_err
("Invalid \\newlinechar");
2920 ("The value for \\newlinechar has to be no higher than 127.",
2921 "Your invalid assignment will be ignored.");
2924 word_define
(p
, val
);
2927 case end_line_char_code
:
2929 print_err
("Invalid \\endlinechar");
2931 ("The value for \\endlinechar has to be no higher than 127.",
2932 "Your invalid assignment will be ignored.");
2935 word_define
(p
, val
);
2940 word_define
(int_base
+ cur_lang_code
, -1);
2942 } else if
(val
> 16383) {
2943 print_err
("Invalid \\language");
2945 ("The absolute value for \\language has to be no higher than 16383.",
2946 "Your invalid assignment will be ignored.");
2949 word_define
(int_base
+ cur_lang_code
, val
);
2950 word_define
(p
, val
);
2954 word_define
(p
, val
);
2957 /* If we are defining subparagraph penalty levels while we are
2958 in hmode
, then we put out a whatsit immediately
, otherwise
2959 we leave it alone. This mechanism might not be sufficiently
2960 powerful
, and some other algorithm
, searching down the stack
,
2961 might be necessary. Good first step.
*/
2962 if
((abs
(mode
) == hmode
) &&
2963 ((p
== (int_base
+ local_inter_line_penalty_code
)) ||
2964 (p
== (int_base
+ local_broken_penalty_code
)))) {
2965 /* LOCAL
: Add local paragraph node
*/
2966 tail_append
(make_local_par_node
());
2968 eq_word_define
(int_base
+ no_local_whatsits_code
,
2969 no_local_whatsits
+ 1);
2971 } else if
((p
>= dimen_base
) && (p <= eqtb_size)) {
2972 if
(p
== (dimen_base
+ page_left_offset_code
)) {
2973 n
= val
- one_true_inch
;
2974 word_define
(dimen_base
+ h_offset_code
, n
);
2975 } else if
(p
== (dimen_base
+ h_offset_code
)) {
2976 n
= val
+ one_true_inch
;
2977 word_define
(dimen_base
+ page_left_offset_code
, n
);
2978 } else if
(p
== (dimen_base
+ page_top_offset_code
)) {
2979 n
= val
- one_true_inch
;
2980 word_define
(dimen_base
+ v_offset_code
, n
);
2981 } else if
(p
== (dimen_base
+ v_offset_code
)) {
2982 n
= val
+ one_true_inch
;
2983 word_define
(dimen_base
+ page_top_offset_code
, n
);
2985 word_define
(p
, val
);
2986 } else if
((p
>= local_base
) && (p < toks_base)) { /* internal locals */
2987 define
(p
, call_cmd
, val
);
2989 confusion
("assign internal value");
2993 @ When a glue register or parameter becomes zero
, it will always point to
2994 |zero_glue| because of the following procedure.
(Exception
: The tabskip
2995 glue isn't trapped while preambles are being scanned.
)
2998 void trap_zero_glue
(void
)
3000 if
((width
(cur_val
) == 0) && (stretch(cur_val) == 0)
3001 && (shrink(cur_val) == 0)) {
3002 add_glue_ref
(zero_glue
);
3003 delete_glue_ref
(cur_val
);
3004 cur_val
= zero_glue
;
3008 @ We use the fact that |register
<advance
<multiply
<divide|
3010 Compute the register location |l| and its type |p|
; but |return| if invalid
3011 Here we use the fact that the consecutive codes |int_val..mu_val| and
3012 |assign_int..assign_mu_glue| correspond to each other nicely.
3015 void do_register_command
(int a
)
3018 halfword q
= cur_cmd
;
3020 if
(q
!= register_cmd
) {
3022 if
((cur_cmd
>= assign_int_cmd
) && (cur_cmd <= assign_mu_glue_cmd)) {
3024 p
= cur_cmd
- assign_int_cmd
;
3027 if
(cur_cmd
!= register_cmd
) {
3028 print_err
("You can't use `");
3029 print_cmd_chr
((quarterword
) cur_cmd
, cur_chr
);
3031 print_cmd_chr
((quarterword
) q
, 0);
3032 help1
("I'm forgetting what you said and not changing anything.");
3038 scan_register_num
();
3039 if
(p
== int_val_level
)
3040 l
= cur_val
+ count_base
;
3041 else if
(p
== attr_val_level
)
3042 l
= cur_val
+ attribute_base
;
3043 else if
(p
== dimen_val_level
)
3044 l
= cur_val
+ scaled_base
;
3045 else if
(p
== glue_val_level
)
3046 l
= cur_val
+ skip_base
;
3047 else if
(p
== mu_val_level
)
3048 l
= cur_val
+ mu_skip_base
;
3050 if
(q
== register_cmd
) {
3051 scan_optional_equals
();
3052 } else if
(scan_keyword
("by")) {
3053 /* optional `\.
{by
}'
*/
3055 arith_error
= false
;
3056 if
(q
< multiply_cmd
) {
3057 /* Compute result of |register| or |advance|
, put it in |cur_val|
*/
3058 if
(p
< glue_val_level
) {
3059 if
((p
== int_val_level
) ||
(p
== attr_val_level
))
3062 scan_normal_dimen
();
3063 if
(q
== advance_cmd
)
3064 cur_val
= cur_val
+ eqtb
[l
].cint
;
3067 if
(q
== advance_cmd
) {
3068 /* Compute the sum of two glue specs
*/
3069 halfword r
= equiv
(l
);
3070 q
= new_spec
(cur_val
);
3071 delete_glue_ref
(cur_val
);
3072 width
(q
) = width
(q
) + width
(r
);
3073 if
(stretch
(q
) == 0) {
3074 stretch_order
(q
) = normal
;
3076 if
(stretch_order
(q
) == stretch_order
(r
)) {
3077 stretch
(q
) = stretch
(q
) + stretch
(r
);
3078 } else if
((stretch_order
(q
) < stretch_order
(r
))
3079 && (stretch(r) != 0)) {
3080 stretch
(q
) = stretch
(r
);
3081 stretch_order
(q
) = stretch_order
(r
);
3083 if
(shrink
(q
) == 0) {
3084 shrink_order
(q
) = normal
;
3086 if
(shrink_order
(q
) == shrink_order
(r
)) {
3087 shrink
(q
) = shrink
(q
) + shrink
(r
);
3088 } else if
((shrink_order
(q
) < shrink_order
(r
))
3089 && (shrink(r) != 0)) {
3090 shrink
(q
) = shrink
(r
);
3091 shrink_order
(q
) = shrink_order
(r
);
3097 /* Compute result of |multiply| or |divide|
, put it in |cur_val|
*/
3099 if
(p
< glue_val_level
) {
3100 if
(q
== multiply_cmd
) {
3101 if
((p
== int_val_level
) ||
(p
== attr_val_level
)) {
3102 cur_val
= mult_integers
(eqtb
[l
].cint
, cur_val
);
3104 cur_val
= nx_plus_y
(eqtb
[l
].cint
, cur_val
, 0);
3107 cur_val
= x_over_n
(eqtb
[l
].cint
, cur_val
);
3110 halfword s
= equiv
(l
);
3111 halfword r
= new_spec
(s
);
3112 if
(q
== multiply_cmd
) {
3113 width
(r
) = nx_plus_y
(width
(s
), cur_val
, 0);
3114 stretch
(r
) = nx_plus_y
(stretch
(s
), cur_val
, 0);
3115 shrink
(r
) = nx_plus_y
(shrink
(s
), cur_val
, 0);
3117 width
(r
) = x_over_n
(width
(s
), cur_val
);
3118 stretch
(r
) = x_over_n
(stretch
(s
), cur_val
);
3119 shrink
(r
) = x_over_n
(shrink
(s
), cur_val
);
3125 print_err
("Arithmetic overflow");
3126 help2
("I can't carry out that multiplication or division,",
3127 "since the result is out of range.");
3128 if
(p
>= glue_val_level
)
3129 delete_glue_ref
(cur_val
);
3133 if
(p
< glue_val_level
) {
3134 if
(p
== attr_val_level
) {
3135 if
((l
- attribute_base
) > max_used_attr
)
3136 max_used_attr
= (l
- attribute_base
);
3137 attr_list_cache
= cache_disabled
;
3139 if
((p
== int_val_level
) ||
(p
== dimen_val_level
))
3140 assign_internal_value
(a
, l
, cur_val
);
3142 word_define
(l
, cur_val
);
3145 define
(l
, glue_ref_cmd
, cur_val
);
3150 void alter_aux
(void
)
3152 halfword c
; /* |hmode| or |vmode|
*/
3153 if
(cur_chr
!= abs
(mode
)) {
3154 report_illegal_case
();
3157 scan_optional_equals
();
3159 scan_normal_dimen
();
3160 prev_depth
= cur_val
;
3163 if
((cur_val
<= 0) ||
(cur_val
> 32767)) {
3164 print_err
("Bad space factor");
3165 help1
("I allow only values in the range 1..32767 here.");
3168 space_factor
= cur_val
;
3175 void alter_prev_graf
(void
)
3177 int p
; /* index into |nest|
*/
3179 while
(abs
(nest
[p
].mode_field
) != vmode
)
3181 scan_optional_equals
();
3184 print_err
("Bad \\prevgraf");
3185 help1
("I allow only nonnegative values here.");
3188 nest
[p
].pg_field
= cur_val
;
3193 void alter_page_so_far
(void
)
3195 int c
; /* index into |page_so_far|
*/
3197 scan_optional_equals
();
3198 scan_normal_dimen
();
3199 page_so_far
[c
] = cur_val
;
3203 void alter_integer
(void
)
3205 int c
; /* 0 for \.
{\\deadcycles
}, 1 for \.
{\\insertpenalties
}, etc.
*/
3207 scan_optional_equals
();
3210 dead_cycles
= cur_val
;
3211 } else if
(c
== 2) {
3212 if
((cur_val
< batch_mode
) ||
(cur_val
> error_stop_mode
)) {
3213 print_err
("Bad interaction mode");
3214 help2
("Modes are 0=batch, 1=nonstop, 2=scroll, and",
3215 "3=errorstop. Proceed, and I'll ignore this case.");
3222 insert_penalties
= cur_val
;
3227 void alter_box_dimen
(void
)
3229 int c
; /* |width_offset| or |height_offset| or |depth_offset|
*/
3230 int b
; /* box number
*/
3232 scan_register_num
();
3234 scan_optional_equals
();
3235 scan_normal_dimen
();
3237 varmem
[box
(b
) + c
].cint
= cur_val
;
3241 void new_interaction
(void
)
3244 interaction
= cur_chr
;
3245 if
(interaction
== batch_mode
)
3246 kpse_make_tex_discard_errors
= 1;
3248 kpse_make_tex_discard_errors
= 0;
3249 fixup_selector
(log_opened_global
);
3252 @ The \.
{\\afterassignment
} command puts a token into the global
3253 variable |after_token|. This global variable is examined just after
3254 every assignment has been performed.
3257 halfword after_token
; /* zero
, or a saved token
*/
3259 @ Here is a procedure that might be called `Get the next non-blank non-relax
3260 non-call non-assignment token'.
3263 void do_assignments
(void
)
3266 /* Get the next non-blank non-relax...
*/
3269 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
3270 if
(cur_cmd
<= max_non_prefixed_command
)
3272 set_box_allowed
= false
;
3274 set_box_allowed
= true
;
3279 void open_or_close_in
(void
)
3281 int c
; /* 1 for \.
{\\openin
}, 0 for \.
{\\closein
} */
3282 int n
; /* stream number
*/
3285 scan_four_bit_int
();
3287 if
(read_open
[n
] != closed
) {
3288 lua_a_close_in
(read_file
[n
], (n
+ 1));
3289 read_open
[n
] = closed
;
3292 scan_optional_equals
();
3295 } while
((cur_cmd
== spacer_cmd
) ||
(cur_cmd
== relax_cmd
));
3297 if
(cur_cmd
!= left_brace_cmd
) {
3298 scan_file_name
(); /* set |cur_name| to desired file name
*/
3299 if
(cur_ext
== get_nullstr
())
3300 cur_ext
= maketexstring
(".tex");
3302 scan_file_name_toks
();
3304 fn
= pack_file_name
(cur_name
, cur_area
, cur_ext
);
3305 if
(lua_a_open_in
(&(read_file[n]), fn, (n + 1))) {
3306 read_open
[n
] = just_open
;
3312 boolean long_help_seen
; /* has the long \.
{\\errmessage
} help been used?
*/
3314 void issue_message
(void
)
3316 int old_setting
; /* holds |selector| setting
*/
3317 int c
; /* identifies \.
{\\message
} and \.
{\\errmessage
} */
3318 str_number s
; /* the message
*/
3320 (void
) scan_toks
(false
, true
);
3321 old_setting
= selector
;
3322 selector
= new_string
;
3323 token_show
(def_ref
);
3324 selector
= old_setting
;
3325 flush_list
(def_ref
);
3329 /* Print string |s| on the terminal
*/
3330 if
(term_offset
+ (int
) str_length
(s
) > max_print_line
- 2)
3332 else if
((term_offset
> 0) ||
(file_offset
> 0))
3338 /* Print string |s| as an error message
*/
3339 /* If \.
{\\errmessage
} occurs often in |scroll_mode|
, without user-defined
3340 \.
{\\errhelp
}, we don't want to give a long help message each time. So we
3341 give a verbose explanation only once.
*/
3344 if
(err_help
!= null
) {
3345 use_err_help
= true
;
3346 } else if
(long_help_seen
) {
3347 help1
("(That was another \\errmessage.)");
3349 if
(interaction
< error_stop_mode
)
3350 long_help_seen
= true
;
3351 help4
("This error message was generated by an \\errmessage",
3352 "command, so I can't give any explicit help.",
3353 "Pretend that you're Hercule Poirot: Examine all clues,",
3354 "and deduce the truth by order and method.");
3357 use_err_help
= false
;
3363 @ The |error| routine calls on |give_err_help| if help is requested from
3364 the |err_help| parameter.
3367 void give_err_help
(void
)
3369 token_show
(err_help
);
3372 @ The \.
{\\uppercase
} and \.
{\\lowercase
} commands are implemented by
3373 building a token list and then changing the cases of the letters in it.
3376 void shift_case
(void
)
3378 halfword b
; /* |lc_code_base| or |uc_code_base|
*/
3379 halfword p
; /* runs through the token list
*/
3380 halfword t
; /* token
*/
3381 halfword c
; /* character code
*/
3382 halfword i
; /* inbetween
*/
3384 p
= scan_toks
(false
, false
);
3385 p
= token_link
(def_ref
);
3387 /* Change the case of the token in |p|
, if a change is appropriate
*/
3389 When the case of a |chr_code| changes
, we don't change the |cmd|.
3390 We also change active characters.
3393 if
(t
< cs_token_flag
) {
3394 c
= t
% STRING_OFFSET
;
3395 if
(b
== uc_code_base
)
3400 set_token_info
(p
, t
- c
+ i
);
3401 } else if
(is_active_cs
(cs_text
(t
- cs_token_flag
))) {
3402 c
= active_cs_value
(cs_text
(t
- cs_token_flag
));
3403 if
(b
== uc_code_base
)
3408 set_token_info
(p
, active_to_cs
(i
, true
) + cs_token_flag
);
3412 back_list
(token_link
(def_ref
));
3413 free_avail
(def_ref
); /* omit reference count
*/
3416 @ We come finally to the last pieces missing from |main_control|
, namely the
3417 `\.
{\\show
}' commands that are useful when debugging.
3420 void show_whatever
(void
)
3422 halfword p
; /* tail of a token list to show
*/
3423 int t
; /* type of conditional being shown
*/
3424 int m
; /* upper bound on |fi_or_else| codes
*/
3425 int l
; /* line where that conditional began
*/
3426 int n
; /* level of \.
{\\if...\\fi
} nesting
*/
3433 /* Show the current contents of a box
*/
3434 scan_register_num
();
3436 tprint_nl
("> \\box");
3439 if
(box
(cur_val
) == null
)
3442 show_box
(box
(cur_val
));
3445 /* Show the current meaning of a token
, then |goto common_ending|
*/
3447 if
(interaction
== error_stop_mode
)
3457 /* Cases for |show_whatever|
*/
3466 if
(cond_ptr
== null
) {
3468 tprint
("no active conditionals");
3475 } while
(p
!= null
);
3481 tprint_nl
("### level ");
3484 print_cmd_chr
(if_test_cmd
, t
);
3489 t
= if_limit_subtype
(p
);
3490 l
= if_line_field
(p
);
3491 m
= if_limit_type
(p
);
3493 } while
(p
!= null
);
3497 /* Show the current value of some parameter or register
,
3498 then |goto common_ending|
*/
3500 if
(interaction
== error_stop_mode
)
3503 token_show
(temp_token_head
);
3504 flush_list
(token_link
(temp_token_head
));
3508 /* Complete a potentially long \.
{\\show
} command
*/
3509 end_diagnostic
(true
);
3511 if
(selector
== term_and_log
) {
3512 if
(tracing_online
<= 0) {
3513 selector
= term_only
;
3514 tprint
(" (see the transcript file)");
3515 selector
= term_and_log
;
3519 if
(interaction
< error_stop_mode
) {
3522 } else if
(tracing_online
> 0) {
3523 help3
("This isn't an error message; I'm just \\showing something.",
3524 "Type `I\\show...' to show more (e.g., \\show\\cs,",
3525 "\\showthe\\count10, \\showbox255, \\showlists).");
3527 help5
("This isn't an error message; I'm just \\showing something.",
3528 "Type `I\\show...' to show more (e.g., \\show\\cs,",
3529 "\\showthe\\count10, \\showbox255, \\showlists).",
3530 "And type `I\\tracingonline=1\\show...' to show boxes and",
3531 "lists on your terminal as well as in the transcript file.");
3537 void initialize
(void
)
3538 { /* this procedure gets things started properly
*/
3539 int k
; /* index into |mem|
, |eqtb|
, etc.
*/
3540 /* Initialize whatever \TeX\ might access
*/
3541 /* Set initial values of key variables
*/
3542 initialize_errors
();
3543 initialize_arithmetic
();
3545 attr_list_cache
= cache_disabled
;
3546 initialize_nesting
();
3548 /* Start a new current page
*/
3549 page_contents
= empty
;
3550 page_tail
= page_head
;
3552 vlink
(page_head
) = null
;
3554 last_glue
= max_halfword
;
3557 last_node_type
= -1;
3561 initialize_equivalents
();
3562 no_new_control_sequence
= true
; /* new identifiers are usually forbidden
*/
3569 static_pdf
= init_pdf_struct
(static_pdf
); /* should be init_backend
() */
3572 format_name
= get_nullstr
();
3573 initialize_directions
();
3574 initialize_write_files
();
3575 seconds_and_micros
(epochseconds
, microseconds
);
3576 init_start_time
(static_pdf
);
3578 edit_name_start
= 0;
3579 stop_at_space
= true
;
3582 /* Initialize table entries
(done by \.
{INITEX
} only
) */
3585 initialize_tokens
();
3586 /* Initialize the special list heads and constant nodes
*/
3587 initialize_alignments
();
3588 initialize_buildpage
();
3590 initialize_active
();
3592 set_eq_type
(undefined_control_sequence
, undefined_cs_cmd
);
3593 set_equiv
(undefined_control_sequence
, null
);
3594 set_eq_level
(undefined_control_sequence
, level_zero
);
3595 for
(k
= null_cs
; k
<= (eqtb_top
- 1); k
++)
3596 eqtb
[k
] = eqtb
[undefined_control_sequence
];
3597 set_equiv
(glue_base
, zero_glue
);
3598 set_eq_level
(glue_base
, level_one
);
3599 set_eq_type
(glue_base
, glue_ref_cmd
);
3600 for
(k
= glue_base
+ 1; k
<= local_base
- 1; k
++)
3601 eqtb
[k
] = eqtb
[glue_base
];
3602 glue_ref_count
(zero_glue
) =
3603 glue_ref_count
(zero_glue
) + local_base
- glue_base
;
3605 par_shape_ptr
= null
;
3606 set_eq_type
(par_shape_loc
, shape_ref_cmd
);
3607 set_eq_level
(par_shape_loc
, level_one
);
3608 for
(k
= etex_pen_base
; k
<= (etex_pens
- 1); k
++)
3609 eqtb
[k
] = eqtb
[par_shape_loc
];
3610 for
(k
= output_routine_loc
; k
<= toks_base
+ biggest_reg
; k
++)
3611 eqtb
[k
] = eqtb
[undefined_control_sequence
];
3613 set_eq_type
(box_base
, box_ref_cmd
);
3614 set_eq_level
(box_base
, level_one
);
3615 for
(k
= box_base
+ 1; k
<= (box_base
+ biggest_reg
); k
++)
3616 eqtb
[k
] = eqtb
[box_base
];
3617 cur_font
= null_font
;
3618 set_eq_type
(cur_font_loc
, data_cmd
);
3619 set_eq_level
(cur_font_loc
, level_one
);
3620 set_equiv
(cat_code_base
, 0);
3621 set_eq_type
(cat_code_base
, data_cmd
);
3622 set_eq_level
(cat_code_base
, level_one
);
3623 eqtb
[internal_math_param_base
] = eqtb
[cat_code_base
];
3624 eqtb
[lc_code_base
] = eqtb
[cat_code_base
];
3625 eqtb
[uc_code_base
] = eqtb
[cat_code_base
];
3626 eqtb
[sf_code_base
] = eqtb
[cat_code_base
];
3627 eqtb
[math_code_base
] = eqtb
[cat_code_base
];
3629 initialize_math_codes
();
3630 initialize_text_codes
();
3631 initex_cat_codes
(0);
3632 for
(k
= '
0'
; k
<= '
9'
; k
++)
3633 set_math_code
(k
, var_code
, 0, k
, level_one
);
3634 for
(k
= 'A'
; k
<= 'Z'
; k
++) {
3635 set_math_code
(k
, var_code
, 1, k
, level_one
);
3636 set_math_code
((k
+ 32), var_code
, 1, (k
+ 32), level_one
);
3637 set_lc_code
(k
, k
+ 32, level_one
);
3638 set_lc_code
(k
+ 32, k
+ 32, level_one
);
3639 set_uc_code
(k
, k
, level_one
);
3640 set_uc_code
(k
+ 32, k
, level_one
);
3641 set_sf_code
(k
, 999, level_one
);
3643 for
(k
= int_base
; k
<= attribute_base
- 1; k
++)
3645 for
(k
= attribute_base
; k
<= del_code_base
- 1; k
++)
3646 eqtb
[k
].cint
= UNUSED_ATTRIBUTE
;
3650 max_dead_cycles
= 25;
3652 end_line_char
= carriage_return
;
3653 set_del_code
('.'
, 0, 0, 0, 0, level_one
); /* this null delimiter is used in error recovery
*/
3654 ex_hyphen_char
= '
-'
;
3656 for
(k
= dimen_base
; k
<= eqtb_size
; k
++)
3658 page_left_offset
= one_inch
;
3659 page_top_offset
= one_inch
;
3660 page_right_offset
= one_inch
;
3661 page_bottom_offset
= one_inch
;
3662 ini_init_primitives
();
3663 hash_used
= frozen_control_sequence
; /* nothing is used
*/
3666 set_eq_type
(frozen_dont_expand
, dont_expand_cmd
);
3667 cs_text
(frozen_dont_expand
) = maketexstring
("notexpanded:");
3668 set_eq_type
(frozen_primitive
, ignore_spaces_cmd
);
3669 set_equiv
(frozen_primitive
, 1);
3670 set_eq_level
(frozen_primitive
, level_one
);
3671 cs_text
(frozen_primitive
) = maketexstring
("primitive");
3675 math_eqno_gap_step
= 1000 ;
3676 cs_text
(frozen_protection
) = maketexstring
("inaccessible");
3677 format_ident
= maketexstring
(" (INITEX)");
3678 cs_text
(end_write
) = maketexstring
("endwrite");
3679 set_eq_level
(end_write
, level_one
);
3680 set_eq_type
(end_write
, outer_call_cmd
);
3681 set_equiv
(end_write
, null
);
3684 synctexoffset
= int_base
+ synctex_code
;