Section 1208: Mode-independent processing
The long main_control procedure has now been fully specified, except for certain activities that are independent of the current mode. These activities do not change the current vlist or hlist or mlist; if they change anything, it is the value of a parameter or the meaning of a control sequence.
Assignments to values in eqtb can be global or local.
Furthermore, a control sequence can be defined to be ‘\long
’ or ‘\outer
’, and it might or might not be expanded.
The prefixes ‘\global
’, ‘\long
’, and ‘\outer
’ can occur in any order.
Therefore we assign binary numeric codes, making it possible to accumulate the union of all specified prefixes by adding the corresponding codes.
(Pascal’s set operations could also have been used.)
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("long", PREFIX, 1);
primitive("outer", PREFIX, 2);
primitive("global", PREFIX, 4);
primitive("def", DEF, 0);
primitive("gdef", DEF, 1);
primitive("edef", DEF, 2);
primitive("xdef", DEF, 3);
Section 1209
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case PREFIX:
if (chr_code == 1) {
print_esc("long");
}
else if (chr_code == 2) {
print_esc("outer");
}
else {
print_esc("global");
}
break;
case DEF:
if (chr_code == 0) {
print_esc("def");
}
else if (chr_code == 1) {
print_esc("gdef");
}
else if (chr_code == 2) {
print_esc("edef");
}
else {
print_esc("xdef");
}
break;
Section 1210
Every prefix, and every command code that might or might not be prefixed, calls the action procedure prefixed_command. This routine accumulates a sequence of prefixes until coming to a non-prefix, then it carries out the command.
⟨ Cases of main_control that don’t depend on mode 1210 ⟩≡
any_mode(TOKS_REGISTER):
any_mode(ASSIGN_TOKS):
any_mode(ASSIGN_INT):
any_mode(ASSIGN_DIMEN):
any_mode(ASSIGN_GLUE):
any_mode(ASSIGN_MU_GLUE):
any_mode(ASSIGN_FONT_DIMEN):
any_mode(ASSIGN_FONT_INT):
any_mode(SET_AUX):
any_mode(SET_PREV_GRAF):
any_mode(SET_PAGE_DIMEN):
any_mode(SET_PAGE_INT):
any_mode(SET_BOX_DIMEN):
any_mode(SET_SHAPE):
any_mode(DEF_CODE):
any_mode(DEF_FAMILY):
any_mode(SET_FONT):
any_mode(DEF_FONT):
any_mode(REGISTER):
any_mode(ADVANCE):
any_mode(MULTIPLY):
any_mode(DIVIDE):
any_mode(PREFIX):
any_mode(LET):
any_mode(SHORTHAND_DEF):
any_mode(READ_TO_CS):
any_mode(DEF):
any_mode(SET_BOX):
any_mode(HYPH_DATA):
any_mode(SET_INTERACTION):
prefixed_command();
break;
Section 1211
If the user says, e.g., ‘\global\global
’, the redundancy is silently accepted.
// << Start file |independent.c|, 1382 >>
// << Declare subprocedures for |prefixed_command|, 1215 >>
void prefixed_command() {
small_number a; // accumulated prefix codes so far
internal_font_number f; // identifies a font
halfword j; // index into a \parshape specification
int k; // index into |font_info|
pointer p, q; // for temporary short - term use
int n; // ditto
bool e; // should a definition be expanded? or was \let not done?
a = 0;
while (cur_cmd == PREFIX) {
if (!odd(a / cur_chr)) {
a += cur_chr;
}
// << Get the next non-blank non-relax non-call token, 404 >>
if (cur_cmd <= MAX_NON_PREFIXED_COMMAND) {
// << Discard erroneous prefixes and |return|, 1212 >>
}
}
// << Discard the prefixes \long and \outer if they are irrelevant, 1213 >>
// << Adjust for the setting of \globaldefs, 1214 >>
switch (cur_cmd) {
// << Assignments, 1217 >>
default:
confusion("prefix");
}
done:
// << Insert a token saved by \afterassignment, if any, 1269 >>
}
Section 1212
⟨ Discard erroneous prefixes and return 1212 ⟩≡
print_err("You can't use a prefix with `");
print_cmd_chr(cur_cmd, cur_chr);
print_char('\'');
help1("I'll pretend you didn't say \\long or \\outer or \\global.");
back_error();
return;
Section 1213
⟨ Discard the prefixes \long and \outer if they are irrelevant 1213 ⟩≡
if (cur_cmd != DEF && a % 4 != 0) {
print_err("You can't use `");
print_esc("long");
print("' or `");
print_esc("outer");
print("' with `");
print_cmd_chr(cur_cmd, cur_chr);
print_char('\'');
help1("I'll pretend you didn't say \\long or \\outer here.");
error();
}
Section 1214
The previous routine does not have to adjust a so that a mod 4 = 0, since the following routines test for the \global
prefix as follows.
#define global (a >= 4)
#define define(...) \
do { \
if (global) { \
geq_define(__VA_ARGS__); \
} \
else { \
eq_define(__VA_ARGS__); \
} \
} while (0)
#define word_define(...) \
do { \
if (global) { \
geq_word_define(__VA_ARGS__); \
} \
else { \
eq_word_define(__VA_ARGS__); \
} \
} while (0)
⟨ Adjust for the setting of \globaldefs 1214 ⟩≡
if (global_defs != 0) {
if (global_defs < 0) {
if (global) {
a -= 4;
}
}
else if (!global) {
a += 4;
}
}
Section 1215
When a control sequence is to be defined, by \def
or \let
or something similar, the get_r_token routine will substitute a special control sequence for a token that is not redefinable.
⟨ Declare subprocedures for prefixed_command 1215 ⟩≡
void get_r_token() {
restart:
do {
get_token();
} while (cur_tok == SPACE_TOKEN);
if (cur_cs == 0 || cur_cs > FROZEN_CONTROL_SEQUENCE) {
print_err("Missing control sequence inserted");
help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.")
("I've inserted an inaccessible control sequence so that your")
("definition will be completed without mixing me up too badly.")
("You can recover graciously from this error, if you're")
("careful; see exercise 27.2 in The TeXbook.");
if (cur_cs == 0) {
back_input();
}
cur_tok = CS_TOKEN_FLAG + FROZEN_PROTECTION;
ins_error();
goto restart;
}
}
Section 1216
String
"inaccessible"
must be added to the pool.
⟨ Read the other strings 51 ⟩+≡
put_string("inaccessible"); // INACCESSIBLE_STRING: 268
⟨ Internal strings numbers in the pool 51 ⟩+≡
#define INACCESSIBLE_STRING 268
⟨ Initialize table entries (done by INITEX only) 164 ⟩+≡
text(FROZEN_PROTECTION) = INACCESSIBLE_STRING;
Section 1217
Here’s an example of the way many of the following routines operate. (Unfortunately, they aren’t all as simple as this.)
⟨ Assignments 1217 ⟩≡
case SET_FONT:
define(CUR_FONT_LOC, DATA, cur_chr);
break;
Section 1218
When a DEF command has been scanned, cur_chr is odd if the definition is supposed to be global, and cur_chr 2 if the definition is supposed to be expanded.
⟨ Assignments 1217 ⟩+≡
case DEF:
if (odd(cur_chr)
&& !global
&& global_defs >= 0)
{
a += 4;
}
e = (cur_chr >= 2);
get_r_token();
p = cur_cs;
q = scan_toks(true, e);
define(p, CALL + (a % 4), def_ref);
break;
Section 1219
Both \let
and \futurelet
share the command code LET.
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("let", LET, NORMAL);
primitive("futurelet", LET, NORMAL + 1);
Section 1220
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case LET:
if (chr_code != NORMAL) {
print_esc("futurelet");
}
else {
print_esc("let");
}
break;
Section 1221
⟨ Assignments 1217 ⟩+≡
case LET:
n = cur_chr;
get_r_token();
p = cur_cs;
if (n == NORMAL) {
do {
get_token();
} while (cur_cmd == SPACER);
if (cur_tok == OTHER_TOKEN + '=') {
get_token();
if (cur_cmd == SPACER) {
get_token();
}
}
}
else {
get_token();
q = cur_tok;
get_token();
back_input();
cur_tok = q;
back_input(); // look ahead, then back up
} // note that |back_input| doesn't affect |cur_cmd|, |cur_chr|
if (cur_cmd >= CALL) {
add_token_ref(cur_chr);
}
define(p, cur_cmd, cur_chr);
break;
Section 1222
A \chardef
creates a control sequence whose cmd is CHAR_GIVEN;
a \mathchardef
creates a control sequence whose cmd is MATH_GIVEN;
and the corresponding chr is the character code or math code.
A \countdef
or \dimendef
or \skipdef
or \muskipdef
creates a control sequence whose cmd is ASSIGN_INT or or ASSIGN_MU_GLUE, and the corresponding chr is the eqtb location of the internal register in question.
#define CHAR_DEF_CODE 0 // |shorthand_def| for \chardef
#define MATH_CHAR_DEF_CODE 1 // |shorthand_def| for \mathchardef
#define COUNT_DEF_CODE 2 // |shorthand_def| for \countdef
#define DIMEN_DEF_CODE 3 // |shorthand_def| for \dimendef
#define SKIP_DEF_CODE 4 // |shorthand_def| for \skipdef
#define MU_SKIP_DEF_CODE 5 // |shorthand_def| for \muskipdef
#define TOKS_DEF_CODE 6 // |shorthand_def| for \toksdef
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("chardef", SHORTHAND_DEF, CHAR_DEF_CODE);
primitive("mathchardef", SHORTHAND_DEF, MATH_CHAR_DEF_CODE);
primitive("countdef", SHORTHAND_DEF, COUNT_DEF_CODE);
primitive("dimendef", SHORTHAND_DEF, DIMEN_DEF_CODE);
primitive("skipdef", SHORTHAND_DEF, SKIP_DEF_CODE);
primitive("muskipdef", SHORTHAND_DEF, MU_SKIP_DEF_CODE);
primitive("toksdef", SHORTHAND_DEF, TOKS_DEF_CODE);
Section 1223
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case SHORTHAND_DEF:
switch (chr_code) {
case CHAR_DEF_CODE:
print_esc("chardef");
break;
case MATH_CHAR_DEF_CODE:
print_esc("mathchardef");
break;
case COUNT_DEF_CODE:
print_esc("countdef");
break;
case DIMEN_DEF_CODE:
print_esc("dimendef");
break;
case SKIP_DEF_CODE:
print_esc("skipdef");
break;
case MU_SKIP_DEF_CODE:
print_esc("muskipdef");
break;
default:
print_esc("toksdef");
}
break;
case CHAR_GIVEN:
print_esc("char");
print_hex(chr_code);
break;
case MATH_GIVEN:
print_esc("mathchar");
print_hex(chr_code);
break;
Section 1224
We temporarily define p to be RELAX, so that an occurrence of p while scanning the definition will simply stop the scanning instead of producing an “undefined control sequence” error or expanding the previous meaning.
This allows, for instance, ‘\chardef\foo=123\foo
’.
⟨ Assignments 1217 ⟩+≡
case SHORTHAND_DEF:
n = cur_chr;
get_r_token();
p = cur_cs;
define(p, RELAX, 256);
scan_optional_equals();
switch (n) {
case CHAR_DEF_CODE:
scan_char_num();
define(p, CHAR_GIVEN, cur_val);
break;
case MATH_CHAR_DEF_CODE:
scan_fifteen_bit_int();
define(p, MATH_GIVEN, cur_val);
break;
default:
scan_eight_bit_int();
switch (n) {
case COUNT_DEF_CODE:
define(p, ASSIGN_INT, COUNT_BASE + cur_val);
break;
case DIMEN_DEF_CODE:
define(p, ASSIGN_DIMEN, SCALED_BASE + cur_val);
break;
case SKIP_DEF_CODE:
define(p, ASSIGN_GLUE, SKIP_BASE + cur_val);
break;
case MU_SKIP_DEF_CODE:
define(p, ASSIGN_MU_GLUE, MU_SKIP_BASE + cur_val);
break;
case TOKS_DEF_CODE:
define(p, ASSIGN_TOKS, TOKS_BASE + cur_val);
} // there are no other cases
}
break;
Section 1225
⟨ Assignments 1217 ⟩+≡
case READ_TO_CS:
scan_int();
n = cur_val;
if (!scan_keyword("to")) {
print_err("Missing `to' inserted");
help2("You should have said `\\read<number> to \\cs'.")
("I'm going to look for the \\cs now.");
error();
}
get_r_token();
p = cur_cs;
read_toks(n, p);
define(p, CALL, cur_val);
break;
Section 1226
The token-list parameters, \output
and \everypar
, etc., receive their values in the following way.
(For safety’s sake, we place an enclosing pair of braces around an \output
list.)
⟨ Assignments 1217 ⟩+≡
case TOKS_REGISTER:
case ASSIGN_TOKS:
q = cur_cs;
if (cur_cmd == TOKS_REGISTER) {
scan_eight_bit_int();
p = TOKS_BASE + cur_val;
}
else {
p = cur_chr; // |p = EVERY_PAR_LOC| or |OUTPUT_ROUTINE_LOC| or...
}
scan_optional_equals();
// << Get the next non-blank non-relax non-call token, 404 >>
if (cur_cmd != LEFT_BRACE) {
// << If the right-hand side is a token parameter or token register, finish the assignment and |goto done|, 1227 >>
}
back_input();
cur_cs = q;
q = scan_toks(false, false);
if (link(def_ref) == null) {
// empty list: revert to the default
define(p, UNDEFINED_CS, null);
free_avail(def_ref);
}
else {
if (p == OUTPUT_ROUTINE_LOC) {
// enclose in curlies
link(q) = get_avail();
q = link(q);
info(q) = RIGHT_BRACE_TOKEN + '}';
q = get_avail();
info(q) = LEFT_BRACE_TOKEN + '{';
link(q) = link(def_ref);
link(def_ref) = q;
}
define(p, CALL, def_ref);
}
break;
Section 1227
⟨ If the right-hand side is a token parameter or token register, finish the assignment and goto done 1227 ⟩≡
if (cur_cmd == TOKS_REGISTER) {
scan_eight_bit_int();
cur_cmd = ASSIGN_TOKS;
cur_chr = TOKS_BASE + cur_val;
}
if (cur_cmd == ASSIGN_TOKS) {
q = equiv(cur_chr);
if (q == null) {
define(p, UNDEFINED_CS, null);
}
else {
add_token_ref(q);
define(p, CALL, q);
}
goto done;
}
Section 1228
Similar routines are used to assign values to the numeric parameters.
⟨ Assignments 1217 ⟩+≡
case ASSIGN_INT:
p = cur_chr;
scan_optional_equals();
scan_int();
word_define(p, cur_val);
break;
case ASSIGN_DIMEN:
p = cur_chr;
scan_optional_equals();
scan_normal_dimen;
word_define(p, cur_val);
break;
case ASSIGN_GLUE:
case ASSIGN_MU_GLUE:
p = cur_chr;
n = cur_cmd;
scan_optional_equals();
if (n == ASSIGN_MU_GLUE) {
scan_glue(MU_VAL);
}
else {
scan_glue(GLUE_VAL);
}
trap_zero_glue();
define(p, GLUE_REF, cur_val);
break;
Section 1229
When a glue register or parameter becomes zero, it will always point to ZERO_GLUE because of the following procedure. (Exception: The tabskip glue isn’t trapped while preambles are being scanned.)
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void trap_zero_glue() {
if (width(cur_val) == 0
&& stretch(cur_val) == 0
&& shrink(cur_val) == 0)
{
add_glue_ref(ZERO_GLUE);
delete_glue_ref(cur_val);
cur_val = ZERO_GLUE;
}
}
Section 1230
The various character code tables are changed by the DEF_CODE commands, and the font families are declared by DEF_FAMILY.
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("catcode", DEF_CODE, CAT_CODE_BASE);
primitive("mathcode", DEF_CODE, MATH_CODE_BASE);
primitive("lccode", DEF_CODE, LC_CODE_BASE);
primitive("uccode", DEF_CODE, UC_CODE_BASE);
primitive("sfcode", DEF_CODE, SF_CODE_BASE);
primitive("delcode", DEF_CODE, DEL_CODE_BASE);
primitive("textfont", DEF_FAMILY, MATH_FONT_BASE);
primitive("scriptfont", DEF_FAMILY, MATH_FONT_BASE + SCRIPT_SIZE);
primitive("scriptscriptfont", DEF_FAMILY, MATH_FONT_BASE + SCRIPT_SCRIPT_SIZE);
Section 1231
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case DEF_CODE:
if (chr_code == CAT_CODE_BASE) {
print_esc("catcode");
}
else if (chr_code == MATH_CODE_BASE) {
print_esc("mathcode");
}
else if (chr_code == LC_CODE_BASE) {
print_esc("lccode");
}
else if (chr_code == UC_CODE_BASE) {
print_esc("uccode");
}
else if (chr_code == SF_CODE_BASE) {
print_esc("sfcode");
}
else {
print_esc("delcode");
}
break;
case DEF_FAMILY:
print_size(chr_code - MATH_FONT_BASE);
break;
Section 1232
The different types of code values have different legal ranges; the following program is careful to check each case properly.
⟨ Assignments 1217 ⟩+≡
case DEF_CODE:
// << Let |n| be the largest legal code value, based on |cur_chr|, 1233 >>
p = cur_chr;
scan_char_num();
p += cur_val;
scan_optional_equals();
scan_int();
if ((cur_val < 0 && p < DEL_CODE_BASE) || cur_val > n) {
print_err("Invalid code (");
print_int(cur_val);
if (p < DEL_CODE_BASE) {
print("), should be in the range 0..");
}
else {
print("), should be at most ");
}
print_int(n);
help1("I'm going to use 0 instead of that illegal code value.");
error();
cur_val = 0;
}
if (p < MATH_CODE_BASE) {
define(p, DATA, cur_val);
}
else if (p < DEL_CODE_BASE) {
define(p, DATA, hi(cur_val));
}
else {
word_define(p, cur_val);
}
break;
Section 1233
⟨ Let n be the largest legal code value, based on cur_chr 1233 ⟩≡
if (cur_chr == CAT_CODE_BASE) {
n = MAX_CHAR_CODE;
}
else if (cur_chr == MATH_CODE_BASE) {
n = 0x8000;
}
else if (cur_chr == SF_CODE_BASE) {
n = 0x7fff;
}
else if (cur_chr == DEL_CODE_BASE) {
n = 0xffffff;
}
else {
n = 255;
}
Section 1234
⟨ Assignments 1217 ⟩+≡
case DEF_FAMILY:
p = cur_chr;
scan_four_bit_int();
p += cur_val;
scan_optional_equals();
scan_font_ident();
define(p, DATA, cur_val);
break;
Section 1235
Next we consider changes to ’s numeric registers.
⟨ Assignments 1217 ⟩+≡
case REGISTER:
case ADVANCE:
case MULTIPLY:
case DIVIDE:
do_register_command(a);
break;
Section 1236
We use the fact that REGISTER ADVANCE MULTIPLY DIVIDE.
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void do_register_command(small_number a) {
pointer l, q, r, s; // for list manipulation
int p; // type of register involved
l = null;
q = cur_cmd;
// << Compute the register location |l| and its type |p|; but |return| if invalid, 1237 >>
if (q == REGISTER) {
scan_optional_equals();
}
else {
scan_keyword("by"); // do nothing, optional "by"
}
arith_error = false;
if (q < MULTIPLY) {
// << Compute result of |REGISTER| or |ADVANCE|, put it in |cur_val|, 1238 >>
}
else {
// << Compute result of |MULTIPLY| or |DIVIDE|, put it in |cur_val|, 1240 >>
}
if (arith_error) {
print_err("Arithmetic overflow");
help2("I can't carry out that multiplication or division,")
("since the result is out of range.");
if (p >= GLUE_VAL) {
delete_glue_ref(cur_val);
}
error();
return;
}
if (p < GLUE_VAL) {
word_define(l, cur_val);
}
else {
trap_zero_glue();
define(l, GLUE_REF, cur_val);
}
}
Section 1237
Here we use the fact that the consecutive codes INT_VAL .. MU_VAL and ASSIGN_INT .. ASSIGN_MU_GLUE correspond to each other nicely.
⟨ Compute the register location l and its type p; but return if invalid 1237 ⟩≡
if (q != REGISTER) {
get_x_token();
if (cur_cmd >= ASSIGN_INT && cur_cmd <= ASSIGN_MU_GLUE) {
l = cur_chr;
p = cur_cmd - ASSIGN_INT;
goto found;
}
if (cur_cmd != REGISTER) {
print_err("You can't use `");
print_cmd_chr(cur_cmd, cur_chr);
print("' after ");
print_cmd_chr(q, 0);
help1("I'm forgetting what you said and not changing anything.");
error();
return;
}
}
p = cur_chr;
scan_eight_bit_int();
switch (p) {
case INT_VAL:
l = cur_val + COUNT_BASE;
break;
case DIMEN_VAL:
l = cur_val + SCALED_BASE;
break;
case GLUE_VAL:
l = cur_val + SKIP_BASE;
break;
case MU_VAL:
l = cur_val + MU_SKIP_BASE;
} // there are no other cases
found:
Section 1238
⟨ Compute result of REGISTER or ADVANCE, put it in cur_val 1238 ⟩≡
if (p < GLUE_VAL) {
if (p == INT_VAL) {
scan_int();
}
else {
scan_normal_dimen;
}
if (q == ADVANCE) {
cur_val += eqtb[l].integer;
}
}
else {
scan_glue(p);
if (q == ADVANCE) {
// << Compute the sum of two glue specs, 1239 >>
}
}
Section 1239
⟨ Compute the sum of two glue specs 1239 ⟩≡
q = new_spec(cur_val);
r = equiv(l);
delete_glue_ref(cur_val);
width(q) += width(r);
if (stretch(q) == 0) {
stretch_order(q) = NORMAL;
}
if (stretch_order(q) == stretch_order(r)) {
stretch(q) += stretch(r);
}
else if (stretch_order(q) < stretch_order(r) && stretch(r) != 0) {
stretch(q) = stretch(r);
stretch_order(q) = stretch_order(r);
}
if (shrink(q) == 0) {
shrink_order(q) = NORMAL;
}
if (shrink_order(q) == shrink_order(r)) {
shrink(q) += shrink(r);
}
else if (shrink_order(q) < shrink_order(r) && shrink(r) != 0) {
shrink(q) = shrink(r);
shrink_order(q) = shrink_order(r);
}
cur_val = q;
Section 1240
⟨ Compute result of MULTIPLY or DIVIDE, put it in cur_val 1240 ⟩≡
scan_int();
if (p < GLUE_VAL) {
if (q == MULTIPLY) {
if (p == INT_VAL) {
cur_val = mult_integers(eqtb[l].integer, cur_val);
}
else {
cur_val = nx_plus_y(eqtb[l].integer, cur_val, 0);
}
}
else {
cur_val = x_over_n(eqtb[l].integer, cur_val);
}
}
else {
s = equiv(l);
r = new_spec(s);
if (q == MULTIPLY) {
width(r) = nx_plus_y(width(s), cur_val, 0);
stretch(r) = nx_plus_y(stretch(s), cur_val, 0);
shrink(r) = nx_plus_y(shrink(s), cur_val, 0);
}
else {
width(r) = x_over_n(width(s), cur_val);
stretch(r) = x_over_n(stretch(s), cur_val);
shrink(r) = x_over_n(shrink(s), cur_val);
}
cur_val = r;
}
Section 1241
The processing of boxes is somewhat different, because we may need to scan and create an entire box before we actually change the value of the old one.
⟨ Assignments 1217 ⟩+≡
case SET_BOX:
scan_eight_bit_int();
if (global) {
n = 256 + cur_val;
}
else {
n = cur_val;
}
scan_optional_equals();
if (set_box_allowed) {
scan_box(BOX_FLAG + n);
}
else {
print_err("Improper ");
print_esc("setbox");
help2("Sorry, \\setbox is not allowed after \\halign in a display,")
("or between \\accent and an accented character.");
error();
}
break;
Section 1242
The space_factor or prev_depth settings are changed when a SET_AUX command is sensed. Similarly, prev_graf is changed in the presence of SET_PREV_GRAF, and dead_cycles or insert_penalties in the presence of SET_PAGE_INT. These definitions are always global.
When some dimension of a box register is changed, the change isn’t exactly global; but does not look at the \global
switch.
⟨ Assignments 1217 ⟩+≡
case SET_AUX:
alter_aux();
break;
case SET_PREV_GRAF:
alter_prev_graf();
break;
case SET_PAGE_DIMEN:
alter_page_so_far();
break;
case SET_PAGE_INT:
alter_integer();
break;
case SET_BOX_DIMEN:
alter_box_dimen();
break;
Section 1243
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void alter_aux() {
halfword c; // |HMODE| or |VMODE|
if (cur_chr != abs(mode)) {
report_illegal_case();
}
else {
c = cur_chr;
scan_optional_equals();
if (c == VMODE) {
scan_normal_dimen;
prev_depth = cur_val;
}
else {
scan_int();
if (cur_val <= 0 || cur_val > 32767) {
print_err("Bad space factor");
help1("I allow only values in the range 1..32767 here.");
int_error(cur_val);
}
else {
space_factor = cur_val;
}
}
}
}
Section 1244
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void alter_prev_graf() {
int p; // index into |nest|
nest[nest_ptr] = cur_list;
p = nest_ptr;
while (abs(nest[p].mode_field) != VMODE) {
decr(p);
}
scan_optional_equals();
scan_int();
if (cur_val < 0) {
print_err("Bad ");
print_esc("prevgraf");
help1("I allow only nonnegative values here.");
int_error(cur_val);
}
else {
nest[p].pg_field = cur_val;
cur_list = nest[nest_ptr];
}
}
Section 1245
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void alter_page_so_far() {
int c; // index into |page_so_far|
c = cur_chr;
scan_optional_equals();
scan_normal_dimen;
page_so_far[c] = cur_val;
}
Section 1246
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void alter_integer() {
int c; // 0 for \deadcycles, 1 for \insertpenalties
c = cur_chr;
scan_optional_equals();
scan_int();
if (c == 0) {
dead_cycles = cur_val;
}
else {
insert_penalties = cur_val;
}
}
Section 1247
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void alter_box_dimen() {
small_number c; // |WIDTH_OFFSET| or |HEIGHT_OFFSET| or |DEPTH_OFFSET|
eight_bits b; // box number
c = cur_chr;
scan_eight_bit_int();
b = cur_val;
scan_optional_equals();
scan_normal_dimen;
if (box(b) != null) {
mem[box(b) + c].sc = cur_val;
}
}
Section 1248
Paragraph shapes are set up in the obvious way.
⟨ Assignments 1217 ⟩+≡
case SET_SHAPE:
scan_optional_equals();
scan_int();
n = cur_val;
if (n <= 0) {
p = null;
}
else {
p = get_node(2*n + 1);
info(p) = n;
for(j = 1; j <= n; j++) {
scan_normal_dimen;
mem[p + 2*j - 1].sc = cur_val; // indentation
scan_normal_dimen;
mem[p + 2*j].sc = cur_val; // width
}
}
define(PAR_SHAPE_LOC, SHAPE_REF, p);
break;
Section 1249
Here’s something that isn’t quite so obvious. It guarantees that info(par_shape_ptr) can hold any positive n for which get_node(2*n + 1) doesn’t overflow the memory capacity.
⟨ Check the “constant” values for consistency 14 ⟩+≡
if (2*MAX_HALFWORD < MEM_TOP - MEM_MIN) {
bad = 41;
}
Section 1250
New hyphenation data is loaded by the HYPH_DATA command.
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("hyphenation", HYPH_DATA, 0);
primitive("patterns", HYPH_DATA, 1);
Section 1251
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case HYPH_DATA:
if (chr_code == 1) {
print_esc("patterns");
}
else {
print_esc("hyphenation");
}
break;
Section 1252
⟨ Assignments 1217 ⟩+≡
case HYPH_DATA:
if (cur_chr == 1) {
#ifdef INIT
new_patterns();
goto done;
#endif
print_err("Patterns can be loaded only by INITEX");
help0;
error();
do {
get_token();
} while (cur_cmd != RIGHT_BRACE); // flush the patterns
return;
}
else {
new_hyph_exceptions();
goto done;
}
Section 1253
All of ’s parameters are kept in eqtb except the font information, the interaction mode, and the hyphenation tables; these are strictly global.
⟨ Assignments 1217 ⟩+≡
case ASSIGN_FONT_DIMEN:
find_font_dimen(true);
k = cur_val;
scan_optional_equals();
scan_normal_dimen;
font_info[k].sc = cur_val;
break;
case ASSIGN_FONT_INT:
n = cur_chr;
scan_font_ident();
f = cur_val;
scan_optional_equals();
scan_int();
if (n == 0) {
hyphen_char[f] = cur_val;
}
else {
skew_char[f] = cur_val;
}
break;
Section 1254
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("hyphenchar", ASSIGN_FONT_INT, 0);
primitive("skewchar", ASSIGN_FONT_INT, 1);
Section 1255
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case ASSIGN_FONT_INT:
if (chr_code == 0) {
print_esc("hyphenchar");
}
else {
print_esc("skewchar");
}
break;
Section 1256
Here is where the information for a new font gets loaded.
⟨ Assignments 1217 ⟩+≡
case DEF_FONT:
new_font(a);
break;
Section 1257
String
"FONT"
must be added to the pool.
⟨ Read the other strings 51 ⟩+≡
put_string("FONT"); // FONT_STRING: 269
⟨ Internal strings numbers in the pool 51 ⟩+≡
#define FONT_STRING 269
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void new_font(small_number a) {
pointer u; // user's font identifier
scaled s; // stated "at" size, or negative of scaled magnification
int f; // runs through existing fonts
str_number t; // name for the frozen font identifier
int old_setting; // holds |selector| setting
str_number flushable_string; // string not yet referenced
if (job_name == 0) {
// avoid confusing `texput` with the font name
open_log_file();
}
get_r_token();
u = cur_cs;
if (u >= HASH_BASE) {
t = text(u);
}
else if (u >= SINGLE_BASE) {
if (u == NULL_CS) {
t = FONT_STRING; // "FONT"
}
else {
t = u - SINGLE_BASE;
}
}
else {
old_setting = selector;
selector = NEW_STRING;
print("FONT");
print_strnumber(u - ACTIVE_BASE);
selector = old_setting;
str_room(1);
t = make_string();
}
define(u, SET_FONT, NULL_FONT);
scan_optional_equals();
scan_file_name();
// << Scan the font size specification, 1258 >>
// << If this font has already been loaded, set |f| to the internal font number and |goto common_ending|, 1260 >>
f = read_font_info(u, cur_name, cur_area, s);
common_ending:
equiv(u) = f;
eqtb[FONT_ID_BASE + f] = eqtb[u];
font_id_text(f) = t;
}
Section 1258
⟨ Scan the font size specification 1258 ⟩≡
name_in_progress = true; // this keeps |cur_name| from being changed
if (scan_keyword("at")) {
// << Put the (positive) 'at' size into |s|, 1259 >>
}
else if (scan_keyword("scaled")) {
scan_int();
s = -cur_val;
if (cur_val <= 0 || cur_val > 32768) {
print_err("Illegal magnification has been changed to 1000");
help1("The magnification ratio must be between 1 and 32768.");
int_error(cur_val);
s = -1000;
}
}
else {
s = -1000;
}
name_in_progress = false;
Section 1259
⟨ Put the (positive) ‘at’ size into s 1259 ⟩≡
scan_normal_dimen;
s = cur_val;
if (s <= 0 || s >= 0x8000000) {
print_err("Improper `at' size (");
print_scaled(s);
print("pt), replaced by 10pt");
help2("I can only handle fonts at positive sizes that are")
("less than 2048pt, so I've changed what you said to 10pt.");
error();
s = 10*UNITY;
}
Section 1260
When the user gives a new identifier to a font that was previously loaded, the new name becomes the font identifier of record.
Font names ‘xyz
’ and ‘XYZ
’ are considered to be different.
⟨ If this font has already been loaded, set f to the internal font number and goto common_ending 1260 ⟩≡
flushable_string = str_ptr - 1;
for(f = FONT_BASE + 1; f <= font_ptr; f++) {
if (str_eq_str(font_name[f], cur_name)
&& str_eq_str(font_area[f], cur_area))
{
if (cur_name == flushable_string) {
flush_string;
cur_name = font_name[f];
}
if (s > 0) {
if (s == font_size[f]) {
goto common_ending;
}
}
else if (font_size[f] == xn_over_d(font_dsize[f], -s, 1000)) {
goto common_ending;
}
}
}
Section 1261
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case SET_FONT:
print("select font ");
slow_print(font_name[chr_code]);
if (font_size[chr_code] != font_dsize[chr_code]) {
print(" at ");
print_scaled(font_size[chr_code]);
print("pt");
}
break;
Section 1262
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("batchmode", SET_INTERACTION, BATCH_MODE);
primitive("nonstopmode", SET_INTERACTION, NONSTOP_MODE);
primitive("scrollmode", SET_INTERACTION, SCROLL_MODE);
primitive("errorstopmode", SET_INTERACTION, ERROR_STOP_MODE);
Section 1263
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case SET_INTERACTION:
switch (chr_code) {
case BATCH_MODE:
print_esc("batchmode");
break;
case NONSTOP_MODE:
print_esc("nonstopmode");
break;
case SCROLL_MODE:
print_esc("scrollmode");
break;
default:
print_esc("errorstopmode");
}
break;
Section 1264
⟨ Assignments 1217 ⟩+≡
case SET_INTERACTION:
new_interaction();
break;
Section 1265
⟨ Declare subprocedures for prefixed_command 1215 ⟩+≡
void new_interaction() {
print_ln();
interaction = cur_chr;
// << Initialize the print |selector| based on |interaction|, 75 >>
if (log_opened) {
selector += 2;
}
}
Section 1266
The \afterassignment
command puts a token into the global variable after_token.
This global variable is examined just after every assignment has been performed.
⟨ Global variables 13 ⟩+≡
halfword after_token; // zero, or a saved token
Section 1267
⟨ Set initial values of key variables 21 ⟩+≡
after_token = 0;
Section 1268
⟨ Cases of main_control that don’t depend on mode 1210 ⟩+≡
any_mode(AFTER_ASSIGNMENT):
get_token();
after_token = cur_tok;
break;
Section 1269
⟨ Insert a token saved by \afterassignment, if any 1269 ⟩≡
if (after_token != 0) {
cur_tok = after_token;
back_input();
after_token = 0;
}
Section 1270
Here is a procedure that might be called ‘Get the next non-blank non-relax non-call non-assignment token’.
void do_assignments() {
while(true) {
// << Get the next non-blank non-relax non-call token, 404 >>
if (cur_cmd <= MAX_NON_PREFIXED_COMMAND) {
return;
}
set_box_allowed = false;
prefixed_command();
set_box_allowed = true;
}
}
Section 1271
⟨ Cases of main_control that don’t depend on mode 1210 ⟩+≡
any_mode(AFTER_GROUP):
get_token();
save_for_after(cur_tok);
break;
Section 1272
Files for \read
are opened and closed by the IN_STREAM command.
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("openin", IN_STREAM, 1);
primitive("closein", IN_STREAM, 0);
Section 1273
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case IN_STREAM:
if (chr_code == 0) {
print_esc("closein");
}
else {
print_esc("openin");
}
break;
Section 1274
⟨ Cases of main_control that don’t depend on mode 1210 ⟩+≡
any_mode(IN_STREAM):
open_or_close_in();
break;
Section 1275
void open_or_close_in() {
int c; // 1 for \openin, 0 for \closein
int n; // stream number
c = cur_chr;
scan_four_bit_int();
n = cur_val;
if (read_open[n] != CLOSED) {
a_close(read_file[n]);
read_open[n] = CLOSED;
}
if (c != 0) {
scan_optional_equals();
scan_file_name();
if (cur_ext == EMPTY_STRING) {
cur_ext = TEX_EXT; // ".tex"
}
pack_cur_name;
if (a_open_in(&read_file[n])) {
read_open[n] = JUST_OPEN;
}
}
}
Section 1276
The user can issue messages to the terminal, regardless of the current mode.
⟨ Cases of main_control that don’t depend on mode 1210 ⟩+≡
any_mode(MESSAGE):
issue_message();
break;
Section 1277
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("message", MESSAGE, 0);
primitive("errmessage", MESSAGE, 1);
Section 1278
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case MESSAGE:
if (chr_code == 0) {
print_esc("message");
}
else {
print_esc("errmessage");
}
break;
Section 1279
void issue_message() {
int old_setting; // holds |selector| setting
int c; // identifies \message and \errmessage
str_number s; // the message
c = cur_chr;
link(GARBAGE) = scan_toks(false, true);
old_setting = selector;
selector = NEW_STRING;
token_show(def_ref);
selector = old_setting;
flush_list(def_ref);
str_room(1);
s = make_string();
if (c == 0) {
// << Print string |s| on the terminal, 1280 >>
}
else {
// << Print string |s| as an error message, 1283 >>
}
flush_string;
}
Section 1280
⟨ Print string s on the terminal 1280 ⟩≡
if (term_offset + length(s) > MAX_PRINT_LINE - 2) {
print_ln();
}
else if (term_offset > 0 || file_offset > 0 ) {
print_char(' ');
}
slow_print(s);
update_terminal;
Section 1281
If \errmessage
occurs often in SCROLL_MODE, without user-defined \errhelp
, we don’t want to give a long help message each time.
So we give a verbose explanation only once.
⟨ Global variables 13 ⟩+≡
bool long_help_seen; // has the long \errmessage help been used?
Section 1282
⟨ Set initial values of key variables 21 ⟩+≡
long_help_seen = false;
Section 1283
⟨ Print string s as an error message 1283 ⟩≡
print_err("");
slow_print(s);
if (err_help != null) {
use_err_help = true;
}
else if (long_help_seen) {
help1("(That was another \\errmessage.)");
}
else {
if (interaction < ERROR_STOP_MODE) {
long_help_seen = true;
}
help4("This error message was generated by an \\errmessage")
("command, so I can't give any explicit help.")
("Pretend that you're Hercule Poirot: Examine all clues,")
("and deduce the truth by order and method.");
}
error();
use_err_help = false;
Section 1284
The error routine calls on give_err_help if help is requested from the err_help parameter.
void give_err_help() {
token_show(err_help);
}
Section 1285
The \uppercase
and \lowercase
commands are implemented by building a token list and then changing the cases of the letters in it.
⟨ Cases of main_control that don’t depend on mode 1210 ⟩+≡
any_mode(CASE_SHIFT):
shift_case();
break;
Section 1286
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("lowercase", CASE_SHIFT, LC_CODE_BASE);
primitive("uppercase", CASE_SHIFT, UC_CODE_BASE);
Section 1287
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case CASE_SHIFT:
if (chr_code == LC_CODE_BASE) {
print_esc("lowercase");
}
else {
print_esc("uppercase");
}
break;
Section 1288
void shift_case() {
pointer b; // |lc_code_base| or |uc_code_base|
pointer p; // runs through the token list
halfword t; // token
eight_bits c; // character code
b = cur_chr;
p = scan_toks(false, false);
p = link(def_ref);
while (p != null) {
// << Change the case of the token in |p|, if a change is appropriate, 1289 >>
p = link(p);
}
back_list(link(def_ref));
free_avail(def_ref); // omit reference count
}
Section 1289
When the case of a chr_code changes, we don’t change the cmd. We also change active characters, using the fact that CS_TOKEN_FLAG + ACTIVE_BASE is a multiple of 256.
⟨ Change the case of the token in p, if a change is appropriate 1289 ⟩≡
t = info(p);
if (t < CS_TOKEN_FLAG + SINGLE_BASE) {
c = t % 256;
if (equiv(b + c) != 0) {
info(p) = t - c + equiv(b + c);
}
}
Section 1290
We come finally to the last pieces missing from main_control, namely the’\show
’ commands that are useful when debugging.
⟨ Cases of main_control that don’t depend on mode 1210 ⟩+≡
any_mode(XRAY):
show_whatever();
break;
Section 1291
#define SHOW_CODE 0 // \show
#define SHOW_BOX_CODE 1 // \showbox
#define SHOW_THE_CODE 2 // \showthe
#define SHOW_LISTS_CODE 3 // \showlists
⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡
primitive("show", XRAY, SHOW_CODE);
primitive("showbox", XRAY, SHOW_BOX_CODE);
primitive("showthe", XRAY, SHOW_THE_CODE);
primitive("showlists", XRAY, SHOW_LISTS_CODE);
Section 1292
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case XRAY:
switch (chr_code) {
case SHOW_BOX_CODE:
print_esc("showbox");
break;
case SHOW_THE_CODE:
print_esc("showthe");
break;
case SHOW_LISTS_CODE:
print_esc("showlists");
break;
default:
print_esc("show");
}
break;
Section 1293
void show_whatever() {
switch (cur_chr) {
case SHOW_LISTS_CODE:
begin_diagnostic();
show_activities();
break;
case SHOW_BOX_CODE:
// << Show the current contents of a box, 1296 >>
break;
case SHOW_CODE:
// << Show the current meaning of a token, then |goto common_ending|, 1294 >>
default:
// << Show the current value of some parameter or register, then |goto common_ending|, 1297 >>
}
// << Complete a potentially long \show command, 1298 >>
common_ending:
if (interaction < ERROR_STOP_MODE) {
help0;
decr(error_count);
}
else if (tracing_online > 0) {
help3("This isn't an error message; I'm just \\showing something.")
("Type `I\\show...' to show more (e.g., \\show\\cs,")
("\\showthe\\count10, \\showbox255, \\showlists).");
}
else {
help5("This isn't an error message; I'm just \\showing something.")
("Type `I\\show...' to show more (e.g., \\show\\cs,")
("\\showthe\\count10, \\showbox255, \\showlists).")
("And type `I\\tracingonline = 1\\show...' to show boxes and")
("lists on your terminal as well as in the transcript file.");
}
error();
}
Section 1294
⟨ Show the current meaning of a token, then goto common_ending 1294 ⟩≡
get_token();
print_nl("> ");
if (cur_cs != 0) {
sprint_cs(cur_cs);
print_char('=');
}
print_meaning();
goto common_ending;
Section 1295
⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡
case UNDEFINED_CS:
print("undefined");
break;
case CALL:
print("macro");
break;
case LONG_CALL:
print_esc("long macro");
break;
case OUTER_CALL:
print_esc("outer macro");
break;
case LONG_OUTER_CALL:
print_esc("long");
print_esc("outer macro");
break;
case END_TEMPLATE:
print_esc("outer endtemplate");
break;
Section 1296
⟨ Show the current contents of a box 1296 ⟩≡
scan_eight_bit_int();
begin_diagnostic();
print_nl("> \\box");
print_int(cur_val);
print_char('=');
if (box(cur_val) == null) {
print("void");
}
else {
show_box(box(cur_val));
}
Section 1297
Originally, we have
p = the_toks();
, but p is unused.
⟨ Show the current value of some parameter or register, then goto common_ending 1297 ⟩≡
the_toks();
print_nl("> ");
token_show(TEMP_HEAD);
flush_list(link(TEMP_HEAD));
goto common_ending;
Section 1298
⟨ Complete a potentially long \show command 1298 ⟩≡
end_diagnostic(true);
print_err("OK");
if (selector == TERM_AND_LOG && tracing_online <= 0) {
selector = TERM_ONLY;
print(" (see the transcript file)");
selector = TERM_AND_LOG;
}