Section 220: The table of equivalents

Now that we have studied the data structures for ’s semantic routines, we ought to consider the data structures used by its syntactic routines. In other words, our next concern will be the tables that looks at when it is scanning what the user has written.

The biggest and most important such table is called eqtb. It holds the current “equivalents” of things; i.e., it explains what things mean or what their current values are, for all quantities that are subject to the nesting structure provided by ’s grouping mechanism. There are six parts to eqtb:

  1. eqtb[ACTIVE_BASE .. (HASH_BASE − 1)] holds the current equivalents of single-character control sequences.

  2. eqtb[HASH_BASE .. (GLUE_BASE − 1)] holds the current equivalents of multiletter control sequences.

  3. eqtb[GLUE_BASE .. (LOCAL_BASE − 1)] holds the current equivalents of glue parameters like the current baselineskip.

  4. eqtb[LOCAL_BASE .. (INT_BASE − 1)] holds the current equivalents of local halfword quantities like the current box registers, the current “catcodes”, the current font, and a pointer to the current paragraph shape.

  5. eqtb[INT_BASE .. (DIMEN_BASE − 1)] holds the current equivalents of fullword integer parameters like the current hyphenation penalty.

  6. eqtb[DIMEN_BASE .. EQTB_SIZE] holds the current equivalents of fullword dimension parameters like the current hsize or amount of hanging indentation.

Note that, for example, the current amount of baselineskip glue is determined by the setting of a particular location in region 3 of eqtb, while the current meaning of the control sequence ‘\baselineskip’ (which might have been changed by \def or \let) appears in region 2.

Section 221

Each entry in eqtb is a memory_word. Most of these words are of type two_halves, and subdivided into three fields:

  1. The eq_level (a quarterword) is the level of grouping at which this equivalent was defined. If the level is LEVEL_ZERO, the equivalent has never been defined; LEVEL_ONE refers to the outer level (outside of all groups), and this level is also used for global definitions that never go away. Higher levels are for equivalents that will disappear at the end of their group.

  2. The eq_type (another quarterword) specifies what kind of entry this is. There are many types, since each primitive like \hbox, \def, etc., has its own special code. The list of command codes above includes all possible settings of the eq_type field.

  3. The equiv (a halfword) is the current equivalent value. This may be a font number, a pointer into mem, or a variety of other things.

constants.h
#define LEVEL_ZERO MIN_QUARTERWORD // level for undefined quantities
#define LEVEL_ONE (LEVEL_ZERO + 1) // outermost level for defined quantities
datastructures.h
#define eq_level_field(X) hh_b1((X))
#define eq_type_field(X)  hh_b0((X))
#define equiv_field(X)    hh_rh((X))

#define eq_level(X) eq_level_field(eqtb[(X)]) // level of definition
#define eq_type(X)  eq_type_field(eqtb[(X)])  // command code for equivalent
#define equiv(X)    equiv_field(eqtb[(X)])    // equivalent value

Section 222

Many locations in eqtb have symbolic names. The purpose of the next paragraphs is to define these names, and to set up the initial values of the equivalents.

In the first region we have 256 equivalents for “active characters” that act as control sequences, followed by 256 equivalents for single-character control sequences.

Then comes region 2, which corresponds to the hash table that we will define later. The maximum address in this region is used for a dummy control sequence that is perpetually undefined. There also are several locations for control sequences that are perpetually defined (since they are used in error recovery).

constants.h
#define ACTIVE_BASE                1                                // beginning of region 1, for active character equivalents
#define SINGLE_BASE                (ACTIVE_BASE + 256)              // equivalents of one-character control sequences
#define NULL_CS                    (SINGLE_BASE + 256)              // equivalent of \csname\endcsname
#define HASH_BASE                  (NULL_CS + 1)                    // beginning of region 2, for the hash table
#define FROZEN_CONTROL_SEQUENCE    (HASH_BASE + HASH_SIZE)          // for error recovery
#define FROZEN_PROTECTION          FROZEN_CONTROL_SEQUENCE          // inaccessible but definable
#define FROZEN_CR                  (FROZEN_CONTROL_SEQUENCE + 1)    // permanent `\cr'
#define FROZEN_END_GROUP           (FROZEN_CONTROL_SEQUENCE + 2)    // permanent `\endgroup'
#define FROZEN_RIGHT               (FROZEN_CONTROL_SEQUENCE + 3)    // permanent `\right'
#define FROZEN_FI                  (FROZEN_CONTROL_SEQUENCE + 4)    // permanent `\fi'
#define FROZEN_END_TEMPLATE        (FROZEN_CONTROL_SEQUENCE + 5)    // permanent `\endtemplate'
#define FROZEN_ENDV                (FROZEN_CONTROL_SEQUENCE + 6)    // second permanent `\endtemplate'
#define FROZEN_RELAX               (FROZEN_CONTROL_SEQUENCE + 7)    // permanent `\relax'
#define END_WRITE                  (FROZEN_CONTROL_SEQUENCE + 8)    // permanent `\endwrite'
#define FROZEN_DONT_EXPAND         (FROZEN_CONTROL_SEQUENCE + 9)    // permanent `\notexpanded:'
#define FROZEN_NULL_FONT           (FROZEN_CONTROL_SEQUENCE + 10)   // permanent `\nullfont'
#define FONT_ID_BASE               (FROZEN_NULL_FONT - FONT_BASE)   // begins table of 257 permanent font identifiers
#define UNDEFINED_CONTROL_SEQUENCE (FROZEN_NULL_FONT + 257)         // dummy location
#define GLUE_BASE                  (UNDEFINED_CONTROL_SEQUENCE + 1) // beginning of region 3

⟨ Initialize table entries (done by INITEX only) 164 ⟩+≡

eq_type(UNDEFINED_CONTROL_SEQUENCE) = UNDEFINED_CS;
equiv(UNDEFINED_CONTROL_SEQUENCE) = null;
eq_level(UNDEFINED_CONTROL_SEQUENCE) = LEVEL_ZERO;
for(k = ACTIVE_BASE; k < UNDEFINED_CONTROL_SEQUENCE; k++) {
    eqtb[k] = eqtb[UNDEFINED_CONTROL_SEQUENCE];
}

Section 223

Here is a routine that displays the current meaning of an eqtb entry in region 1 or 2. (Similar routines for the other regions will appear below.)

⟨ Show equivalent n, in region 1 or 2 223 ⟩≡

sprint_cs(n);
print_char('=');
print_cmd_chr(eq_type(n), equiv(n));
if (eq_type(n) >= CALL) {
    print_char(':');
    show_token_list(link(equiv(n)), null, 32);
}

Section 224

Region 3 of eqtb contains the 256 \skip registers, as well as the glue parameters defined here. It is important that the “muskip” parameters have larger numbers than the others.

NOTE

The ⟨ Current mem equivalent of glue parameter number n 224 ⟩ is directly included in sections 152 and 154.

constants.h
#define LINE_SKIP_CODE                0      // interline glue if |baseline_skip| is infeasible
#define BASELINE_SKIP_CODE            1      // desired glue between baselines
#define PAR_SKIP_CODE                 2      // extra glue just above a paragraph
#define ABOVE_DISPLAY_SKIP_CODE       3      // extra glue just above displayed math
#define BELOW_DISPLAY_SKIP_CODE       4      // extra glue just below displayed math
#define ABOVE_DISPLAY_SHORT_SKIP_CODE 5      // glue above displayed math following short lines
#define BELOW_DISPLAY_SHORT_SKIP_CODE 6      // glue below displayed math following short lines
#define LEFT_SKIP_CODE                7      // glue at left of justified lines
#define RIGHT_SKIP_CODE               8      // glue at right of justified lines
#define TOP_SKIP_CODE                 9      // glue at top of main pages
#define SPLIT_TOP_SKIP_CODE           10     // glue at top of split pages
#define TAB_SKIP_CODE                 11     // glue between aligned entries
#define SPACE_SKIP_CODE               12     // glue between words (if not |ZERO_GLUE|)
#define XSPACE_SKIP_CODE              13     // glue after sentences (if not |ZERO_GLUE|)
#define PAR_FILL_SKIP_CODE            14     // glue on last line of paragraph
#define THIN_MU_SKIP_CODE             15     // thin space in math formula
#define MED_MU_SKIP_CODE              16     // medium space in math formula
#define THICK_MU_SKIP_CODE            17     // thick space in math formula
#define GLUE_PARS                     18     // total number of glue parameters
#define SKIP_BASE    (GLUE_BASE + GLUE_PARS) // table of 256 "skip" registers
#define MU_SKIP_BASE (SKIP_BASE + 256)       // table of 256 "muskip" registers
#define LOCAL_BASE   (MU_SKIP_BASE + 256)    // beginning of region 4
datastructures.h
#define skip(X)     equiv(SKIP_BASE + (X))    // |mem| location of glue specification
#define mu_skip(X)  equiv(MU_SKIP_BASE + (X)) // |mem| location of math glue spec
#define glue_par(X) equiv(GLUE_BASE + (X))    // |mem| location of glue specification

#define line_skip                glue_par(LINE_SKIP_CODE)
#define baseline_skip            glue_par(BASELINE_SKIP_CODE)
#define par_skip                 glue_par(PAR_SKIP_CODE)
#define above_display_skip       glue_par(ABOVE_DISPLAY_SKIP_CODE)
#define below_display_skip       glue_par(BELOW_DISPLAY_SKIP_CODE)
#define above_display_short_skip glue_par(ABOVE_DISPLAY_SHORT_SKIP_CODE)
#define below_display_short_skip glue_par(BELOW_DISPLAY_SHORT_SKIP_CODE)
#define left_skip                glue_par(LEFT_SKIP_CODE)
#define right_skip               glue_par(RIGHT_SKIP_CODE)
#define top_skip                 glue_par(TOP_SKIP_CODE)
#define split_top_skip           glue_par(SPLIT_TOP_SKIP_CODE)
#define tab_skip                 glue_par(TAB_SKIP_CODE)
#define space_skip               glue_par(SPACE_SKIP_CODE)
#define xspace_skip              glue_par(XSPACE_SKIP_CODE)
#define par_fill_skip            glue_par(PAR_FILL_SKIP_CODE)
#define thin_mu_skip             glue_par(THIN_MU_SKIP_CODE)
#define med_mu_skip              glue_par(MED_MU_SKIP_CODE)
#define thick_mu_skip            glue_par(THICK_MU_SKIP_CODE)

Section 225

Sometimes we need to convert ’s internal code numbers into symbolic form. The print_skip_param routine gives the symbolic name of a glue parameter.

other_printing.c
void print_skip_param(int n) {
    switch(n) {
    case LINE_SKIP_CODE:
        print_esc("lineskip");
        break;
    
    case BASELINE_SKIP_CODE:
        print_esc("baselineskip");
        break;
    
    case PAR_SKIP_CODE:
        print_esc("parskip");
        break;
    
    case ABOVE_DISPLAY_SKIP_CODE:
        print_esc("abovedisplayskip");
        break;
    
    case BELOW_DISPLAY_SKIP_CODE:
        print_esc("belowdisplayskip");
        break;
    
    case ABOVE_DISPLAY_SHORT_SKIP_CODE:
        print_esc("abovedisplayshortskip");
        break;
    
    case BELOW_DISPLAY_SHORT_SKIP_CODE:
        print_esc("belowdisplayshortskip");
        break;
    
    case LEFT_SKIP_CODE:
        print_esc("leftskip");
        break;
    
    case RIGHT_SKIP_CODE:
        print_esc("rightskip");
        break;
    
    case TOP_SKIP_CODE:
        print_esc("topskip");
        break;
    
    case SPLIT_TOP_SKIP_CODE:
        print_esc("splittopskip");
        break;
    
    case TAB_SKIP_CODE:
        print_esc("tabskip");
        break;
    
    case SPACE_SKIP_CODE:
        print_esc("spaceskip");
        break;
    
    case XSPACE_SKIP_CODE:
        print_esc("xspaceskip");
        break;
    
    case PAR_FILL_SKIP_CODE:
        print_esc("parfillskip");
        break;
    
    case THIN_MU_SKIP_CODE:
        print_esc("thinmuskip");
        break;
    
    case MED_MU_SKIP_CODE:
        print_esc("medmuskip");
        break;
    
    case THICK_MU_SKIP_CODE:
        print_esc("thickmuskip");
        break;
    
    default:
        print("[unknown glue parameter!]");
    }
}

Section 226

The symbolic names for glue parameters are put into ’s hash table by using the routine called primitive, defined below. Let us enter them now, so that we don’t have to list all those parameter names anywhere else.

⟨ Put each of TeX’s primitives into the hash table 226 ⟩≡

primitive("lineskip", ASSIGN_GLUE, GLUE_BASE + LINE_SKIP_CODE);
primitive("baselineskip", ASSIGN_GLUE, GLUE_BASE + BASELINE_SKIP_CODE);
primitive("parskip", ASSIGN_GLUE, GLUE_BASE + PAR_SKIP_CODE);
primitive("abovedisplayskip", ASSIGN_GLUE, GLUE_BASE + ABOVE_DISPLAY_SKIP_CODE);
primitive("belowdisplayskip", ASSIGN_GLUE, GLUE_BASE + BELOW_DISPLAY_SKIP_CODE);
primitive("abovedisplayshortskip",  ASSIGN_GLUE, GLUE_BASE + ABOVE_DISPLAY_SHORT_SKIP_CODE);
primitive("belowdisplayshortskip",  ASSIGN_GLUE, GLUE_BASE + BELOW_DISPLAY_SHORT_SKIP_CODE);
primitive("leftskip", ASSIGN_GLUE, GLUE_BASE + LEFT_SKIP_CODE);
primitive("rightskip", ASSIGN_GLUE, GLUE_BASE + RIGHT_SKIP_CODE);
primitive("topskip", ASSIGN_GLUE, GLUE_BASE + TOP_SKIP_CODE);
primitive("splittopskip", ASSIGN_GLUE, GLUE_BASE + SPLIT_TOP_SKIP_CODE);
primitive("tabskip", ASSIGN_GLUE, GLUE_BASE + TAB_SKIP_CODE);
primitive("spaceskip", ASSIGN_GLUE, GLUE_BASE + SPACE_SKIP_CODE);
primitive("xspaceskip", ASSIGN_GLUE, GLUE_BASE + XSPACE_SKIP_CODE);
primitive("parfillskip", ASSIGN_GLUE, GLUE_BASE + PAR_FILL_SKIP_CODE);
primitive("thinmuskip", ASSIGN_MU_GLUE, GLUE_BASE + THIN_MU_SKIP_CODE);
primitive("medmuskip", ASSIGN_MU_GLUE, GLUE_BASE + MED_MU_SKIP_CODE);
primitive("thickmuskip", ASSIGN_MU_GLUE, GLUE_BASE + THICK_MU_SKIP_CODE);

Section 227

⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩≡

case ASSIGN_GLUE:
case ASSIGN_MU_GLUE:
    if (chr_code < SKIP_BASE) {
        print_skip_param(chr_code - GLUE_BASE);
    }
    else if (chr_code < MU_SKIP_BASE) {
        print_esc("skip");
        print_int(chr_code - SKIP_BASE);
    }
    else {
        print_esc("muskip");
        print_int(chr_code - MU_SKIP_BASE);
    }
    break;

Section 228

All glue parameters and registers are initially ‘0pt plus0pt minus0pt’.

⟨ Initialize table entries (done by INITEX only) 164 ⟩+≡

equiv(GLUE_BASE) = ZERO_GLUE;
eq_level(GLUE_BASE) = LEVEL_ONE;
eq_type(GLUE_BASE) = GLUE_REF;
for(k = GLUE_BASE + 1; k < LOCAL_BASE; k++) {
    eqtb[k] = eqtb[GLUE_BASE];
}
glue_ref_count(ZERO_GLUE) += LOCAL_BASE - GLUE_BASE;

Section 229

⟨ Show equivalent n, in region 3 229 ⟩≡

if (n < SKIP_BASE) {
    print_skip_param(n - GLUE_BASE);
    print_char('=');
    if (n < GLUE_BASE + THIN_MU_SKIP_CODE) {
        print_spec(equiv(n), "pt");
    }
    else {
        print_spec(equiv(n), "mu");
    }
} 
else if (n < MU_SKIP_BASE) {
  print_esc("skip");
  print_int(n - SKIP_BASE);
  print_char('=');
  print_spec(equiv(n), "pt");
}
else {
    print_esc("muskip");
    print_int(n - MU_SKIP_BASE);
    print_char('=');
    print_spec(equiv(n), "mu");
}

Section 230

Region 4 of eqtb contains the local quantities defined here. The bulk of this region is taken up by five tables that are indexed by eight-bit characters; these tables are important to both the syntactic and semantic portions of . There are also a bunch of special things like font and token parameters, as well as the tables of \toks and \box registers.

constants.h
#define PAR_SHAPE_LOC      LOCAL_BASE             // specifies paragraph shape
#define OUTPUT_ROUTINE_LOC (LOCAL_BASE + 1)       // points to token list for \output
#define EVERY_PAR_LOC      (LOCAL_BASE + 2)       // points to token list for \everypar
#define EVERY_MATH_LOC     (LOCAL_BASE + 3)       // points to token list for \everymath
#define EVERY_DISPLAY_LOC  (LOCAL_BASE + 4)       // points to token list for \everydisplay
#define EVERY_HBOX_LOC     (LOCAL_BASE + 5)       // points to token list for \everyhbox
#define EVERY_VBOX_LOC     (LOCAL_BASE + 6)       // points to token list for \everyvbox
#define EVERY_JOB_LOC      (LOCAL_BASE + 7)       // points to token list for \everyjob
#define EVERY_CR_LOC       (LOCAL_BASE + 8)       // points to token list for \everycr
#define ERR_HELP_LOC       (LOCAL_BASE + 9)       // points to token list for \errhelp
#define TOKS_BASE          (LOCAL_BASE + 10)      // table of 256 token list registers
#define BOX_BASE           (TOKS_BASE + 256)      // table of 256 box registers
#define CUR_FONT_LOC       (BOX_BASE + 256)       // internal font number outside math mode
#define MATH_FONT_BASE     (CUR_FONT_LOC + 1)     // table of 48 math font numbers
#define CAT_CODE_BASE      (MATH_FONT_BASE + 48)  // table of 256 command codes (the "catcodes")
#define LC_CODE_BASE       (CAT_CODE_BASE + 256)  // table of 256 lowercase mappings
#define UC_CODE_BASE       (LC_CODE_BASE + 256)   // table of 256 uppercase mappings
#define SF_CODE_BASE       (UC_CODE_BASE + 256)   // table of 256 spacefactor mappings
#define MATH_CODE_BASE     (SF_CODE_BASE + 256)   // table of 256 math mode mappings
#define INT_BASE           (MATH_CODE_BASE + 256) // beginning of region 5
datastructures.h
#define par_shape_ptr  equiv(PAR_SHAPE_LOC)
#define output_routine equiv(OUTPUT_ROUTINE_LOC)
#define every_par      equiv(EVERY_PAR_LOC)
#define every_math     equiv(EVERY_MATH_LOC)
#define every_display  equiv(EVERY_DISPLAY_LOC)
#define every_hbox     equiv(EVERY_HBOX_LOC)
#define every_vbox     equiv(EVERY_VBOX_LOC)
#define every_job      equiv(EVERY_JOB_LOC)
#define every_cr       equiv(EVERY_CR_LOC)
#define err_help       equiv(ERR_HELP_LOC)
#define toks(X)        equiv(TOKS_BASE + (X))
#define box(X)         equiv(BOX_BASE + (X))
#define cur_font       equiv(CUR_FONT_LOC)
#define fam_fnt(X)     equiv(MATH_FONT_BASE + (X))
#define cat_code(X)    equiv(CAT_CODE_BASE + (X))
#define lc_code(X)     equiv(LC_CODE_BASE + (X))
#define uc_code(X)     equiv(UC_CODE_BASE + (X))
#define sf_code(X)     equiv(SF_CODE_BASE + (X))
// Note: |math_code(c)| is the true math code plus |MIN_HALFWORD|
#define math_code(X)   equiv(MATH_CODE_BASE + (X))

⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡

primitive("output", ASSIGN_TOKS, OUTPUT_ROUTINE_LOC);
primitive("everypar", ASSIGN_TOKS, EVERY_PAR_LOC);
primitive("everymath", ASSIGN_TOKS, EVERY_MATH_LOC);
primitive("everydisplay", ASSIGN_TOKS, EVERY_DISPLAY_LOC);
primitive("everyhbox", ASSIGN_TOKS, EVERY_HBOX_LOC);
primitive("everyvbox", ASSIGN_TOKS, EVERY_VBOX_LOC);
primitive("everyjob", ASSIGN_TOKS, EVERY_JOB_LOC);
primitive("everycr", ASSIGN_TOKS, EVERY_CR_LOC);
primitive("errhelp", ASSIGN_TOKS, ERR_HELP_LOC);

Section 231

⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡

case ASSIGN_TOKS:
    if (chr_code >= TOKS_BASE) {
        print_esc("toks");
        print_int(chr_code - TOKS_BASE);
    }
    else {
        switch(chr_code) {
        case OUTPUT_ROUTINE_LOC:
            print_esc("output");
            break;
        
        case EVERY_PAR_LOC:
            print_esc("everypar");
            break;
        
        case EVERY_MATH_LOC:
            print_esc("everymath");
            break;
        
        case EVERY_DISPLAY_LOC:
            print_esc("everydisplay");
            break;
        
        case EVERY_HBOX_LOC:
            print_esc("everyhbox");
            break;
        
        case EVERY_VBOX_LOC:
            print_esc("everyvbox");
            break;
        
        case EVERY_JOB_LOC:
            print_esc("everyjob");
            break;
        
        case EVERY_CR_LOC:
            print_esc("everycr");
            break;
        
        default:
            print_esc("errhelp");
        }
    }
    break;

Section 232

We initialize most things to null or undefined values. An undefined font is represented by the internal code FONT_BASE.

However, the character code tables are given initial values based on the conventional interpretation of ASCII code. These initial values should not be changed when is adapted for use with non-English languages; all changes to the initialization conventions should be made in format packages, not in itself, so that global interchange of formats is possible.

constants.h
#define NULL_FONT FONT_BASE
#define VAR_CODE 0x7000 // math code meaning "use the current family"

⟨ Initialize table entries (done by INITEX only) 164 ⟩+≡

par_shape_ptr = null;
eq_type(PAR_SHAPE_LOC) = SHAPE_REF;
eq_level(PAR_SHAPE_LOC) = LEVEL_ONE;
for(k = OUTPUT_ROUTINE_LOC; k <= TOKS_BASE + 255; k++) {
    eqtb[k] = eqtb[UNDEFINED_CONTROL_SEQUENCE];
}
box(0) = null;
eq_type(BOX_BASE) = BOX_REF;
eq_level(BOX_BASE) = LEVEL_ONE;
for(k = BOX_BASE + 1; k <= BOX_BASE + 255; k++) {
    eqtb[k] = eqtb[BOX_BASE];
}
cur_font = NULL_FONT;
eq_type(CUR_FONT_LOC) = DATA;
eq_level(CUR_FONT_LOC) = LEVEL_ONE;
for(k = MATH_FONT_BASE; k <= MATH_FONT_BASE + 47; k++) {
    eqtb[k] = eqtb[CUR_FONT_LOC];
}
equiv(CAT_CODE_BASE) = 0;
eq_type(CAT_CODE_BASE) = DATA;
eq_level(CAT_CODE_BASE) = LEVEL_ONE;
for(k = CAT_CODE_BASE + 1; k <= INT_BASE - 1; k++) {
    eqtb[k] = eqtb[CAT_CODE_BASE];
}
for(k = 0; k <= 255; k++) {
    cat_code(k) = OTHER_CHAR;
    math_code(k) = hi(k);
    sf_code(k) = 1000;
}
cat_code(CARRIAGE_RETURN) = CAR_RET;
cat_code(' ') = SPACER;
cat_code('\\') = ESCAPE;
cat_code('%') = COMMENT;
cat_code(INVALID_CODE) = INVALID_CHAR;
cat_code(NULL_CODE) = IGNORE;
for(k = '0'; k <= '9'; k++) {
    math_code(k) = hi(k + VAR_CODE);
}
for(k = 'A'; k <= 'Z'; k++) {
    cat_code(k) = LETTER;
    cat_code(k + 'a' - 'A') = LETTER;
    math_code(k) = hi(k + VAR_CODE + 0x100);
    math_code(k + 'a' - 'A') = hi(k + 'a' - 'A' + VAR_CODE + 0x100);
    lc_code(k) = k + 'a' - 'A';
    lc_code(k + 'a' - 'A') = k + 'a' - 'A';
    uc_code(k) = k;
    uc_code(k + 'a' - 'A') = k;
    sf_code(k) = 999;
}

Section 233

⟨ Show equivalent n, in region 4 233 ⟩≡

if (n == PAR_SHAPE_LOC) {
    print_esc("parshape");
    print_char('=');
    if (par_shape_ptr == null) {
        print_char('0');
    }
    else {
        print_int(info(par_shape_ptr));
    }
}
else if (n < TOKS_BASE) {
  print_cmd_chr(ASSIGN_TOKS, n);
  print_char('=');
  if (equiv(n) != null) {
    show_token_list(link(equiv(n)), null, 32);
  }
}
else if (n < BOX_BASE) {
  print_esc("toks");
  print_int(n - TOKS_BASE);
  print_char('=');
  if (equiv(n) != null) {
    show_token_list(link(equiv(n)), null, 32);
  }
}
else if (n < CUR_FONT_LOC) {
  print_esc("box");
  print_int(n - BOX_BASE);
  print_char('=');
  if (equiv(n) == null) {
    print("void");
  }
  else {
    depth_threshold = 0;
    breadth_max = 1;
    show_node_list(equiv(n));
  }
}
else if (n < CAT_CODE_BASE) {
    // << Show the font identifier in |eqtb[n]|, 234 >>
}
else {
    // << Show the halfword code in |eqtb[n]|, 235 >>
}

Section 234

⟨ Show the font identifier in eqtb[n] 234 ⟩≡

if (n == CUR_FONT_LOC) {
    print("current font");
}
else if (n < MATH_FONT_BASE + 16) {
    print_esc("textfont");
    print_int(n - MATH_FONT_BASE);
}
else if (n < MATH_FONT_BASE + 32) {
    print_esc("scriptfont");
    print_int(n - MATH_FONT_BASE - 16);
}
else  {
    print_esc("scriptscriptfont");
    print_int(n - MATH_FONT_BASE - 32);
}
print_char('=');
print_esc_strnumber(hh_rh(hash[FONT_ID_BASE + equiv(n)])); // that's |font_id_text(equiv(n))|

Section 235

⟨ Show the halfword code in eqtb[n] 235 ⟩≡

if (n < MATH_CODE_BASE) {
  if (n < LC_CODE_BASE) {
    print_esc("catcode");
    print_int(n - CAT_CODE_BASE);
  }
  else if (n < UC_CODE_BASE) {
    print_esc("lccode");
    print_int(n - LC_CODE_BASE);
  }
  else if (n < SF_CODE_BASE) {
    print_esc("uccode");
    print_int(n - UC_CODE_BASE);
  }
  else {
    print_esc("sfcode");
    print_int(n - SF_CODE_BASE);
  }
  print_char('=');
  print_int(equiv(n));
}
else {
    print_esc("mathcode");
    print_int(n - MATH_CODE_BASE);
    print_char('=');
    print_int(ho(equiv(n)));
}

Section 236

Region 5 of eqtb contains the integer parameters and registers defined here, as well as the DEL_CODE table. The latter table differs from the CAT_CODE .. MATH_CODE tables that precede it, since delimiter codes are fullword integers while the other kinds of codes occupy at most a halfword. This is what makes region 5 different from region 4. We will store the eq_level information in an auxiliary array of quarterwords that will be defined later.

NOTE

time is defined as time_ to avoid conflict with time from file time.h used to get local time.

constants.h
#define PRETOLERANCE_CODE           0        // badness tolerance before hyphenation
#define TOLERANCE_CODE              1        // badness tolerance after hyphenation
#define LINE_PENALTY_CODE           2        // added to the badness of every line
#define HYPHEN_PENALTY_CODE         3        // penalty for break after discretionary hyphen
#define EX_HYPHEN_PENALTY_CODE      4        // penalty for break after explicit hyphen
#define CLUB_PENALTY_CODE           5        // penalty for creating a club line
#define WIDOW_PENALTY_CODE          6        // penalty for creating a widow line
#define DISPLAY_WIDOW_PENALTY_CODE  7        // ditto, just before a display
#define BROKEN_PENALTY_CODE         8        // penalty for breaking a page at a broken line
#define BIN_OP_PENALTY_CODE         9        // penalty for breaking after a binary operation
#define REL_PENALTY_CODE            10       // penalty for breaking after a relation
#define PRE_DISPLAY_PENALTY_CODE    11       // penalty for breaking just before a displayed formula
#define POST_DISPLAY_PENALTY_CODE   12       // penalty for breaking just after a displayed formula
#define INTER_LINE_PENALTY_CODE     13       // additional penalty between lines
#define DOUBLE_HYPHEN_DEMERITS_CODE 14       // demerits for double hyphen break
#define FINAL_HYPHEN_DEMERITS_CODE  15       // demerits for final hyphen break
#define ADJ_DEMERITS_CODE           16       // demerits for adjacent incompatible lines
#define MAG_CODE                    17       // magnification ratio
#define DELIMITER_FACTOR_CODE       18       // ratio for variable-size delimiters
#define LOOSENESS_CODE              19       // change in number of lines for a paragraph
#define TIME_CODE                   20       // current time of day
#define DAY_CODE                    21       // current day of the month
#define MONTH_CODE                  22       // current month of the year
#define YEAR_CODE                   23       // current year of our Lord
#define SHOW_BOX_BREADTH_CODE       24       // nodes per level in |show_box|
#define SHOW_BOX_DEPTH_CODE         25       // maximum level in |show_box|
#define HBADNESS_CODE               26       // hboxes exceeding this badness will be shown by |hpack|
#define VBADNESS_CODE               27       // vboxes exceeding this badness will be shown by |vpack|
#define PAUSING_CODE                28       // pause after each line is read from a file
#define TRACING_ONLINE_CODE         29       // show diagnostic output on terminal
#define TRACING_MACROS_CODE         30       // show macros as they are being expanded
#define TRACING_STATS_CODE          31       // show memory usage if TeX knows it
#define TRACING_PARAGRAPHS_CODE     32       // show line-break calculations
#define TRACING_PAGES_CODE          33       // show page-break calculations
#define TRACING_OUTPUT_CODE         34       // show boxes when they are shipped out
#define TRACING_LOST_CHARS_CODE     35       // show characters that aren't in the font
#define TRACING_COMMANDS_CODE       36       // show command codes at |big_switch|
#define TRACING_RESTORES_CODE       37       // show equivalents when they are restored
#define UC_HYPH_CODE                38       // hyphenate words beginning with a capital letter
#define OUTPUT_PENALTY_CODE         39       // penalty found at current page break
#define MAX_DEAD_CYCLES_CODE        40       // bound on consecutive dead cycles of output
#define HANG_AFTER_CODE             41       // hanging indentation changes after this many lines
#define FLOATING_PENALTY_CODE       42       // penalty for insertions held over after a split
#define GLOBAL_DEFS_CODE            43       // override \global specifications
#define CUR_FAM_CODE                44       // current family
#define ESCAPE_CHAR_CODE            45       // escape character for token output
#define DEFAULT_HYPHEN_CHAR_CODE    46       // value of \hyphenchar when a font is loaded
#define DEFAULT_SKEW_CHAR_CODE      47       // value of \skewchar when a font is loaded
#define END_LINE_CHAR_CODE          48       // character placed at the right end of the buffer
#define NEW_LINE_CHAR_CODE          49       // character that prints as |print_ln|
#define LANGUAGE_CODE               50       // current hyphenation table
#define LEFT_HYPHEN_MIN_CODE        51       // minimum left hyphenation fragment size
#define RIGHT_HYPHEN_MIN_CODE       52       // minimum right hyphenation fragment size
#define HOLDING_INSERTS_CODE        53       // do not remove insertion nodes from \box255
#define ERROR_CONTEXT_LINES_CODE    54       // maximum intermediate line pairs shown
#define INT_PARS                    55       // total number of integer parameters
#define COUNT_BASE    (INT_BASE + INT_PARS)  // 256 user \count registers
#define DEL_CODE_BASE (COUNT_BASE + 256)     // 256 delimiter code mappings
#define DIMEN_BASE    (DEL_CODE_BASE + 256)  // beginning of region 6
datastructures.h
#define del_code(X)              eqtb[DEL_CODE_BASE + (X)].integer
#define count(X)                 eqtb[COUNT_BASE + (X)].integer
#define int_par(X)               eqtb[INT_BASE + (X)].integer // an integer parameter
#define pretolerance             int_par(PRETOLERANCE_CODE)
#define tolerance                int_par(TOLERANCE_CODE)
#define line_penalty             int_par(LINE_PENALTY_CODE)
#define hyphen_penalty           int_par(HYPHEN_PENALTY_CODE)
#define ex_hyphen_penalty        int_par(EX_HYPHEN_PENALTY_CODE)
#define club_penalty             int_par(CLUB_PENALTY_CODE)
#define widow_penalty            int_par(WIDOW_PENALTY_CODE)
#define display_widow_penalty    int_par(DISPLAY_WIDOW_PENALTY_CODE)
#define broken_penalty           int_par(BROKEN_PENALTY_CODE)
#define bin_op_penalty           int_par(BIN_OP_PENALTY_CODE)
#define rel_penalty              int_par(REL_PENALTY_CODE)
#define pre_display_penalty      int_par(PRE_DISPLAY_PENALTY_CODE)
#define post_display_penalty     int_par(POST_DISPLAY_PENALTY_CODE)
#define inter_line_penalty       int_par(INTER_LINE_PENALTY_CODE)
#define double_hyphen_demerits   int_par(DOUBLE_HYPHEN_DEMERITS_CODE)
#define final_hyphen_demerits    int_par(FINAL_HYPHEN_DEMERITS_CODE)
#define adj_demerits             int_par(ADJ_DEMERITS_CODE)
#define mag                      int_par(MAG_CODE)
#define delimiter_factor         int_par(DELIMITER_FACTOR_CODE)
#define looseness                int_par(LOOSENESS_CODE)
#define time_                    int_par(TIME_CODE)
#define day                      int_par(DAY_CODE)
#define month                    int_par(MONTH_CODE)
#define year                     int_par(YEAR_CODE)
#define show_box_breadth         int_par(SHOW_BOX_BREADTH_CODE)
#define show_box_depth           int_par(SHOW_BOX_DEPTH_CODE)
#define hbadness                 int_par(HBADNESS_CODE)
#define vbadness                 int_par(VBADNESS_CODE)
#define pausing                  int_par(PAUSING_CODE)
#define tracing_online           int_par(TRACING_ONLINE_CODE)
#define tracing_macros           int_par(TRACING_MACROS_CODE)
#define tracing_stats            int_par(TRACING_STATS_CODE)
#define tracing_paragraphs       int_par(TRACING_PARAGRAPHS_CODE)
#define tracing_pages            int_par(TRACING_PAGES_CODE)
#define tracing_output           int_par(TRACING_OUTPUT_CODE)
#define tracing_lost_chars       int_par(TRACING_LOST_CHARS_CODE)
#define tracing_commands         int_par(TRACING_COMMANDS_CODE)
#define tracing_restores         int_par(TRACING_RESTORES_CODE)
#define uc_hyph                  int_par(UC_HYPH_CODE)
#define output_penalty           int_par(OUTPUT_PENALTY_CODE)
#define max_dead_cycles          int_par(MAX_DEAD_CYCLES_CODE)
#define hang_after               int_par(HANG_AFTER_CODE)
#define floating_penalty         int_par(FLOATING_PENALTY_CODE)
#define global_defs              int_par(GLOBAL_DEFS_CODE)
#define cur_fam                  int_par(CUR_FAM_CODE)
#define escape_char              int_par(ESCAPE_CHAR_CODE)
#define default_hyphen_char      int_par(DEFAULT_HYPHEN_CHAR_CODE)
#define default_skew_char        int_par(DEFAULT_SKEW_CHAR_CODE)
#define end_line_char            int_par(END_LINE_CHAR_CODE)
#define new_line_char            int_par(NEW_LINE_CHAR_CODE)
#define language                 int_par(LANGUAGE_CODE)
#define left_hyphen_min          int_par(LEFT_HYPHEN_MIN_CODE)
#define right_hyphen_min         int_par(RIGHT_HYPHEN_MIN_CODE)
#define holding_inserts          int_par(HOLDING_INSERTS_CODE)
#define error_context_lines      int_par(ERROR_CONTEXT_LINES_CODE)

⟨ Assign the values depth_threshold = show_box_depth and breadth_max = show_box_breadth 236 ⟩≡

depth_threshold = show_box_depth;
breadth_max = show_box_breadth;

Section 237

We can print the symbolic name of an integer parameter as follows.

other_printing.c
void print_param(int n) {
    switch(n) {
    case PRETOLERANCE_CODE:
        print_esc("pretolerance");
        break;
    
    case TOLERANCE_CODE:
        print_esc("tolerance");
        break;
    
    case LINE_PENALTY_CODE:
        print_esc("linepenalty");
        break;
    
    case HYPHEN_PENALTY_CODE:
        print_esc("hyphenpenalty");
        break;
    
    case EX_HYPHEN_PENALTY_CODE:
        print_esc("exhyphenpenalty");
        break;
    
    case CLUB_PENALTY_CODE:
        print_esc("clubpenalty");
        break;
    
    case WIDOW_PENALTY_CODE:
        print_esc("widowpenalty");
        break;
    
    case DISPLAY_WIDOW_PENALTY_CODE:
        print_esc("displaywidowpenalty");
        break;
    
    case BROKEN_PENALTY_CODE:
        print_esc("brokenpenalty");
        break;
    
    case BIN_OP_PENALTY_CODE:
        print_esc("binoppenalty");
        break;
    
    case REL_PENALTY_CODE:
        print_esc("relpenalty");
        break;
    
    case PRE_DISPLAY_PENALTY_CODE:
        print_esc("predisplaypenalty");
        break;
    
    case POST_DISPLAY_PENALTY_CODE:
        print_esc("postdisplaypenalty");
        break;
    
    case INTER_LINE_PENALTY_CODE:
        print_esc("interlinepenalty");
        break;
    
    case DOUBLE_HYPHEN_DEMERITS_CODE:
        print_esc("doublehyphendemerits");
        break;
    
    case FINAL_HYPHEN_DEMERITS_CODE:
        print_esc("finalhyphendemerits");
        break;
    
    case ADJ_DEMERITS_CODE:
        print_esc("adjdemerits");
        break;
    
    case MAG_CODE:
        print_esc("mag");
        break;
    
    case DELIMITER_FACTOR_CODE:
        print_esc("delimiterfactor");
        break;
    
    case LOOSENESS_CODE:
        print_esc("looseness");
        break;
    
    case TIME_CODE:
        print_esc("time");
        break;
    
    case DAY_CODE:
        print_esc("day");
        break;
    
    case MONTH_CODE:
        print_esc("month");
        break;
    
    case YEAR_CODE:
        print_esc("year");
        break;
    
    case SHOW_BOX_BREADTH_CODE:
        print_esc("showboxbreadth");
        break;
    
    case SHOW_BOX_DEPTH_CODE:
        print_esc("showboxdepth");
        break;
    
    case HBADNESS_CODE:
        print_esc("hbadness");
        break;
    
    case VBADNESS_CODE:
        print_esc("vbadness");
        break;
    
    case PAUSING_CODE:
        print_esc("pausing");
        break;
    
    case TRACING_ONLINE_CODE:
        print_esc("tracingonline");
        break;
    
    case TRACING_MACROS_CODE:
        print_esc("tracingmacros");
        break;
    
    case TRACING_STATS_CODE:
        print_esc("tracingstats");
        break;
    
    case TRACING_PARAGRAPHS_CODE:
        print_esc("tracingparagraphs");
        break;
    
    case TRACING_PAGES_CODE:
        print_esc("tracingpages");
        break;
    
    case TRACING_OUTPUT_CODE:
        print_esc("tracingoutput");
        break;
    
    case TRACING_LOST_CHARS_CODE:
        print_esc("tracinglostchars");
        break;
    
    case TRACING_COMMANDS_CODE:
        print_esc("tracingcommands");
        break;
    
    case TRACING_RESTORES_CODE:
        print_esc("tracingrestores");
        break;
    
    case UC_HYPH_CODE:
        print_esc("uchyph");
        break;
    
    case OUTPUT_PENALTY_CODE:
        print_esc("outputpenalty");
        break;
    
    case MAX_DEAD_CYCLES_CODE:
        print_esc("maxdeadcycles");
        break;
    
    case HANG_AFTER_CODE:
        print_esc("hangafter");
        break;
    
    case FLOATING_PENALTY_CODE:
        print_esc("floatingpenalty");
        break;
    
    case GLOBAL_DEFS_CODE:
        print_esc("globaldefs");
        break;
    
    case CUR_FAM_CODE:
        print_esc("fam");
        break;
    
    case ESCAPE_CHAR_CODE:
        print_esc("escapechar");
        break;
    
    case DEFAULT_HYPHEN_CHAR_CODE:
        print_esc("defaulthyphenchar");
        break;
    
    case DEFAULT_SKEW_CHAR_CODE:
        print_esc("defaultskewchar");
        break;
    
    case END_LINE_CHAR_CODE:
        print_esc("endlinechar");
        break;
    
    case NEW_LINE_CHAR_CODE:
        print_esc("newlinechar");
        break;
    
    case LANGUAGE_CODE:
        print_esc("language");
        break;
    
    case LEFT_HYPHEN_MIN_CODE:
        print_esc("lefthyphenmin");
        break;
    
    case RIGHT_HYPHEN_MIN_CODE:
        print_esc("righthyphenmin");
        break;
    
    case HOLDING_INSERTS_CODE:
        print_esc("holdinginserts");
        break;
    
    case ERROR_CONTEXT_LINES_CODE:
        print_esc("errorcontextlines");
        break;
    
    default:
        print("[unknown integer parameter!]");
    }
}

Section 238

The integer parameter names must be entered into the hash table.

⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡

primitive("pretolerance", ASSIGN_INT, INT_BASE + PRETOLERANCE_CODE);
primitive("tolerance", ASSIGN_INT, INT_BASE + TOLERANCE_CODE);
primitive("linepenalty", ASSIGN_INT, INT_BASE + LINE_PENALTY_CODE);
primitive("hyphenpenalty", ASSIGN_INT, INT_BASE + HYPHEN_PENALTY_CODE);
primitive("exhyphenpenalty", ASSIGN_INT, INT_BASE + EX_HYPHEN_PENALTY_CODE);
primitive("clubpenalty", ASSIGN_INT, INT_BASE + CLUB_PENALTY_CODE);
primitive("widowpenalty", ASSIGN_INT, INT_BASE + WIDOW_PENALTY_CODE);
primitive("displaywidowpenalty",  ASSIGN_INT, INT_BASE + DISPLAY_WIDOW_PENALTY_CODE);
primitive("brokenpenalty", ASSIGN_INT, INT_BASE + BROKEN_PENALTY_CODE);
primitive("binoppenalty", ASSIGN_INT, INT_BASE + BIN_OP_PENALTY_CODE);
primitive("relpenalty", ASSIGN_INT, INT_BASE + REL_PENALTY_CODE);
primitive("predisplaypenalty", ASSIGN_INT, INT_BASE + PRE_DISPLAY_PENALTY_CODE);
primitive("postdisplaypenalty", ASSIGN_INT, INT_BASE + POST_DISPLAY_PENALTY_CODE);
primitive("interlinepenalty", ASSIGN_INT, INT_BASE + INTER_LINE_PENALTY_CODE);
primitive("doublehyphendemerits",  ASSIGN_INT, INT_BASE + DOUBLE_HYPHEN_DEMERITS_CODE);
primitive("finalhyphendemerits",  ASSIGN_INT, INT_BASE + FINAL_HYPHEN_DEMERITS_CODE);
primitive("adjdemerits", ASSIGN_INT, INT_BASE + ADJ_DEMERITS_CODE);
primitive("mag", ASSIGN_INT, INT_BASE + MAG_CODE);
primitive("delimiterfactor", ASSIGN_INT, INT_BASE + DELIMITER_FACTOR_CODE);
primitive("looseness", ASSIGN_INT, INT_BASE + LOOSENESS_CODE);
primitive("time", ASSIGN_INT, INT_BASE + TIME_CODE);
primitive("day", ASSIGN_INT, INT_BASE + DAY_CODE);
primitive("month", ASSIGN_INT, INT_BASE + MONTH_CODE);
primitive("year", ASSIGN_INT, INT_BASE + YEAR_CODE);
primitive("showboxbreadth", ASSIGN_INT, INT_BASE + SHOW_BOX_BREADTH_CODE);
primitive("showboxdepth", ASSIGN_INT, INT_BASE + SHOW_BOX_DEPTH_CODE);
primitive("hbadness", ASSIGN_INT, INT_BASE + HBADNESS_CODE);
primitive("vbadness", ASSIGN_INT, INT_BASE + VBADNESS_CODE);
primitive("pausing", ASSIGN_INT, INT_BASE + PAUSING_CODE);
primitive("tracingonline", ASSIGN_INT, INT_BASE + TRACING_ONLINE_CODE);
primitive("tracingmacros", ASSIGN_INT, INT_BASE + TRACING_MACROS_CODE);
primitive("tracingstats", ASSIGN_INT, INT_BASE + TRACING_STATS_CODE);
primitive("tracingparagraphs", ASSIGN_INT, INT_BASE + TRACING_PARAGRAPHS_CODE);
primitive("tracingpages", ASSIGN_INT, INT_BASE + TRACING_PAGES_CODE);
primitive("tracingoutput", ASSIGN_INT, INT_BASE + TRACING_OUTPUT_CODE);
primitive("tracinglostchars", ASSIGN_INT, INT_BASE + TRACING_LOST_CHARS_CODE);
primitive("tracingcommands", ASSIGN_INT, INT_BASE + TRACING_COMMANDS_CODE);
primitive("tracingrestores", ASSIGN_INT, INT_BASE + TRACING_RESTORES_CODE);
primitive("uchyph", ASSIGN_INT, INT_BASE + UC_HYPH_CODE);
primitive("outputpenalty", ASSIGN_INT, INT_BASE + OUTPUT_PENALTY_CODE);
primitive("maxdeadcycles", ASSIGN_INT, INT_BASE + MAX_DEAD_CYCLES_CODE);
primitive("hangafter", ASSIGN_INT, INT_BASE + HANG_AFTER_CODE);
primitive("floatingpenalty", ASSIGN_INT, INT_BASE + FLOATING_PENALTY_CODE);
primitive("globaldefs", ASSIGN_INT, INT_BASE + GLOBAL_DEFS_CODE);
primitive("fam", ASSIGN_INT, INT_BASE + CUR_FAM_CODE);
primitive("escapechar", ASSIGN_INT, INT_BASE + ESCAPE_CHAR_CODE);
primitive("defaulthyphenchar", ASSIGN_INT, INT_BASE + DEFAULT_HYPHEN_CHAR_CODE);
primitive("defaultskewchar", ASSIGN_INT, INT_BASE + DEFAULT_SKEW_CHAR_CODE);
primitive("endlinechar", ASSIGN_INT, INT_BASE + END_LINE_CHAR_CODE);
primitive("newlinechar", ASSIGN_INT, INT_BASE + NEW_LINE_CHAR_CODE);
primitive("language", ASSIGN_INT, INT_BASE + LANGUAGE_CODE);
primitive("lefthyphenmin", ASSIGN_INT, INT_BASE + LEFT_HYPHEN_MIN_CODE);
primitive("righthyphenmin", ASSIGN_INT, INT_BASE + RIGHT_HYPHEN_MIN_CODE);
primitive("holdinginserts", ASSIGN_INT, INT_BASE + HOLDING_INSERTS_CODE);
primitive("errorcontextlines", ASSIGN_INT, INT_BASE + ERROR_CONTEXT_LINES_CODE);

Section 239

⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡

case ASSIGN_INT:
    if (chr_code < COUNT_BASE) {
        print_param(chr_code - INT_BASE);
    }
    else {
        print_esc("count");
        print_int(chr_code - COUNT_BASE);
    }
    break;

Section 240

The integer parameters should really be initialized by a macro package; the following initialization does the minimum to keep from complete failure.

⟨ Initialize table entries (done by INITEX only) 164 ⟩+≡

for(k = INT_BASE; k < DEL_CODE_BASE; k++) {
    eqtb[k].integer = 0;
}
mag = 1000;
tolerance = 10000;
hang_after = 1; max_dead_cycles = 25;
escape_char = '\\';
end_line_char = CARRIAGE_RETURN;
for(k = 0; k <= 255; k++) {
    del_code(k) = -1;
}
del_code('.') = 0; // this null delimiter is used in error recovery

Section 241

The following procedure, which is called just before initializes its input and output, establishes the initial values of the date and time. Since standard Pascal cannot provide such information, something special is needed. The program here simply assumes that suitable values appear in the global variables sys_time, sys_day, sys_month, and sys_year (which are initialized to noon on 4 July 1776, in case the implementor is careless).

NOTE

We make use of localtime function to get the time from the machine.

init_cleanup.c
void fix_date_and_time() {
    time_t rawtime;
    struct tm *data;
    time(&rawtime);
    data = localtime(&rawtime);
    sys_time = data->tm_hour * 60 + data->tm_min;
    sys_day = data->tm_mday;
    sys_month = data->tm_mon + 1;
    sys_year = data->tm_year + 1900;

    time_ = sys_time;  // minutes since midnight
    day = sys_day;     // day of the month
    month = sys_month; // month of the year
    year = sys_year;   // Anno Domini
}

Section 242

⟨ Show equivalent n, in region 5 242 ⟩≡

if (n < COUNT_BASE) {
    print_param(n - INT_BASE);
}
else if (n < DEL_CODE_BASE) {
    print_esc("count");
    print_int(n - COUNT_BASE);
}
else {
    print_esc("delcode");
    print_int(n - DEL_CODE_BASE);
}
print_char('=');
print_int(eqtb[n].integer);

Section 243

⟨ Set variable c to the current escape character 243 ⟩≡

c = escape_char;

Section 244

NOTE

⟨ Character |s| is the current new-line character 244 ⟩ is already included in sections 58 and 59.

Section 245

is occasionally supposed to print diagnostic information that goes only into the transcript file, unless tracing_online is positive. Here are two routines that adjust the destination of print commands:

other_printing.c
// prepare to do some tracing
void begin_diagnostic() {
    old_setting = selector;
    if (tracing_online <= 0 && selector == TERM_AND_LOG) {
        decr(selector);
        if (history == SPOTLESS) {
            history = WARNING_ISSUED;
        }
    }
}

// restore proper conditions after tracing
void end_diagnostic(bool blank_line) {
    print_nl("");
    if (blank_line) {
        print_ln();
    }
    selector = old_setting;
}

Section 246

Of course we had better declare a few more global variables, if the previous routines are going to work.

⟨ Global variables 13 ⟩+≡

int old_setting;
int sys_time, sys_day, sys_month, sys_year; // date and time supplied by external system

Section 247

The final region of eqtb contains the dimension parameters defined here, and the 256 \dimen registers.

constants.h
#define PAR_INDENT_CODE           0           // indentation of paragraphs
#define MATH_SURROUND_CODE        1           // space around math in text
#define LINE_SKIP_LIMIT_CODE      2           // threshold for |line_skip| instead of |baseline_skip|
#define HSIZE_CODE                3           // line width in horizontal mode
#define VSIZE_CODE                4           // page height in vertical mode
#define MAX_DEPTH_CODE            5           // maximum depth of boxes on main pages
#define SPLIT_MAX_DEPTH_CODE      6           // maximum depth of boxes on split pages
#define BOX_MAX_DEPTH_CODE        7           // maximum depth of explicit vboxes
#define HFUZZ_CODE                8           // tolerance for overfull hbox messages
#define VFUZZ_CODE                9           // tolerance for overfull vbox messages
#define DELIMITER_SHORTFALL_CODE  10          // maximum amount uncovered by variable delimiters
#define NULL_DELIMITER_SPACE_CODE 11          // blank space in null delimiters
#define SCRIPT_SPACE_CODE         12          // extra space after subscript or superscript
#define PRE_DISPLAY_SIZE_CODE     13          // length of text preceding a display
#define DISPLAY_WIDTH_CODE        14          // length of line for displayed equation
#define DISPLAY_INDENT_CODE       15          // indentation of line for displayed equation
#define OVERFULL_RULE_CODE        16          // width of rule that identifies overfull hboxes
#define HANG_INDENT_CODE          17          // amount of hanging indentation
#define H_OFFSET_CODE             18          // amount of horizontal offset when shipping pages out
#define V_OFFSET_CODE             19          // amount of vertical offset when shipping pages out
#define EMERGENCY_STRETCH_CODE    20          // reduces badnesses on final pass of line-breaking
#define DIMEN_PARS                21          // total number of dimension parameters
#define SCALED_BASE (DIMEN_BASE + DIMEN_PARS) // table of 256 user-defined \dimen registers
#define EQTB_SIZE   (SCALED_BASE + 255)       // largest subscript of |eqtb|
datastructures.h
#define dimen(X)             eqtb[SCALED_BASE + (X)].sc
#define dimen_par(X)         eqtb[DIMEN_BASE + (X)].sc  // a scaled quantity
#define par_indent           dimen_par(PAR_INDENT_CODE)
#define math_surround        dimen_par(MATH_SURROUND_CODE)
#define line_skip_limit      dimen_par(LINE_SKIP_LIMIT_CODE)
#define hsize                dimen_par(HSIZE_CODE)
#define vsize                dimen_par(VSIZE_CODE)
#define max_depth            dimen_par(MAX_DEPTH_CODE)
#define split_max_depth      dimen_par(SPLIT_MAX_DEPTH_CODE)
#define box_max_depth        dimen_par(BOX_MAX_DEPTH_CODE)
#define hfuzz                dimen_par(HFUZZ_CODE)
#define vfuzz                dimen_par(VFUZZ_CODE)
#define delimiter_shortfall  dimen_par(DELIMITER_SHORTFALL_CODE)
#define null_delimiter_space dimen_par(NULL_DELIMITER_SPACE_CODE)
#define script_space         dimen_par(SCRIPT_SPACE_CODE)
#define pre_display_size     dimen_par(PRE_DISPLAY_SIZE_CODE)
#define display_width        dimen_par(DISPLAY_WIDTH_CODE)
#define display_indent       dimen_par(DISPLAY_INDENT_CODE)
#define overfull_rule        dimen_par(OVERFULL_RULE_CODE)
#define hang_indent          dimen_par(HANG_INDENT_CODE)
#define h_offset             dimen_par(H_OFFSET_CODE)
#define v_offset             dimen_par(V_OFFSET_CODE)
#define emergency_stretch    dimen_par(EMERGENCY_STRETCH_CODE)
other_printing.c
void print_length_param(int n) {
    switch(n) {
    case PAR_INDENT_CODE:
        print_esc("parindent");
        break;
    
    case MATH_SURROUND_CODE:
        print_esc("mathsurround");
        break;
    
    case LINE_SKIP_LIMIT_CODE:
        print_esc("lineskiplimit");
        break;
    
    case HSIZE_CODE:
        print_esc("hsize");
        break;
    
    case VSIZE_CODE:
        print_esc("vsize");
        break;
    
    case MAX_DEPTH_CODE:
        print_esc("maxdepth");
        break;
    
    case SPLIT_MAX_DEPTH_CODE:
        print_esc("splitmaxdepth");
        break;
    
    case BOX_MAX_DEPTH_CODE:
        print_esc("boxmaxdepth");
        break;
    
    case HFUZZ_CODE:
        print_esc("hfuzz");
        break;
    
    case VFUZZ_CODE:
        print_esc("vfuzz");
        break;
    
    case DELIMITER_SHORTFALL_CODE:
        print_esc("delimitershortfall");
        break;
    
    case NULL_DELIMITER_SPACE_CODE:
        print_esc("nulldelimiterspace");
        break;
    
    case SCRIPT_SPACE_CODE:
        print_esc("scriptspace");
        break;
    
    case PRE_DISPLAY_SIZE_CODE:
        print_esc("predisplaysize");
        break;
    
    case DISPLAY_WIDTH_CODE:
        print_esc("displaywidth");
        break;
    
    case DISPLAY_INDENT_CODE:
        print_esc("displayindent");
        break;
    
    case OVERFULL_RULE_CODE:
        print_esc("overfullrule");
        break;
    
    case HANG_INDENT_CODE:
        print_esc("hangindent");
        break;
    
    case H_OFFSET_CODE:
        print_esc("hoffset");
        break;
    
    case V_OFFSET_CODE:
        print_esc("voffset");
        break;
    
    case EMERGENCY_STRETCH_CODE:
        print_esc("emergencystretch");
        break;
    
    default:
        print("[unknown dimen parameter!]");
    }
}

Section 248

⟨ Put each of TeX’s primitives into the hash table 226 ⟩+≡

primitive("parindent", ASSIGN_DIMEN, DIMEN_BASE + PAR_INDENT_CODE);
primitive("mathsurround", ASSIGN_DIMEN, DIMEN_BASE + MATH_SURROUND_CODE);
primitive("lineskiplimit", ASSIGN_DIMEN, DIMEN_BASE + LINE_SKIP_LIMIT_CODE);
primitive("hsize", ASSIGN_DIMEN, DIMEN_BASE + HSIZE_CODE);
primitive("vsize", ASSIGN_DIMEN, DIMEN_BASE + VSIZE_CODE);
primitive("maxdepth", ASSIGN_DIMEN, DIMEN_BASE + MAX_DEPTH_CODE);
primitive("splitmaxdepth", ASSIGN_DIMEN, DIMEN_BASE + SPLIT_MAX_DEPTH_CODE);
primitive("boxmaxdepth", ASSIGN_DIMEN, DIMEN_BASE + BOX_MAX_DEPTH_CODE);
primitive("hfuzz", ASSIGN_DIMEN, DIMEN_BASE + HFUZZ_CODE);
primitive("vfuzz", ASSIGN_DIMEN, DIMEN_BASE + VFUZZ_CODE);
primitive("delimitershortfall", ASSIGN_DIMEN, DIMEN_BASE + DELIMITER_SHORTFALL_CODE);
primitive("nulldelimiterspace", ASSIGN_DIMEN, DIMEN_BASE + NULL_DELIMITER_SPACE_CODE);
primitive("scriptspace", ASSIGN_DIMEN, DIMEN_BASE + SCRIPT_SPACE_CODE);
primitive("predisplaysize", ASSIGN_DIMEN, DIMEN_BASE + PRE_DISPLAY_SIZE_CODE);
primitive("displaywidth", ASSIGN_DIMEN, DIMEN_BASE + DISPLAY_WIDTH_CODE);
primitive("displayindent", ASSIGN_DIMEN, DIMEN_BASE + DISPLAY_INDENT_CODE);
primitive("overfullrule", ASSIGN_DIMEN, DIMEN_BASE + OVERFULL_RULE_CODE);
primitive("hangindent", ASSIGN_DIMEN, DIMEN_BASE + HANG_INDENT_CODE);
primitive("hoffset", ASSIGN_DIMEN, DIMEN_BASE + H_OFFSET_CODE);
primitive("voffset", ASSIGN_DIMEN, DIMEN_BASE + V_OFFSET_CODE);
primitive("emergencystretch", ASSIGN_DIMEN, DIMEN_BASE + EMERGENCY_STRETCH_CODE);

Section 249

⟨ Cases of print_cmd_chr for symbolic printing of primitives 227 ⟩+≡

case ASSIGN_DIMEN:
    if (chr_code < SCALED_BASE) {
        print_length_param(chr_code - DIMEN_BASE);
    }
    else {
        print_esc("dimen");
        print_int(chr_code - SCALED_BASE);
    }
    break;

Section 250

⟨ Initialize table entries (done by INITEX only) 164 ⟩+≡

for(k = DIMEN_BASE; k <= EQTB_SIZE; k++) {
    eqtb[k].sc = 0;
}

Section 251

⟨ Show equivalent n, in region 6 251 ⟩≡

if (n < SCALED_BASE) {
    print_length_param(n - DIMEN_BASE);
}
else {
    print_esc("dimen");
    print_int(n - SCALED_BASE);
}
print_char('=');
print_scaled(eqtb[n].sc);
print("pt");

Section 252

Here is a procedure that displays the contents of eqtb[n] symbolically.

other_printing.c
#ifdef STAT
void show_eqtb(pointer n) {
    if (n < ACTIVE_BASE) {
        print_char('?'); // this can't happen
    }
    else if (n <GLUE_BASE) {
        // << Show equivalent |n|, in region 1 or 2, 223 >>
    }
    else if (n <LOCAL_BASE) {
        // << Show equivalent |n|, in region 3, 229 >>
    }
    else if (n <INT_BASE) {
        // << Show equivalent |n|, in region 4, 233 >>
    }
    else if (n <DIMEN_BASE) {
        // << Show equivalent |n|, in region 5, 242 >>
    }
    else if (n <=EQTB_SIZE) {
        // << Show equivalent |n|, in region 6, 251 >>
    }
    else {
        print_char('?'); // this can't happen either
    }
}
#endif

Section 253

The last two regions of eqtb have fullword values instead of the three fields eq_level, eq_type, and equiv. An eq_type is unnecessary, but needs to store the eq_level information in another array called xeq_level.

NOTE

Little bit of pointer arithmetic so arrays can be indexed by something other than zero.

⟨ Global variables 13 ⟩+≡

memory_word eqtb0[EQTB_SIZE - ACTIVE_BASE + 1];
memory_word *eqtb = eqtb0 - ACTIVE_BASE;
quarterword xeq_level0[EQTB_SIZE - INT_BASE + 1];
quarterword *xeq_level = xeq_level0 - INT_BASE;

Section 254

⟨ Set initial values of key variables 21 ⟩+≡

for(k = INT_BASE; k <= EQTB_SIZE; k++) {
    xeq_level[k] = LEVEL_ONE;
}

Section 255

When the debugging routine search_mem is looking for pointers having a given value, it is interested only in regions 1 to 3 of eqtb, and in the first part of region 4.

⟨ Search eqtb for equivalents equal to p 255 ⟩≡

for(q = ACTIVE_BASE; q <= BOX_BASE + 255; q++) {
    if (equiv(q) == p) {
        print_nl("EQUIV(");
        print_int(q);
        print_char(')');
    }
}