Section 173: Displaying boxes

We can reinforce our knowledge of the data structures just introduced by considering two procedures that display a list in symbolic form. The first of these, called short_display, is used in “overfull box” messages to give the top-level description of a list. The other one, called show_node_list, prints a detailed description of exactly what is in the data structure.

The philosophy of short_display is to ignore the fine points about exactly what is inside boxes, except that ligatures and discretionary breaks are expanded. As a result, short_display is a recursive procedure, but the recursion is never more than one level deep.

A global variable font_in_short_display keeps track of the font code that is assumed to be present when short_display begins; deviations from this font will be printed.

⟨ Global variables 13 ⟩+≡

int font_in_short_display; // an internal font number

Section 174

Boxes, rules, inserts, whatsits, marks, and things in general that are sort of “complicated” are indicated only by printing ‘[]’.

display_boxes.c
// << Start file |display_boxes.c|, 1382 >>

// prints highlights of list |p|
void short_display(int p) {
    int n; // for replacement counts
    while (p > MEM_MIN) {
        if (is_char_node(p)) {
            if (p <= mem_end) {
                if (font(p) != font_in_short_display) {
                    if (/* font(p) < FONT_BASE || */ font(p) > FONT_MAX) {
                        // Comparison with |FONT_BASE| is useless
                        // since |FONT_BASE = 0| and |font(p)| is in [0 .. 65535]
                        print_char('*');
                    }
                    else {
                        // << Print the font identifier for |font(p)|, 267 >>
                    }
                    print_char(' ');
                    font_in_short_display = font(p);
                }
                print_strnumber(character(p));
            }
        }
        else {
            // << Print a short indication of the contents of node |p|, 175 >>
        }
        p = link(p);
    }
}

Section 175

⟨ Print a short indication of the contents of node p 175 ⟩≡

switch(type(p)) {
case HLIST_NODE:
case VLIST_NODE:
case INS_NODE:
case WHATSIT_NODE:
case MARK_NODE:
case ADJUST_NODE:
case UNSET_NODE:
    print("[]");
    break;

case RULE_NODE:
    print_char('|');
    break;

case GLUE_NODE:
    if (glue_ptr(p) != ZERO_GLUE) {
        print_char(' ');
    }
    break;

case MATH_NODE:
    print_char('$');
    break;

case LIGATURE_NODE:
    short_display(lig_ptr(p));
    break;

case DISC_NODE:
    short_display(pre_break(p));
    short_display(post_break(p));
    n = replace_count(p);
    while (n > 0) {
        if (link(p) != null) {
            p = link(p);
        }
        decr(n);
    }
    break;

default:
    do_nothing;
}

Section 176

The show_node_list routine requires some auxiliary subroutines: one to print a font-and-character combination, one to print a token list without its reference count, and one to print a rule dimension.

display_boxes.c
// prints |CHAR_NODE| data
void print_font_and_char(int p) {
    if (p > mem_end) {
        print_esc("CLOBBERED.");
    }
    else {
        if (/* font(p) < FONT_BASE || */ font(p) > FONT_MAX) {
            // |FONT_BASE = 0| and |font(p)| is in [0 .. 65535]
            print_char('*');
        }
        else {
            // << Print the font identifier for |font(p)|, 267 >>
        }
        print_char(' ');
        print_strnumber(character(p));
    }
}

// prints token list data in braces
void print_mark(int p) {
    print_char('{');
    if (p < hi_mem_min || p > mem_end) {
        print_esc("CLOBBERED.");
    }
    else {
        show_token_list(link(p), null, MAX_PRINT_LINE - 10);
    }
    print_char('}');
}

// prints dimension in rule node
void print_rule_dimen(scaled d) {
    if (is_running(d)) {
        print_char('*');
    }
    else {
        print_scaled(d);
    }
}

Section 177

Then there is a subroutine that prints glue stretch and shrink, possibly followed by the name of finite units:

display_boxes.c
// prints a glue component
void print_glue(scaled d, int order, char *s) {
    print_scaled(d);
    if (order < NORMAL || order > FILLL) {
        print("foul");
    }
    else if (order > NORMAL) {
        print("fil");
        while (order > FIL) {
            print_char('l');
            decr(order);
        }
    }
    else if (strlen(s) != 0) {
        print(s);
    }
}

Section 178

The next subroutine prints a whole glue specification.

display_boxes.c
// prints a glue specification
void print_spec(int p, char *s) {
    if (p < MEM_MIN || p >= lo_mem_max) {
        print_char('*');
    }
    else {
        print_scaled(width(p));
        if (strlen(s) != 0) {
            print(s);
        }
        if (stretch(p) != 0) {
            print(" plus ");
            print_glue(stretch(p), stretch_order(p), s);
        }
        if (shrink(p) != 0) {
            print(" minus ");
            print_glue(shrink(p), shrink_order(p), s);
        }
    }
}

Section 179

We also need to declare some procedures that appear later in this documentation.

NOTE

Those are procedures for displaying the elements of mlists, and procedure print_skip_param. Originally:

  • ⟨ Declare procedures needed for displaying the elements of mlists, 691 ⟩;
  • ⟨ Declare the procedure called print_skip_param, 225 ⟩.

Section 180

Since boxes can be inside of boxes, show_node_list is inherently recursive, up to a given maximum number of levels. The history of nesting is indicated by the current string, which will be printed at the beginning of each line; the length of this string, namely cur_length, is the depth of nesting.

Recursive calls on show_node_list therefore use the following pattern:

io.h
// |str_room| need not be checked; see |show_box| below
#define node_list_display(X) append_char('.'); show_node_list((X)); flush_char

Section 181

A global variable called depth_threshold is used to record the maximum depth of nesting for which show_node_list will show information. If we have depth_threshold = 0, for example, only the top level information will be given and no sublists will be traversed. Another global variable, called breadth_max, tells the maximum number of items to show at each level; breadth_max had better be positive, or you won’t see anything.

⟨ Global variables 13 ⟩+≡

int depth_threshold; // maximum nesting depth in box displays
int breadth_max;    // maximum number of items shown at the same list level

Section 182

Now we are ready for show_node_list itself. This procedure has been written to be “extra robust” in the sense that it should not crash or get into a loop even if the data structures have been messed up by bugs in the rest of the program. You can safely call its parent routine show_box(p) for arbitrary values of p when you are debugging . However, in the presence of bad data, the procedure may fetch a memory_word whose variant is different from the way it was stored; for example, it might try to read mem[p].hh when mem[p] contains a scaled integer, if p is a pointer that has been clobbered or chosen at random.

display_boxes.c
// prints a node list symbolically
void show_node_list(int p) {
    int n;    // the number of items already printed at this level
    double g; // a glue ratio, as a floating point number
    if (cur_length > depth_threshold) {
        if (p > null) {
            // indicate that there's been some truncation
            print(" []");
        }
        return;
    }
    n = 0;
    while (p > MEM_MIN) {
        // display the nesting history
        print_ln();
        print_current_string();
        if (p > mem_end) {
            // pointer out of range
            print("Bad link, display aborted.");
            return;
        }
        incr(n);
        if (n > breadth_max) {
            // time to stop
            print("etc.");
            return;
        }
        // << Display node |p|, 183 >>
        p = link(p);
    }
}

Section 183

⟨ Display node p 183 ⟩≡

if (is_char_node(p)) {
    print_font_and_char(p);
}
else {
    switch(type(p)) {
    case HLIST_NODE:
    case VLIST_NODE:
    case UNSET_NODE:
        // << Display box |p|, 184 >>
        break;
    
    case RULE_NODE:
        // << Display rule |p|, 187 >>
        break;
    
    case INS_NODE:
        // << Display insertion |p|, 188 >>
        break;
    
    case WHATSIT_NODE:
        // << Display the whatsit node |p|, 1356 >>
        break;
    
    case GLUE_NODE:
        // << Display glue |p|, 189 >>
        break;
    
    case KERN_NODE:
        // << Display kern |p|, 191 >>
        break;
    
    case MATH_NODE:
        // << Display math node |p|, 192 >>
        break;
    
    case LIGATURE_NODE:
        // << Display ligature |p|, 193 >>
        break;
    
    case PENALTY_NODE:
        // << Display penalty |p|, 194 >>
        break;
    
    case DISC_NODE:
        // << Display discretionary |p|, 195 >>
        break;
    
    case MARK_NODE:
        // << Display mark |p|, 196 >>
        break;
    
    case ADJUST_NODE:
        // << Display adjustment |p|, 197 >>
        break;
    
    // << Cases of |show_node_list| that arise in mlists only, 690 >>

    default:
        print("Unknown node type!");
    }
}

Section 184

⟨ Display box p 184 ⟩≡

if (type(p) == HLIST_NODE) {
    print_esc("h");
}
else if (type(p) == VLIST_NODE) {
    print_esc("v");
}
else {
    print_esc("unset");
}
print("box(");
print_scaled(height(p));
print_char('+');
print_scaled(depth(p));
print(")x");
print_scaled(width(p));
if (type(p) == UNSET_NODE) {
    // << Display special fields of the unset node |p|, 185 >>
}
else {
    // << Display the value of |glue_set(p)|, 186 >>
    if (shift_amount(p) != 0) {
        print(", shifted ");
        print_scaled(shift_amount(p));
    }
}
node_list_display(list_ptr(p)); // recursive call

Section 185

⟨ Display special fields of the unset node p 185 ⟩≡

if (span_count(p) != MIN_QUARTERWORD) {
    print(" (");
    print_int(span_count(p) + 1);
    print(" columns)");
}
if (glue_stretch(p) != 0) {
    print(", stretch ");
    print_glue(glue_stretch(p), glue_order(p), "");
}
if (glue_shrink(p) != 0) {
    print(", shrink ");
    print_glue(glue_shrink(p), glue_sign(p), "");
}

Section 186

The code will have to change in this place if glue_ratio is a structured type instead of an ordinary real. Note that this routine should avoid arithmetic errors even if the glue_set field holds an arbitrary random value. The following code assumes that a properly formed nonzero real number has absolute value or more when it is regarded as an integer; this precaution was adequate to prevent floating point underflow on the author’s computer.

NOTE

The precaution has been removed.

⟨ Display the value of glue_set(p) 186 ⟩≡

g = glue_set(p);
if (g != 0.0 && glue_sign(p) != NORMAL) {
    print(", glue set ");
    if (glue_sign(p) == SHRINKING) {
        print("- ");
    }
    /* if (abs(mem[p + GLUE_OFFSET].integer) < 0x100000) {
        print("?.?");
    } */
    if (abs(g) > 20000.0) {
        if (g > 0.0) {
            print_char('>');
        }
        else {
            print("< -");
        }
        print_glue(20000*UNITY, glue_order(p), "");
    }
    else {
        print_glue((scaled)round(UNITY*g), glue_order(p), "");
    }
}

Section 187

⟨ Display rule p 187 ⟩≡

print_esc("rule(");
print_rule_dimen(height(p));
print_char('+');
print_rule_dimen(depth(p));
print(")x");
print_rule_dimen(width(p));

Section 188

⟨ Display insertion p 188 ⟩≡

print_esc("insert");
print_int(subtype(p));
print(", natural size ");
print_scaled(height(p));
print("; split(");
print_spec(split_top_ptr(p), "");
print_char(',');
print_scaled(depth(p));
print("); float cost ");
print_int(float_cost(p));
node_list_display(ins_ptr(p)); // recursive call

Section 189

⟨ Display glue p 189 ⟩≡

if (subtype(p) >= A_LEADERS) {
    // << Display leaders |p|, 190 >>
}
else {
    print_esc("glue");
    if (subtype(p) != NORMAL) {
        print_char('(');
        if (subtype(p) < COND_MATH_GLUE) {
            print_skip_param(subtype(p) - 1);
        }
        else if (subtype(p) == COND_MATH_GLUE) {
            print_esc("nonscript");
        }
        else {
            print_esc("mskip");
        }
        print_char(')');
    }

    if (subtype(p) != COND_MATH_GLUE) {
        print_char(' ');
        if (subtype(p) < COND_MATH_GLUE) {
            print_spec(glue_ptr(p), "");
        }
        else {
            print_spec(glue_ptr(p), "mu");
        }
    }
}

Section 190

⟨ Display leaders p 190 ⟩≡

if (subtype(p) == C_LEADERS) {
    print_esc("cleaders ");
}
else if (subtype(p) == X_LEADERS) {
    print_esc("xleaders ");
}
else {
    print_esc("leaders ");
}
print_spec(glue_ptr(p), "");
node_list_display(leader_ptr(p)); // recursive call

Section 191

An “explicit” kern value is indicated implicitly by an explicit space.

⟨ Display kern p 191 ⟩≡

if (subtype(p) != MU_GLUE) {
    print_esc("kern");
    if (subtype(p) != NORMAL) {
        print_char(' ');
    }
    print_scaled(width(p));
    if (subtype(p) == ACC_KERN) {
        print(" (for accent)");
    }
}
else {
    print_esc("mkern");
    print_scaled(width(p));
    print("mu");
}

Section 192

⟨ Display math node p 192 ⟩≡

print_esc("math");
if (subtype(p) == BEFORE) {
    print("on");
}
else {
    print("off");
}
if (width(p) != 0) {
    print(", surrounded ");
    print_scaled(width(p));
}

Section 193

⟨ Display ligature p 193 ⟩≡

print_font_and_char(lig_char(p));
print(" (ligature ");
if (subtype(p) > 1) {
    print_char('|');
}
font_in_short_display = font(lig_char(p));
short_display(lig_ptr(p));
if (odd(subtype(p))) {
    print_char('|');
}
print_char(')');

Section 194

⟨ Display penalty p 194 ⟩≡

print_esc("penalty ");
print_int(penalty(p));

Section 195

The post_break list of a discretionary node is indicated by a prefixed ‘|’ instead of the ‘.’ before the pre_break list.

⟨ Display discretionary p 195 ⟩≡

print_esc("discretionary");
if (replace_count(p) > 0) {
    print(" replacing ");
    print_int(replace_count(p));
}
node_list_display(pre_break(p)); // recursive call
append_char('|');
show_node_list(post_break(p)); // recursive call
flush_char;

Section 196

⟨ Display mark p 196 ⟩≡

print_esc("mark");
print_mark(mark_ptr(p));

Section 197

⟨ Display adjustment p 197 ⟩≡

print_esc("vadjust");
node_list_display(adjust_ptr(p)); // recursive call

Section 198

The recursive machinery is started by calling show_box.

display_boxes.c
void show_box(pointer p) {
    // << Assign the values |depth_threshold = show_box_depth| and |breadth_max = show_box_breadth|, 236 >>
    if (breadth_max <= 0) {
        breadth_max = 5;
    }
    if (pool_ptr + depth_threshold >= POOL_SIZE) {
        depth_threshold = POOL_SIZE - pool_ptr - 1;
    } // now there's enough room for prefix string
    show_node_list(p); // the show starts at |p|
    print_ln();
}