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 ‘[]
’.
// << 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.
// 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:
// 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.
// 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.
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:
// |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.
// 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.
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.
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();
}