diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/printing.w')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/printing.w | 984 |
1 files changed, 984 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.w b/Build/source/texk/web2c/luatexdir/tex/printing.w new file mode 100644 index 00000000000..cb4199841e8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/printing.w @@ -0,0 +1,984 @@ +% printing.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "lua/luatex-api.h" /* for ptexbanner */ +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: printing.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/printing.w $"; + +@ @c +#define font_id_text(A) cs_text(font_id_base+(A)) + +#define wlog(A) fputc(A,log_file) +#define wterm(A) fputc(A,term_out) + + +@ Messages that are sent to a user's terminal and to the transcript-log file +are produced by several `|print|' procedures. These procedures will +direct their output to a variety of places, based on the setting of +the global variable |selector|, which has the following possible +values: + +\yskip +\hang |term_and_log|, the normal setting, prints on the terminal and on the + transcript file. + +\hang |log_only|, prints only on the transcript file. + +\hang |term_only|, prints only on the terminal. + +\hang |no_print|, doesn't print at all. This is used only in rare cases + before the transcript file is open. + +\hang |pseudo|, puts output into a cyclic buffer that is used + by the |show_context| routine; when we get to that routine we shall discuss + the reasoning behind this curious mode. + +\hang |new_string|, appends the output to the current string in the + string pool. + +\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output. + +\yskip +\noindent The symbolic names `|term_and_log|', etc., have been assigned +numeric codes that satisfy the convenient relations |no_print+1=term_only|, +|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|. + +Three additional global variables, |tally| and |term_offset| and +|file_offset|, record the number of characters that have been printed +since they were most recently cleared to zero. We use |tally| to record +the length of (possibly very long) stretches of printing; |term_offset| +and |file_offset|, on the other hand, keep track of how many characters +have appeared so far on the current line that has been output to the +terminal or to the transcript file, respectively. + +@c +alpha_file log_file; /* transcript of \TeX\ session */ +int selector = term_only; /* where to print a message */ +int dig[23]; /* digits in a number being output */ +int tally = 0; /* the number of characters recently printed */ +int term_offset = 0; /* the number of characters on the current terminal line */ +int file_offset = 0; /* the number of characters on the current file line */ +packed_ASCII_code trick_buf[(ssup_error_line + 1)]; /* circular buffer for pseudoprinting */ +int trick_count; /* threshold for pseudoprinting, explained later */ +int first_count; /* another variable for pseudoprinting */ +boolean inhibit_par_tokens = false; /* for minor adjustments to |show_token_list| */ + +@ To end a line of text output, we call |print_ln| +@c +void print_ln(void) +{ /* prints an end-of-line */ + switch (selector) { + case term_and_log: + wterm_cr(); + wlog_cr(); + term_offset = 0; + file_offset = 0; + break; + case log_only: + wlog_cr(); + file_offset = 0; + break; + case term_only: + wterm_cr(); + term_offset = 0; + break; + case no_print: + case pseudo: + case new_string: + break; + default: + fprintf(write_file[selector], "\n"); + break; + } +} /* |tally| is not affected */ + + +@ The |print_char| procedure sends one byte to the desired destination. + All printing comes through |print_ln| or |print_char|. + +@c +#define wterm_char(A) do { \ + if ((A>=0x20)||(A==0x0A)||(A==0x0D)||(A==0x09)) { \ + wterm(A); \ + } else { \ + if (term_offset+2>=max_print_line) { \ + wterm_cr(); term_offset=0; \ + } \ + incr(term_offset); wterm('^'); \ + incr(term_offset); wterm('^'); \ + wterm(A+64); \ + } \ + } while (0) + +#define needs_wrapping(A,B) \ + (((A>=0xC0)&&(A<=0xDF)&&(B+2>=max_print_line))|| \ + ((A>=0xE0)&&(A<=0xEF)&&(B+3>=max_print_line))|| \ + ((A>=0xF0)&&(B+4>=max_print_line))) + +#define fix_term_offset(A) do { \ + if (needs_wrapping(A,term_offset)){ \ + wterm_cr(); term_offset=0; \ + } \ + } while (0) + +#define fix_log_offset(A) do { \ + if (needs_wrapping(A,file_offset)){ \ + wlog_cr(); file_offset=0; \ + } \ + } while (0) + +void print_char(int s) +{ /* prints a single byte */ + assert(s >= 0 && s < 256); + if (s == int_par(new_line_char_code)) { + if (selector < pseudo) { + print_ln(); + return; + } + } + switch (selector) { + case term_and_log: + fix_term_offset(s); + fix_log_offset(s); + wterm_char(s); + wlog(s); + incr(term_offset); + incr(file_offset); + if (term_offset == max_print_line) { + wterm_cr(); + term_offset = 0; + } + if (file_offset == max_print_line) { + wlog_cr(); + file_offset = 0; + } + break; + case log_only: + fix_log_offset(s); + wlog(s); + incr(file_offset); + if (file_offset == max_print_line) { + wlog_cr(); + file_offset = 0; + } + break; + case term_only: + fix_term_offset(s); + wterm_char(s); + incr(term_offset); + if (term_offset == max_print_line) { + wterm_cr(); + term_offset = 0; + } + break; + case no_print: + break; + case pseudo: + if (tally < trick_count) + trick_buf[tally % error_line] = (packed_ASCII_code) s; + break; + case new_string: + append_char(s); + break; /* we drop characters if the string space is full */ + default: + fprintf(write_file[selector], "%c", s); + } + incr(tally); +} + + +@ An entire string is output by calling |print|. Note that if we are outputting +the single standard ASCII character \.c, we could call |print("c")|, since +|"c"=99| is the number of a single-character string, as explained above. But +|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char| +routine when it knows that this is safe. (The present implementation +assumes that it is always safe to print a visible ASCII character.) +@^system dependencies@> + +The first 256 entries above the 17th unicode plane are used for a +special trick: when \TeX\ has to print items in that range, it will +instead print the character that results from substracting 0x110000 +from that value. This allows byte-oriented output to things like +\.{\\specials} and \.{\\pdfliterals}. Todo: Perhaps it would be useful +to do the same substraction while typesetting. + +@c +void print(int s) +{ /* prints string |s| */ + unsigned char *j, *l; /* current character code position */ + + if (s >= str_ptr) { + /* this can't happen */ + print_char('?'); + print_char('?'); + print_char('?'); + return; + } else if (s < STRING_OFFSET) { + if (s < 0) { + /* can't happen */ + print_char('?'); + print_char('?'); + print_char('?'); + } else { + /* TH not sure about this, disabled for now! */ + if ((false) && (selector > pseudo)) { + print_char(s); + return; /* internal strings are not expanded */ + } + if (s == int_par(new_line_char_code)) { + if (selector < pseudo) { + print_ln(); + return; + } + } + if (s <= 0x7F) { + print_char(s); + } else if (s <= 0x7FF) { + print_char(0xC0 + (s / 0x40)); + print_char(0x80 + (s % 0x40)); + } else if (s <= 0xFFFF) { + print_char(0xE0 + (s / 0x1000)); + print_char(0x80 + ((s % 0x1000) / 0x40)); + print_char(0x80 + ((s % 0x1000) % 0x40)); + } else if (s >= 0x110000) { + int c = s - 0x110000; + if (c >= 256) { + pdf_warning("print", "bad raw byte to print (c=", + true, false); + print_int(c); + tprint("), skipped."); + print_ln(); + } else { + print_char(c); + } + } else { + print_char(0xF0 + (s / 0x40000)); + print_char(0x80 + ((s % 0x40000) / 0x1000)); + print_char(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); + print_char(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); + } + } + return; + } + if (selector == new_string) { + append_string(str_string(s), (unsigned) str_length(s)); + return; + } + j = str_string(s); + l = j + str_length(s); + while (j < l) { + /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */ + /* I don't bother checking the last two bytes explicitly */ + if ((j < l - 4) && (*j == 0xF4) && (*(j + 1) == 0x90)) { + int c = (*(j + 2) - 128) * 64 + (*(j + 3) - 128); + assert(c >= 0 && c < 256); + print_char(c); + j = j + 4; + } else { + print_char(*j); + incr(j); + } + } +} + + +@ The procedure |print_nl| is like |print|, but it makes sure that the +string appears at the beginning of a new line. + +@c +void print_nlp(void) +{ /* move to beginning of a line */ + if (((term_offset > 0) && (odd(selector))) || + ((file_offset > 0) && (selector >= log_only))) + print_ln(); +} + +void print_nl(str_number s) +{ /* prints string |s| at beginning of line */ + print_nlp(); + print(s); +} + +@ |char *| versions of the same procedures + +@c +void tprint(const char *s) +{ + const unsigned char *ss = (const unsigned char *) s; + if (selector == new_string) { + append_string(ss, (unsigned) strlen(s)); + return; + } + while (*ss) + print_char(*ss++); +} + +void tprint_nl(const char *s) +{ + print_nlp(); + tprint(s); +} + +@ |slow_print| is the same as |print| nowadays, but the name is kept for now. + +@c +void slow_print(int s) +{ /* prints string |s| */ + print(s); +} + +@ Here is the very first thing that \TeX\ prints: a headline that identifies +the version number and format package. The |term_offset| variable is temporarily +incorrect, but the discrepancy is not serious since we assume that the banner +and format identifier together will occupy at most |max_print_line| +character positions. + +@c +void print_banner(const char *v, int e, int ver) +{ + boolean res; + int callback_id; + callback_id = callback_defined(start_run_callback); + if (callback_id == 0) { + if (ver < 0) + fprintf(term_out, "This is LuaTeX, Version %s-%d ", v, e); + else + fprintf(term_out, "This is LuaTeX, Version %s-%d (rev %d) ", v, e, + ver); + if (format_ident > 0) + slow_print(format_ident); + print_ln(); + if (shellenabledp) { + wterm(' '); + if (restrictedshell) + fprintf(term_out, "restricted "); + fprintf(term_out, "\\write18 enabled.\n"); + } + } else if (callback_id > 0) { + res = run_callback(callback_id, "->"); + } +} + +@ @c +void log_banner(const char *v, int e, int ver) +{ + const char *months[] = { " ", + "JAN", "FEB", "MAR", "APR", "MAY", "JUN", + "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" + }; + unsigned month = (unsigned) int_par(month_code); + if (month > 12) + month = 0; + if (ver < 0) + fprintf(log_file, "This is LuaTeX, Version %s-%d ", v, e); + else + fprintf(log_file, "This is LuaTeX, Version %s-%d (rev %d) ", v, e, ver); + slow_print(format_ident); + print_char(' '); + print_char(' '); + print_int(int_par(day_code)); + print_char(' '); + fprintf(log_file, months[month]); + print_char(' '); + print_int(int_par(year_code)); + print_char(' '); + print_two(int_par(time_code) / 60); + print_char(':'); + print_two(int_par(time_code) % 60); + if (shellenabledp) { + wlog_cr(); + wlog(' '); + if (restrictedshell) + fprintf(log_file, "restricted "); + fprintf(log_file, "\\write18 enabled."); + } + if (filelineerrorstylep) { + wlog_cr(); + fprintf(log_file, " file:line:error style messages enabled."); + } + if (parsefirstlinep) { + wlog_cr(); + fprintf(log_file, " %%&-line parsing enabled."); + } +} + +@ @c +void print_version_banner(void) +{ + fprintf(term_out, ptexbanner); +} + +@ The procedure |print_esc| prints a string that is preceded by +the user's escape character (which is usually a backslash). + +@c +void print_esc(str_number s) +{ /* prints escape character, then |s| */ + int c; /* the escape character code */ + /* Set variable |c| to the current escape character */ + c = int_par(escape_char_code); + if (c >= 0 && c < STRING_OFFSET) + print(c); + print(s); +} + +@ @c +void tprint_esc(const char *s) +{ /* prints escape character, then |s| */ + int c; /* the escape character code */ + /* Set variable |c| to the current escape character */ + c = int_par(escape_char_code); + if (c >= 0 && c < STRING_OFFSET) + print(c); + tprint(s); +} + +@ An array of digits in the range |0..15| is printed by |print_the_digs|. + +@c +void print_the_digs(eight_bits k) +{ + /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */ + while (k-- > 0) { + if (dig[k] < 10) + print_char('0' + dig[k]); + else + print_char('A' - 10 + dig[k]); + } +} + +@ The following procedure, which prints out the decimal representation of a +given integer |n|, has been written carefully so that it works properly +if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div| +to negative arguments, since such operations are not implemented consistently +by all PASCAL compilers. + +@c +void print_int(longinteger n) +{ /* prints an integer in decimal form */ + int k; /* index to current digit; we assume that $|n|<10^{23}$ */ + longinteger m; /* used to negate |n| in possibly dangerous cases */ + k = 0; + if (n < 0) { + print_char('-'); + if (n > -100000000) { + n = -n; + } else { + m = -1 - n; + n = m / 10; + m = (m % 10) + 1; + k = 1; + if (m < 10) + dig[0] = (int) m; + else { + dig[0] = 0; + incr(n); + } + } + } + do { + dig[k] = (int) (n % 10); + n = n / 10; + incr(k); + } while (n != 0); + print_the_digs((eight_bits) k); +} + + +@ Here is a trivial procedure to print two digits; it is usually called with +a parameter in the range |0<=n<=99|. + +@c +void print_two(int n) +{ /* prints two least significant digits */ + n = abs(n) % 100; + print_char('0' + (n / 10)); + print_char('0' + (n % 10)); +} + + +@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. + +@c +void print_hex(int n) +{ /* prints a positive integer in hexadecimal form */ + int k; /* index to current digit; we assume that $0\L n<16^{22}$ */ + k = 0; + print_char('"'); + do { + dig[k] = n % 16; + n = n / 16; + incr(k); + } while (n != 0); + print_the_digs((eight_bits) k); +} + + +@ Roman numerals are produced by the |print_roman_int| routine. Readers +who like puzzles might enjoy trying to figure out how this tricky code +works; therefore no explanation will be given. Notice that 1990 yields +\.{mcmxc}, not \.{mxm}. + +@c +void print_roman_int(int n) +{ + char *j, *k; /* mysterious indices */ + nonnegative_integer u, v; /* mysterious numbers */ + char mystery[] = "m2d5c2l5x2v5i"; + j = (char *) mystery; + v = 1000; + while (1) { + while (n >= (int) v) { + print_char(*j); + n = n - (int) v; + } + if (n <= 0) + return; /* nonpositive input produces no output */ + k = j + 2; + u = v / (*(k - 1) - '0'); + if (*(k - 1) == '2') { + k = k + 2; + u = u / (*(k - 1) - '0'); + } + if (n + (int) u >= (int) v) { + print_char(*k); + n = n + (int) u; + } else { + j = j + 2; + v = v / (*(j - 1) - '0'); + } + } +} + + +@ The |print| subroutine will not print a string that is still being +created. The following procedure will. + +@c +void print_current_string(void) +{ /* prints a yet-unmade string */ + unsigned j = 0; /* points to current character code */ + while (j < cur_length) + print_char(cur_string[j++]); +} + +@ The procedure |print_cs| prints the name of a control sequence, given +a pointer to its address in |eqtb|. A space is printed after the name +unless it is a single nonletter or an active character. This procedure +might be invoked with invalid data, so it is ``extra robust.'' The +individual characters must be printed one at a time using |print|, since +they may be unprintable. + +@c +void print_cs(int p) +{ /* prints a purported control sequence */ + str_number t = cs_text(p); + if (p < hash_base) { /* nullcs */ + if (p == null_cs) { + tprint_esc("csname"); + tprint_esc("endcsname"); + } else { + tprint_esc("IMPOSSIBLE."); + } + } else if ((p >= undefined_control_sequence) && + ((p <= eqtb_size) || p > eqtb_size + hash_extra)) { + tprint_esc("IMPOSSIBLE."); + } else if (t >= str_ptr) { + tprint_esc("NONEXISTENT."); + } else { + if (is_active_cs(t)) { + print(active_cs_value(t)); + } else { + print_esc(t); + if (single_letter(t)) { + if (get_cat_code(int_par(cat_code_table_code), + pool_to_unichar(str_string(t))) == letter_cmd) + print_char(' '); + } else { + print_char(' '); + } + } + } +} + + +@ Here is a similar procedure; it avoids the error checks, and it never +prints a space after the control sequence. + +@c +void sprint_cs(pointer p) +{ /* prints a control sequence */ + str_number t; + if (p == null_cs) { + tprint_esc("csname"); + tprint_esc("endcsname"); + } else { + t = cs_text(p); + if (is_active_cs(t)) + print(active_cs_value(t)); + else + print_esc(t); + } +} + + +@ This procedure is never called when |interaction<scroll_mode|. +@c +void prompt_input(const char *s) +{ + wake_up_terminal(); + tprint(s); + term_input(); +} + + +@ Then there is a subroutine that prints glue stretch and shrink, possibly +followed by the name of finite units: + +@c +void print_glue(scaled d, int order, const char *s) +{ /* prints a glue component */ + print_scaled(d); + if ((order < normal) || (order > filll)) { + tprint("foul"); + } else if (order > normal) { + tprint("fi"); + while (order > sfi) { + print_char('l'); + decr(order); + } + } else if (s != NULL) { + tprint(s); + } +} + +@ The next subroutine prints a whole glue specification +@c +void print_spec(int p, const char *s) +{ /* prints a glue specification */ + if (p < 0) { + print_char('*'); + } else { + print_scaled(width(p)); + if (s != NULL) + tprint(s); + if (stretch(p) != 0) { + tprint(" plus "); + print_glue(stretch(p), stretch_order(p), s); + } + if (shrink(p) != 0) { + tprint(" minus "); + print_glue(shrink(p), shrink_order(p), s); + } + } +} + + +@ 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. +@^recursion@> + +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. + +@c +int font_in_short_display; /* an internal font number */ + + +@ Boxes, rules, inserts, whatsits, marks, and things in general that are +sort of ``complicated'' are indicated only by printing `\.{[]}'. + +@c +void print_font_identifier(internal_font_number f) +{ + str_number fonttext; + if (pdf_font_blink(f) == null_font) + fonttext = font_id_text(f); + else + fonttext = font_id_text(pdf_font_blink(f)); + if (fonttext > 0) { + print_esc(fonttext); + } else { + tprint_esc("FONT"); + if (pdf_font_blink(f) == null_font) + print_int(f); + else + print_int(pdf_font_blink(f)); + } + if (int_par(pdf_tracing_fonts_code) > 0) { + tprint(" ("); + print_font_name(f); + if (font_size(f) != font_dsize(f)) { + tprint("@@"); + print_scaled(font_size(f)); + tprint("pt"); + } + print_char(')'); + } else if (font_expand_ratio(f) != 0) { + tprint(" ("); + if (font_expand_ratio(f) > 0) + print_char('+'); + print_int(font_expand_ratio(f)); + print_char(')'); + } +} + +@ @c +void short_display(int p) +{ /* prints highlights of list |p| */ + while (p != null) { + if (is_char_node(p)) { + if (lig_ptr(p) != null) { + short_display(lig_ptr(p)); + } else { + if (font(p) != font_in_short_display) { + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + font_in_short_display = font(p); + } + print(character(p)); + } + } else { + /* Print a short indication of the contents of node |p| */ + print_short_node_contents(p); + } + p = vlink(p); + } +} + + +@ 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. + +@c +void print_font_and_char(int p) +{ /* prints |char_node| data */ + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + print(character(p)); +} + +@ @c +void print_mark(int p) +{ /* prints token list data in braces */ + print_char('{'); + if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) + tprint_esc("CLOBBERED."); + else + show_token_list(token_link(p), null, max_print_line - 10); + print_char('}'); +} + +@ @c +void print_rule_dimen(scaled d) +{ /* prints dimension in rule node */ + if (is_running(d)) + print_char('*'); + else + print_scaled(d); +} + + +@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive, +@^recursion@> +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. + +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. + +@c +int depth_threshold; /* maximum nesting depth in box displays */ +int breadth_max; /* maximum number of items shown at the same list level */ + + +@ The recursive machinery is started by calling |show_box|. + +@c +void show_box(halfword p) +{ + /* Assign the values |depth_threshold:=show_box_depth| and + |breadth_max:=show_box_breadth| */ + depth_threshold = int_par(show_box_depth_code); + breadth_max = int_par(show_box_breadth_code); + + if (breadth_max <= 0) + breadth_max = 5; + show_node_list(p); /* the show starts at |p| */ + print_ln(); +} + + +@ Helper for debugging purposes + +@c +void short_display_n(int p, int m) +{ /* prints highlights of list |p| */ + int i = 0; + font_in_short_display = null_font; + if (p == null) + return; + while (p != null) { + if (is_char_node(p)) { + if (p <= max_halfword) { + if (font(p) != font_in_short_display) { + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + font_in_short_display = font(p); + } + print(character(p)); + } + } else { + if ((type(p) == glue_node) || + (type(p) == disc_node) || + (type(p) == penalty_node) || + ((type(p) == kern_node) && (subtype(p) == explicit))) + incr(i); + if (i >= m) + return; + if (type(p) == disc_node) { + print_char('|'); + short_display(vlink(pre_break(p))); + print_char('|'); + short_display(vlink(post_break(p))); + print_char('|'); + } else { + /* Print a short indication of the contents of node |p| */ + print_short_node_contents(p); + } + } + p = vlink(p); + if (p == null) + return; + } + update_terminal(); +} + + +@ When debugging a macro package, it can be useful to see the exact +control sequence names in the format file. For example, if ten new +csnames appear, it's nice to know what they are, to help pinpoint where +they came from. (This isn't a truly ``basic'' printing procedure, but +that's a convenient module in which to put it.) + +@c +void print_csnames(int hstart, int hfinish) +{ + int h; + unsigned char *c, *l; + fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, + (int) hfinish); + for (h = hstart; h <= hfinish; h++) { + if (cs_text(h) > 0) { /* if have anything at this position */ + c = str_string(cs_text(h)); + l = c + str_length(cs_text(h)); + while (c < l) { + fputc(*c++, stderr); /* print the characters */ + } + fprintf(stderr, "|"); + } + } +} + + +@ A helper for printing file:line:error style messages. Look for a +filename in |full_source_filename_stack|, and if we fail to find +one fall back on the non-file:line:error style. + +@c +void print_file_line(void) +{ + int level; + level = in_open; + while ((level > 0) && (full_source_filename_stack[level] == 0)) + decr(level); + if (level == 0) { + tprint_nl("! "); + } else { + tprint_nl(""); + tprint(full_source_filename_stack[level]); + print_char(':'); + if (level == in_open) + print_int(line); + else + print_int(line_stack[iindex + 1 - (in_open - level)]); + tprint(": "); + } +} + + +@ \TeX\ 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: + +@c +void begin_diagnostic(void) +{ /* prepare to do some tracing */ + global_old_setting = selector; + if ((int_par(tracing_online_code) <= 0) && (selector == term_and_log)) { + decr(selector); + if (history == spotless) + history = warning_issued; + } +} + +@ @c +void end_diagnostic(boolean blank_line) +{ /* restore proper conditions after tracing */ + tprint_nl(""); + if (blank_line) + print_ln(); + selector = global_old_setting; +} + + +@ Of course we had better declare another global variable, if the previous +routines are going to work. + +@c +int global_old_setting; |