texprinting.c /size: 47 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7print_state_info lmt_print_state = {
8     .logfile               = NULL,
9     .loggable_info         = NULL,
10     .selector              = 0,
11     .tally                 = 0,
12     .terminal_offset       = 0,
13     .logfile_offset        = 0,
14     .new_string_line       = 0,
15     .trick_buffer          = { 0 },
16     .trick_count           = 0,
17     .first_count           = 0,
18     .saved_selector        = 0,
19     .font_in_short_display = 0,
20     .saved_logfile         = NULL,
21     .saved_logfile_offset  = 0,
22};
23
24/*tex
25
26    During the development of \LUAMETATEX\ reporting has been stepwise upgraded, for instance with more
27    abstract print functions and a formatter. Much more detail is shown and additional tracing options
28    have been added (like for marks, inserts, adjust, math, etc.). The format of the traditonal messages
29    was mostly kept (sometimes under paramameter control using a higher tracing value) but after reading
30    the nth ridiculous comment about logging in \LUATEX\ related to \CONTEXT\ I decided that it no
31    longer made sense to offer compatibility because it will never satisfy everyone and we want to move
32    on, so per spring 2022 we will see even further normalization and log compatility options get (are)
33    dropped. If there are inconsistencies left, assume they will be dealt with. It's all about being able
34    to recognize what gets logged. If someone longs for the old reporting, there are plenty alternative
35    engines available.
36
37    [where: ...] : all kind of tracing
38    {...}        : more traditional tex tracing
39    <...>        : if tracing (maybe)
40
41*/
42
43/*tex
44
45    Messages that are sent to a user's terminal and to the transcript-log file are produced by
46    several |print| procedures. These procedures will direct their output to a variety of places,
47    based on the setting of the global variable |selector|, which has the following possible values:
48
49    \startitemize
50
51    \startitem
52        |term_and_log|, the normal setting, prints on the terminal and on the transcript file.
53    \stopitem
54
55    \startitem
56        |log_only|, prints only on the transcript file.
57    \stopitem
58
59    \startitem
60        |term_only|, prints only on the terminal.
61    \stopitem
62
63    \startitem
64        |no_print|, doesn't print at all. This is used only in rare cases before the transcript
65        file is open.
66    \stopitem
67
68    \startitem
69        |pseudo|, puts output into a cyclic buffer that is used by the |show_context| routine; when
70        we get to that routine we shall discuss the reasoning behind this curious mode.
71    \stopitem
72
73    \startitem
74        |new_string|, appends the output to the current string in the string pool.
75    \stopitem
76
77    \startitem
78        0 to 15, prints on one of the sixteen files for |\write| output.
79    \stopitem
80
81    \stopitemize
82
83    The symbolic names |term_and_log|, etc., have been assigned numeric codes that satisfy the
84    convenient relations |no_print + 1 = term_only|, |no_print + 2 = log_only|, |term_only + 2 =
85    log_only + 1 = term_and_log|.
86
87    Three additional global variables, |tally| and |term_offset| and |file_offset|, record the
88    number of characters that have been printed since they were most recently cleared to zero. We
89    use |tally| to record the length of (possibly very long) stretches of printing; |term_offset|
90    and |file_offset|, on the other hand, keep track of how many characters have appeared so far on
91    the current line that has been output to the terminal or to the transcript file, respectively.
92
93    The state structure collects: |new_string_line| and |escape_controls|, the transcript handle of
94    a \TEX\ session: |log_file|, the target of a message: |selector|, the digits in a number being
95    output |dig[23]|, the number of characters recently printed |tally|, the number of characters
96    on the current terminal line |term_offset|, the number of characters on the current file line
97    |file_offset|, the circular buffer for pseudoprinting |trick_buf|, the threshold for
98    pseudoprinting (explained later) |trick_count|, another variable for pseudoprinting
99    |first_count|, a blocker for minor adjustments to |show_token_list| namely |inhibit_par_tokens|.
100
101    To end a line of text output, we call |print_ln|:
102
103*/
104
105void tex_print_ln(void)
106{
107    switch (lmt_print_state.selector) {
108        case no_print_selector_code:
109            break;
110        case terminal_selector_code:
111            fputc('\n', stdout);
112            lmt_print_state.terminal_offset = 0;
113            break;
114        case logfile_selector_code:
115            fputc('\n', lmt_print_state.logfile);
116            lmt_print_state.logfile_offset = 0;
117            break;
118        case terminal_and_logfile_selector_code:
119            fputc('\n', stdout);
120            fputc('\n', lmt_print_state.logfile);
121            lmt_print_state.terminal_offset = 0;
122            lmt_print_state.logfile_offset = 0;
123            break;
124        case pseudo_selector_code:
125            break;
126        case new_string_selector_code:
127            if (lmt_print_state.new_string_line > 0) {
128                tex_print_char(lmt_print_state.new_string_line);
129            }
130            break;
131        case luabuffer_selector_code:
132            lmt_newline_to_buffer();
133            break;
134        default:
135            break;
136    }
137    /*tex |tally| is not affected */
138}
139
140
141/*tex
142
143    The |print_char| procedure sends one byte to the desired destination. All printing comes through
144    |print_ln| or |print_char|, except for the case of |print_str| (see below).
145
146    The checking of the line length is an inheritance from previous engines and we dropped it here.
147    It doesn't make much sense nowadays. The same is true for escaping.
148
149    Incrementing the tally ... only needed in pseudo mode :
150
151*/
152
153void tex_print_char(int s)
154{
155    if (s < 0 || s > 255) {
156        tex_formatted_warning("print", "weird character %i", s);
157    } else {
158        switch (lmt_print_state.selector) {
159            case no_print_selector_code:
160                break;
161            case terminal_selector_code:
162                if (s == new_line_char_par) { 
163                    fputc('\n', stdout);
164                    lmt_print_state.terminal_offset = 0;
165                } else { 
166                    fputc(s, stdout);
167                    ++lmt_print_state.terminal_offset;
168                }
169                break;
170            case logfile_selector_code:
171                if (s == new_line_char_par) { 
172                    fputc('\n', lmt_print_state.logfile);
173                    lmt_print_state.logfile_offset = 0;
174                } else {
175                    fputc(s, lmt_print_state.logfile);
176                    ++lmt_print_state.logfile_offset;
177                }
178                break;
179            case terminal_and_logfile_selector_code:
180                if (s == new_line_char_par) { 
181                    fputc('\n', stdout);
182                    fputc('\n', lmt_print_state.logfile);
183                    lmt_print_state.terminal_offset = 0;
184                    lmt_print_state.logfile_offset = 0;
185                } else { 
186                    fputc(s, stdout);
187                    fputc(s, lmt_print_state.logfile);
188                    ++lmt_print_state.terminal_offset;
189                    ++lmt_print_state.logfile_offset;
190                }
191                break;
192            case pseudo_selector_code:
193                if (lmt_print_state.tally < lmt_print_state.trick_count) {
194                    lmt_print_state.trick_buffer[lmt_print_state.tally % lmt_error_state.line_limits.size] = (unsigned char) s;
195                }
196                ++lmt_print_state.tally;
197                break;
198            case new_string_selector_code:
199                tex_append_char((unsigned char) s);
200                break;
201            case luabuffer_selector_code:
202                lmt_char_to_buffer((char) s);
203                break;
204            default:
205                break;
206        }
207    }
208}
209
210/*tex
211
212    An entire string is output by calling |print|. Note that if we are outputting the single
213    standard \ASCII\ character |c|, we could call |print("c")|, since |"c" = 99| is the number of a
214    single-character string, as explained above. But |print_char("c")| is quicker, so \TEX\ goes
215    directly to the |print_char| routine when it knows that this is safe. (The present
216    implementation assumes that it is always safe to print a visible \ASCII\ character.)
217
218    The first 256 entries above the 17th unicode plane are used for a special trick: when \TEX\ has
219    to print items in that range, it will instead print the character that results from substracting
220    0x110000 from that value. This allows byte-oriented output to things like |\specials|. We dropped 
221    this feature because it was never used (we used it as part of experiments with \LUATEX). The old 
222    code branches can be found in the repository. 
223
224*/
225
226/* no_print terminal | logfile | terminal_and_logfile | pseudo | new_string | luabuffer */ 
227
228static void tex_aux_uprint(int s)
229{
230    /*tex We're not sure about this so it's disabled for now! */
231    /*
232    if ((print_state.selector > pseudo_selector_code)) {
233        / *tex internal strings are not expanded * /
234        print_char(s);
235        return;
236    }
237    */
238    if (s == new_line_char_par && lmt_print_state.selector < pseudo_selector_code) {
239        tex_print_ln();
240    } else if (s <= 0x7F) {
241        tex_print_char(s);
242    } else if (s <= 0x7FF) {
243        tex_print_char(0xC0 + (s / 0x40));
244        tex_print_char(0x80 + (s % 0x40));
245    } else if (s <= 0xFFFF) {
246        tex_print_char(0xE0 + (s / 0x1000));
247        tex_print_char(0x80 + ((s % 0x1000) / 0x40));
248        tex_print_char(0x80 + ((s % 0x1000) % 0x40));
249    } else {
250        tex_print_char(0xF0 + (s / 0x40000));
251        tex_print_char(0x80 + ((s % 0x40000) / 0x1000));
252        tex_print_char(0x80 + (((s % 0x40000) % 0x1000) / 0x40));
253        tex_print_char(0x80 + (((s % 0x40000) % 0x1000) % 0x40));
254    }
255}
256
257void tex_print_tex_str(int s)
258{
259    if (s >= lmt_string_pool_state.string_pool_data.ptr) {
260        tex_normal_warning("print", "bad string pointer");
261    } else if (s < cs_offset_value) {
262        if (s < 0) {
263            tex_normal_warning("print", "bad string offset");
264        } else {
265            tex_aux_uprint(s);
266        }
267    } else if (lmt_print_state.selector == new_string_selector_code) {
268        tex_append_string(str_string(s), (unsigned) str_length(s));
269    } else {
270        unsigned char *j = str_string(s);
271        for (unsigned i = 0; i < str_length(s); i++) {
272            tex_print_char(j[i]);
273        }
274    }
275}
276
277/*tex
278
279    The procedure |print_nl| is like |print|, but it makes sure that the string appears at the
280    beginning of a new line.
281
282*/
283
284void tex_print_nlp(void)
285{
286    if (lmt_print_state.new_string_line > 0) {
287        tex_print_char(lmt_print_state.new_string_line);
288    } else {
289        switch (lmt_print_state.selector) {
290             case terminal_selector_code:
291                 if (lmt_print_state.terminal_offset > 0) {
292                     fputc('\n', stdout);
293                     lmt_print_state.terminal_offset = 0;
294                 }
295                 break;
296             case logfile_selector_code:
297                 if (lmt_print_state.logfile_offset > 0) {
298                     fputc('\n', lmt_print_state.logfile);
299                     lmt_print_state.logfile_offset = 0;
300                 }
301                 break;
302             case terminal_and_logfile_selector_code:
303                 if (lmt_print_state.terminal_offset > 0) {
304                     fputc('\n', stdout);
305                     lmt_print_state.terminal_offset = 0;
306                 }
307                 if (lmt_print_state.logfile_offset > 0) {
308                     fputc('\n', lmt_print_state.logfile);
309                     lmt_print_state.logfile_offset = 0;
310                 }
311                 break;
312             case luabuffer_selector_code:
313                 lmt_newline_to_buffer();
314                 break;
315        }
316    }
317}
318
319/*tex
320
321    The |char *| versions of the same procedures. |print_str| is different because it uses
322    buffering, which works well because most of the output actually comes through |print_str|.
323
324*/
325
326void tex_print_str(const char *s)
327{
328    int logfile = 0;
329    int terminal = 0;
330    switch (lmt_print_state.selector) {
331        case no_print_selector_code:
332            return;
333        case terminal_selector_code:
334            terminal = 1;
335            break;
336        case logfile_selector_code:
337            logfile = 1;
338            break;
339        case terminal_and_logfile_selector_code:
340            logfile = 1;
341            terminal = 1;
342            break;
343        case pseudo_selector_code:
344            while ((*s) && (lmt_print_state.tally < lmt_print_state.trick_count)) {
345                lmt_print_state.trick_buffer[lmt_print_state.tally % lmt_error_state.line_limits.size] = (unsigned char) *s++;
346                lmt_print_state.tally++;
347            }
348            return;
349        case new_string_selector_code:
350            tex_append_string((const unsigned char *) s, (unsigned) strlen(s));
351            return;
352        case luabuffer_selector_code:
353            lmt_string_to_buffer(s);
354            return;
355        default:
356            return;
357    }
358    if (terminal || logfile) {
359        int len = (int) strlen(s);
360        if (logfile && ! lmt_fileio_state.log_opened) {
361            logfile = 0;
362        }
363        if (len > 0) {
364            int newline = s[len-1] == '\n';
365            if (logfile) {
366                fputs(s, lmt_print_state.logfile);
367                if (newline) {
368                    lmt_print_state.logfile_offset = 0;
369                } else {
370                    lmt_print_state.logfile_offset += len;
371                }
372            }
373            if (terminal) {
374                fputs(s, stdout);
375                if (newline) {
376                    lmt_print_state.terminal_offset = 0;
377                } else {
378                    lmt_print_state.terminal_offset += len;
379                }
380            }
381        }
382    }
383}
384
385/*tex
386
387    Here is the very first thing that \TEX\ prints: a headline that identifies the version number
388    and format package. The |term_offset| variable is temporarily incorrect, but the discrepancy is
389    not serious since we assume that the banner and format identifier together will occupy at most
390    |max_print_line| character positions. Well, we dropped that check in this variant.
391
392    Maybe we should drop printing the format identifier.
393
394*/
395
396void tex_print_banner(void)
397{
398    fprintf(
399        stdout,
400        "%s %s\n",
401        lmt_engine_state.luatex_banner,
402        lmt_engine_state.dump_name
403    );
404}
405
406void tex_print_log_banner(void)
407{
408    fprintf(
409        lmt_print_state.logfile,
410        "engine: %s, format id: %s, time stamp: %d-%d-%d %d:%d, startup file: %s, job name: %s",
411        lmt_engine_state.luatex_banner,
412        lmt_engine_state.dump_name,
413        year_par, month_par > 12 ? 0 : month_par, day_par, time_par / 60, time_par % 60,
414        lmt_engine_state.startup_filename ? lmt_engine_state.startup_filename : "-",
415        lmt_engine_state.startup_jobname ? lmt_engine_state.startup_jobname : "-"
416    );
417}
418
419void tex_print_version_banner(void)
420{
421    fputs(lmt_engine_state.luatex_banner, stdout);
422}
423
424/*tex
425
426    The procedure |print_esc| prints a string that is preceded by the user's escape character
427    (which is usually a backslash).
428
429*/
430
431void tex_print_tex_str_esc(strnumber s)
432{
433    /*tex Set variable |c| to the current escape character: */
434    int c = escape_char_par;
435    if (c >= 0) {
436        tex_print_tex_str(c);
437    }
438    if (s) {
439        tex_print_tex_str(s);
440    }
441}
442
443/*tex This prints escape character, then |s|. */
444
445void tex_print_str_esc(const char *s)
446{
447    /*tex Set variable |c| to the current escape character: */
448    int c = escape_char_par;
449    if (c >= 0) {
450        tex_print_tex_str(c);
451    }
452    if (s) {
453        tex_print_str(s);
454    }
455}
456
457/*tex
458
459    The following procedure, which prints out the decimal representation of a given integer |n|,
460    has been written carefully so that it works properly if |n = 0| or if |(-n)| would cause
461    overflow. It does not apply |mod| or |div| to negative arguments, since such operations are not
462    implemented consistently by all \PASCAL\ compilers.
463
464*/
465
466void tex_print_int(int n)
467{
468    /*tex In the end a 0..9 fast path works out best; using |sprintf| is slower. */
469    if (n < 0) {
470        tex_print_char('-');
471        n = -n; 
472    }
473    if (n >= 0 && n <= 9) { 
474        tex_print_char('0' + n);
475    } else if (n >= 0 && n <= 99) { 
476        tex_print_char('0' + n/10);
477        tex_print_char('0' + n%10);
478    } else { 
479        int k = 0;
480        unsigned char digits[24];
481//        if (n < 0) {
482//            tex_print_char('-');
483//            n = -n; 
484//        }
485        do {
486            digits[k] = '0' + (unsigned char) (n % 10);
487            n = n / 10;
488            ++k;
489        } while (n != 0);
490        while (k-- > 0) {
491            tex_print_char(digits[k]);
492        }
493    }
494}
495
496/*tex
497
498    Conversely, here is a procedure analogous to |print_int|. If the output of this procedure is
499    subsequently read by \TEX\ and converted by the |round_decimals| routine above, it turns out
500    that the original value will be reproduced exactly; the \quote {simplest} such decimal number
501    is output, but there is always at least one digit following the decimal point.
502
503    The invariant relation in the |repeat| loop is that a sequence of decimal digits yet to be
504    printed will yield the original number if and only if they form a fraction~$f$ in the range $s
505    - \delta \L10 \cdot 2^{16} f < s$. We can stop if and only if $f = 0$ satisfies this condition;
506    the loop will terminate before $s$ can possibly become zero.
507
508    The next one prints a scaled real, rounded to five digits.
509
510*/
511
512void tex_print_dimension(scaled s, int unit)
513{
514    if (s == 0) {
515        tex_print_str("0.0"); /* really .. just 0 is not ok for some applications */
516    } else {
517        /*tex The amount of allowable inaccuracy: */
518        scaled delta = 10;
519        char buffer[20] = { 0 } ;
520        int i = 0;
521        if (s < 0) {
522            /*tex Print the sign, if negative. */
523            tex_print_char('-');
524            s = -s;
525        }
526        /*tex Print the integer part. */
527        tex_print_int(s / unity);
528        buffer[i++] = '.';
529        s = 10 * (s % unity) + 5;
530        do {
531            if (delta > unity) {
532                /*tex Round the last digit, so: |s + 32768 - 50000| it is. */
533                s = s + 0x8000 - 50000;
534            }
535            buffer[i++] = (unsigned char) ('0' + (s / unity));
536            s = 10 * (s % unity);
537            delta *= 10;
538        } while (s > delta);
539     // buffer[i++] = '\0';
540        tex_print_str(buffer);
541    }
542    if (unit != no_unit) {
543        tex_print_unit(unit);
544    }
545}
546
547void tex_print_sparse_dimension(scaled s, int unit)
548{
549    if (s == 0) {
550        tex_print_char('0');
551    } else if (s == unity) {
552        tex_print_char('1');
553    } else {
554        /*tex The amount of allowable inaccuracy: */
555        scaled delta = 10;
556        char buffer[20];
557        int i = 0;
558        if (s < 0) {
559            /*tex Print the sign, if negative. */
560            tex_print_char('-');
561            /*tex So we trust it here while in printing int we mess around. */
562            s = -s; 
563        }
564        /*tex Print the integer part. */
565        tex_print_int(s / unity);
566        s = 10 * (s % unity) + 5;
567        do {
568            if (delta > unity) {
569                /*tex Round the last digit. */
570                s = s + 0100000 - 50000;
571            }
572            buffer[i++] = (unsigned char) ('0' + (s / unity));
573            s = 10 * (s % unity);
574            delta *= 10;
575        } while (s > delta);
576        if (i == 1 && buffer[i-1] == '0') {
577            /* no need */
578        } else { 
579            buffer[i++] = '\0';
580            tex_print_char('.');
581            tex_print_str(buffer);
582        }
583    }
584    if (unit != no_unit) {
585        tex_print_unit(unit);
586    }
587}
588
589/*tex 
590    Good enough.
591*/
592
593void tex_print_posit(halfword s)
594{
595    char b[32];
596    sprintf(b, "%.20g", tex_posit_to_double(s));
597    tex_print_str(b);
598}
599
600/*tex
601
602    Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. We have a few 
603    variants. Because we have bitsets that can give upto |0xFFFFFFFF| we treat the given integer
604    as an unsigned. 
605*/
606
607void tex_print_hex(long long sn)
608{
609    if (sn == 0) { 
610        tex_print_char('0');
611    } else { 
612        unsigned long long n = 0;
613        int k = 0;
614        unsigned char digits[24];
615        if (sn < 0) { 
616            tex_print_char('-');
617            n = (unsigned long long) -sn;
618        } else { 
619            n = (unsigned long long) sn;
620        }
621        do {
622            unsigned char d = (unsigned char) (n % 16);
623            if (d < 10) {
624                digits[k] = '0' + d;
625            } else {
626                digits[k] = 'A' - 10 + d;
627            }
628            n = n / 16;
629            ++k;
630        } while (n != 0);
631        while (k-- > 0) {
632            tex_print_char(digits[k]);
633        }
634    }
635}
636
637void tex_print_qhex(long long n)
638{
639    tex_print_char('"');
640    tex_print_hex(n);
641}
642
643void tex_print_uhex(long long n)
644{
645    tex_print_str("U+");
646    /* todo: loop */
647    if (n < 16) {
648        tex_print_char('0');
649    }
650    if (n < 256) {
651        tex_print_char('0');
652    }
653    if (n < 4096) {
654        tex_print_char('0');
655    }
656    tex_print_hex(n);
657}
658
659static void tex_print_xhex(long long n)
660{
661    tex_print_char('"');
662    /* todo: loop */
663    if (n < 0xF) {
664        tex_print_char('0');
665    }
666    if (n < 0xFF) {
667        tex_print_char('0');
668    }
669    if (n < 0xFFF) {
670        tex_print_char('0');
671    }
672    if (n < 0xFFFF) {
673        tex_print_char('0');
674    }
675    if (n < 0xFFFFF) {
676        tex_print_char('0');
677    }
678    if (n < 0xFFFFFF) {
679        tex_print_char('0');
680    }
681    if (n < 0xFFFFFFF) {
682        tex_print_char('0');
683    }
684    tex_print_hex(n);
685}
686
687/*tex
688
689    Roman numerals are produced by the |print_roman_int| routine. Readers who like puzzles might
690    enjoy trying to figure out how this tricky code works; therefore no explanation will be given.
691    Notice that 1990 yields |mcmxc|, not |mxm|.
692
693*/
694
695void tex_print_roman_int(int n)
696{
697    char mystery[] = "m2d5c2l5x2v5i";
698    char *j = (char *) mystery;
699    int v = 1000;
700    while (1) {
701        while (n >= v) {
702            tex_print_char(*j);
703            n = n - v;
704        }
705        if (n <= 0) {
706            /*tex nonpositive input produces no output */
707            return;
708        } else {
709            char *k = j + 2;
710            int u = v / (*(k - 1) - '0');
711            if (*(k - 1) == '2') {
712                k = k + 2;
713                u = u / (*(k - 1) - '0');
714            }
715            if (n + u >= v) {
716                tex_print_char(*k);
717                n = n + u;
718            } else {
719                j = j + 2;
720                v = v / (*(j - 1) - '0');
721            }
722        }
723    }
724}
725
726/*tex
727
728    The |print| subroutine will not print a string that is still being created. The following
729    procedure will.
730
731*/
732
733void tex_print_current_string(void)
734{
735    for (int j = 0; j < lmt_string_pool_state.string_temp_top; j++) {
736        tex_print_char(lmt_string_pool_state.string_temp[j++]);
737    }
738}
739
740/*tex
741
742    The procedure |print_cs| prints the name of a control sequence, given a pointer to its address
743    in |eqtb|. A space is printed after the name unless it is a single nonletter or an active
744    character. This procedure might be invoked with invalid data, so it is \quote {extra robust}.
745    The individual characters must be printed one at a time using |print|, since they may be
746    unprintable.
747
748*/
749
750void tex_print_cs_checked(halfword p)
751{
752    switch (tex_cs_state(p)) {
753        case cs_no_error:
754            {
755                strnumber t = cs_text(p);
756                if (t < 0 || t >= lmt_string_pool_state.string_pool_data.ptr) {
757                    tex_print_str(error_string_nonexistent(13));
758                } else if (tex_is_active_cs(t)) {
759                    tex_print_tex_str(active_cs_value(t));
760                } else {
761                    tex_print_tex_str_esc(t);
762                    if (! tex_single_letter(t) || (tex_get_cat_code(cat_code_table_par, aux_str2uni(str_string(t))) == letter_cmd)) {
763                        tex_print_char(' ');
764                    }
765                }
766            }
767            break;
768        case cs_null_error:
769            tex_print_str_esc("csname");
770            tex_print_str_esc("endcsname");
771            tex_print_char(' ');
772            break;
773        case cs_below_base_error:
774            tex_print_str(error_string_impossible(11));
775            break;
776        case cs_undefined_error:
777            tex_print_str_esc("undefined");
778            tex_print_char(' ');
779            break;
780        case cs_out_of_range_error:
781            tex_print_str(error_string_impossible(12));
782            break;
783    } 
784}
785
786/*tex
787
788    Here is a similar procedure; it avoids the error checks, and it never prints a space after the
789    control sequence. The other one doesn't even print the bogus cs.
790
791*/
792
793void tex_print_cs(halfword p)
794{
795    if (p == null_cs) {
796        tex_print_str_esc("csname");
797        tex_print_str_esc("endcsname");
798    } else {
799        strnumber t = cs_text(p);
800        if (tex_is_active_cs(t)) {
801            tex_print_tex_str(active_cs_value(t));
802        } else {
803            tex_print_tex_str_esc(t);
804        }
805    }
806}
807
808void tex_print_cs_name(halfword p)
809{
810    if (p != null_cs) {
811        strnumber t = cs_text(p);
812        if (tex_is_active_cs(t)) {
813            tex_print_tex_str(active_cs_value(t));
814        } else {
815            tex_print_tex_str(t);
816        }
817    }
818}
819
820/*tex
821
822    Then there is a subroutine that prints glue stretch and shrink, possibly followed by the name
823    of finite units:
824
825*/
826
827void tex_print_glue(scaled d, int order, int unit)
828{
829    tex_print_dimension(d, no_unit);
830    if ((order < normal_glue_order) || (order > filll_glue_order)) {
831        tex_print_str("foul");
832    } else if (order > normal_glue_order) {
833        tex_print_str("fi");
834        while (order > fi_glue_order) {
835            tex_print_char('l');
836            --order;
837        }
838    } else {
839        tex_print_unit(unit);
840    }
841}
842
843/*tex The next subroutine prints a whole glue specification. */
844
845void tex_print_unit(int unit)
846{
847    if (unit != no_unit) {
848        tex_print_str(unit == pt_unit ? "pt" : "mu");
849    }
850}
851
852void tex_print_spec(int p, int unit)
853{
854    if (p < 0) {
855        tex_print_char('*');
856    } else if (p == 0) {
857        tex_print_dimension(0, unit);
858    } else {
859        tex_print_dimension(glue_amount(p), unit);
860        if (glue_stretch(p)) {
861            tex_print_str(" plus ");
862            tex_print_glue(glue_stretch(p), glue_stretch_order(p), unit);
863        }
864        if (glue_shrink(p)) {
865            tex_print_str(" minus ");
866            tex_print_glue(glue_shrink(p), glue_shrink_order(p), unit);
867        }
868    }
869}
870
871void tex_print_fontspec(int p)
872{
873    tex_print_int(font_spec_identifier(p));
874    if (font_spec_scale(p) != unused_scale_value) {
875        tex_print_str(" scale ");
876        tex_print_int(font_spec_scale(p));
877    }
878    if (font_spec_x_scale(p) != unused_scale_value) {
879        tex_print_str(" xscale ");
880        tex_print_int(font_spec_x_scale(p));
881    }
882    if (font_spec_y_scale(p) != unused_scale_value) {
883        tex_print_str(" yscale ");
884        tex_print_int(font_spec_y_scale(p));
885    }
886    if (font_spec_slant(p)) {
887        tex_print_str(" slant ");
888        tex_print_int(font_spec_slant(p));
889    }
890    if (font_spec_weight(p)) {
891        tex_print_str(" weight ");
892        tex_print_int(font_spec_weight(p));
893    }
894}
895
896/*tex Math characters: */
897
898void tex_print_mathspec(int p)
899{
900    if (p) {
901        mathcodeval m = tex_get_math_spec(p);
902        tex_show_mathcode_value(m, node_subtype(p));
903    } else {
904        tex_print_str("[invalid mathspec]");
905    }
906}
907
908/*tex
909
910    We can reinforce our knowledge of the data structures just introduced by considering two
911    procedures that display a list in symbolic form. The first of these, called |short_display|, is
912    used in \quotation {overfull box} messages to give the top-level description of a list. The
913    other one, called |show_node_list|, prints a detailed description of exactly what is in the
914    data structure.
915
916    The philosophy of |short_display| is to ignore the fine points about exactly what is inside
917    boxes, except that ligatures and discretionary breaks are expanded. As a result,
918    |short_display| is a recursive procedure, but the recursion is never more than one level deep.
919
920    A global variable |font_in_short_display| keeps track of the font code that is assumed to be
921    present when |short_display| begins; deviations from this font will be printed.
922
923    Boxes, rules, inserts, whatsits, marks, and things in general that are sort of \quote
924    {complicated} are indicated only by printing |[]|.
925
926    We print a bit more than original \TEX. A value of 0 or 1 or any large value will behave the
927    same as before. The reason for this extension is that a |name| not always makes sense.
928
929    \starttyping
930    0   \foo xyz
931    1   \foo (bar)
932    2   <bar> xyz
933    3   <bar @ ..> xyz
934    4   <id>
935    5   <id: bar>
936    6   <id: bar @ ..> xyz
937    \stoptyping
938
939    This is no longer the case: we now always print a full specification. The |\tracingfonts|
940    register will be dropped. 
941
942*/
943
944void tex_print_char_identifier(halfword c) // todo: use string_print_format
945{
946    if (c <= 0x10FFFF) {
947        char b[10];
948        if ( (c >= 0x00E000 && c <= 0x00F8FF) || (c >= 0x0F0000 && c <= 0x0FFFFF) ||
949             (c >= 0x100000 && c <= 0x10FFFF) || (c >= 0x00D800 && c <= 0x00DFFF) ) {
950            sprintf(b, "0x%06X", c);
951            tex_print_str(b);
952        } else {
953            sprintf(b, "U+%06X", c);
954            tex_print_str(b);
955            tex_print_char(' ');
956            tex_print_tex_str(c);
957        }
958    }
959}
960
961void tex_print_font_identifier(halfword f)
962{
963    /*tex |< >| is less likely to clash with text parenthesis */
964    if (tex_is_valid_font(f)) {
965     // switch (tracing_fonts_par) {
966     //    case 0:
967     //    case 1:
968     //        if (font_original(f)) {
969     //            tex_print_format(font_original(f));
970     //        } else {
971     //            tex_print_format("font: %i", f);
972     //        }
973     //        if (tracing_fonts_par == 0) {
974     //            break;
975     //        } else if (font_size(f) == font_design_size(f)) {
976     //            tex_print_format(" (%s)", font_name(f));
977     //        } else {
978     //            tex_print_format(" (%s @ %D)", font_name(f), font_size(f), pt_unit);
979     //        }
980     //        break;
981     //    case 2:
982     //        tex_print_format("<%s>", font_name(f));
983     //        break;
984     //    case 3:
985     //        tex_print_format("<%s @ %D>", font_name(f), font_size(f), pt_unit);
986     //        break;
987     //    case 4:
988     //        tex_print_format("<%i>", f);
989     //        break;
990     //    case 5:
991     //        tex_print_format("<%i: %s>", f, font_name(f));
992     //        break;
993     // /* case 6: */
994     //    default:
995                tex_print_format("<%i: %s @ %p>", f, font_name(f), font_size(f));
996     //         break;
997     // }
998    } else {
999        tex_print_str("<*>");
1000    }
1001}
1002
1003void tex_print_font_specifier(halfword e)
1004{
1005    if (e && tex_is_valid_font(font_spec_identifier(e))) {
1006        tex_print_format("<%i: %i %i %i %i %i>", font_spec_identifier(e), font_spec_scale(e), font_spec_x_scale(e), font_spec_y_scale(e), font_spec_slant(e), font_spec_weight(e));
1007    } else {
1008        tex_print_str("<*>");
1009    }
1010}
1011
1012void tex_print_font(halfword f)
1013{
1014    if (! f) {
1015        tex_print_str("nullfont");
1016    } else if (tex_is_valid_font(f)) {
1017        tex_print_str(font_name(f));
1018     /* if (font_size(f) != font_design_size(f)) { */
1019            /*tex
1020                Nowadays this check for designsize is rather meaningless so we could as well
1021                always enter this branch. We can even make this while blob a callback.
1022            */
1023            tex_print_format(" at %p", font_size(f));
1024     /* } */
1025    } else {
1026        tex_print_str("nofont");
1027    }
1028}
1029
1030/*tex This prints highlights of list |p|. */
1031
1032void tex_short_display(halfword p)
1033{
1034    tex_print_levels();
1035    if (p) {
1036        tex_print_short_node_contents(p);
1037    } else {
1038        tex_print_str("empty list");
1039    }
1040}
1041
1042/*tex This prints token list data in braces. */
1043
1044void tex_print_token_list(const char *s, halfword p)
1045{
1046    tex_print_levels();
1047    tex_print_str("..");
1048    if (s) {
1049        tex_print_str(s);
1050        tex_print_char(' ');
1051    }
1052    tex_print_char('{');
1053    if ((p >= 0) && (p <= (int) lmt_token_memory_state.tokens_data.top)) {
1054        tex_show_token_list(p, 0, 0);
1055    } else {
1056        tex_print_str(error_string_clobbered(21));
1057    }
1058    tex_print_char('}');
1059}
1060
1061/*tex This prints dimensions of a rule node. */
1062
1063void tex_print_rule_dimension(scaled d)
1064{
1065    if (d == null_flag) {
1066        tex_print_char('*');
1067    } else {
1068        tex_print_dimension(d, pt_unit);
1069    }
1070}
1071
1072/*tex
1073
1074    Since boxes can be inside of boxes, |show_node_list| is inherently recursive, up to a given
1075    maximum number of levels. The history of nesting is indicated by the current string, which
1076    will be printed at the beginning of each line; the length of this string, namely |cur_length|,
1077    is the depth of nesting.
1078
1079    A global variable called |depth_threshold| is used to record the maximum depth of nesting for
1080    which |show_node_list| will show information. If we have |depth_threshold = 0|, for example,
1081    only the top level information will be given and no sublists will be traversed. Another global
1082    variable, called |breadth_max|, tells the maximum number of items to show at each level;
1083    |breadth_max| had better be positive, or you won't see anything.
1084
1085    The maximum nesting depth in box displays is kept in |depth_threshold| and the maximum number
1086    of items shown at the same list level in |breadth_max|.
1087
1088    The recursive machinery is started by calling |show_box|. Assign the values |depth_threshold :=
1089    show_box_depth| and |breadth_max := show_box_breadth|
1090
1091*/
1092
1093void tex_show_box(halfword p)
1094{
1095    /*tex the show starts at |p| */
1096    tex_show_node_list(p, show_box_depth_par, show_box_breadth_par);
1097    tex_print_ln();
1098}
1099
1100/*tex
1101
1102    \TEX\ is occasionally supposed to print diagnostic information that goes only into the
1103    transcript file, unless |tracing_online| is positive. Here are two routines that adjust the
1104    destination of print commands:
1105
1106*/
1107
1108void tex_begin_diagnostic(void)
1109{
1110    lmt_print_state.saved_selector = lmt_print_state.selector;
1111    if ((tracing_online_par <= 0) && (lmt_print_state.selector == terminal_and_logfile_selector_code)) {
1112        lmt_print_state.selector = logfile_selector_code;
1113        if (lmt_error_state.history == spotless) {
1114            lmt_error_state.history = warning_issued;
1115        }
1116    }
1117    tex_print_levels();
1118}
1119
1120/*tex Restore proper conditions after tracing. */
1121
1122void tex_end_diagnostic(void)
1123{
1124    tex_print_nlp();
1125    lmt_print_state.selector = lmt_print_state.saved_selector;
1126}
1127
1128static void tex_print_padding(void)
1129{
1130    switch (lmt_print_state.selector) {
1131        case terminal_selector_code:
1132            if (! odd(lmt_print_state.terminal_offset)) {
1133                tex_print_char(' ');
1134            }
1135            break;
1136        case logfile_selector_code:
1137        case terminal_and_logfile_selector_code:
1138            if (! odd(lmt_print_state.logfile_offset)) {
1139                tex_print_char(' ');
1140            }
1141            break;
1142        case luabuffer_selector_code:
1143            break;
1144    }
1145}
1146
1147void tex_print_levels(void)
1148{
1149    int l0 = tracing_levels_par;
1150    tex_print_nlp();
1151    if (l0 > 0) {
1152        int l1 = (l0 & tracing_levels_group) == tracing_levels_group;
1153        int l2 = (l0 & tracing_levels_input) == tracing_levels_input;
1154        int l4 = (l0 & tracing_levels_catcodes) == tracing_levels_catcodes;
1155        if (l1) {
1156            tex_print_int(cur_level);
1157            tex_print_char(':');
1158        }
1159        if (l2) {
1160            tex_print_int(lmt_input_state.input_stack_data.ptr);
1161            tex_print_char(':');
1162        }
1163        if (l4) {
1164            tex_print_int(cat_code_table_par);
1165            tex_print_char(':');
1166        }
1167        if (l1 || l2 || l4) {
1168            tex_print_char(' ');
1169        }
1170        tex_print_padding();
1171    }
1172}
1173
1174/* maybe %GROUP% where we scan upto [UPPER][%], so %G and %GR are also is ok
1175
1176    shared with error messages, so at some point we will merge:
1177
1178    %c   int       char
1179    %s  *char      string
1180    %q  *char      'string'
1181    %i   int       integer
1182    %e             backslash (tex escape)
1183    %C   int int   symbolic representation of cmd chr
1184    %E  *char      \cs
1185    %S   int       tex cs string
1186    %M   int       mode
1187    %T   int       tex string
1188    %%             percent
1189
1190    specific for print (I need to identify the rest)
1191
1192 !  %U   int       unicode
1193 !  %D   int       dimension
1194
1195 !  %B   int       badness
1196 !  %G   int       group
1197
1198 !  %L   int      (if) linenumber
1199
1200*/
1201
1202const char *tex_print_format_args(const char *format, va_list args)
1203{
1204    while (1) {
1205        int chr = *format++;
1206        switch (chr) {
1207            case '\0':
1208                return va_arg(args, char *);
1209            case '%':
1210                {
1211                    chr = *format++;
1212                    switch (chr) {
1213                        case '\0':
1214                            return va_arg(args, char *);
1215                        case 'c':
1216                            tex_print_char(va_arg(args, int));
1217                            break;
1218                        case 'd': /* detail */
1219                            tex_print_str(tex_aux_subtype_str(va_arg(args, int)));
1220                            break;
1221                        case 'e':
1222                            tex_print_str_esc(NULL);
1223                            break;
1224                        case 'i':
1225                            tex_print_int(va_arg(args, int));
1226                            break;
1227                        case 'l':
1228                            tex_print_levels();
1229                            break;
1230                        case 'n':
1231                            tex_print_extended_subtype(null, (quarterword) va_arg(args, int));
1232                            break;
1233                        case 'm':
1234                            tex_print_cs_checked(va_arg(args, int));
1235                            break;
1236                        case 's':
1237                            tex_print_str(va_arg(args, char *));
1238                            break;
1239                        case 'p':
1240                            tex_print_dimension(va_arg(args, scaled), pt_unit);
1241                            break;
1242                        case 'q':
1243                            tex_print_char('\'');
1244                            tex_print_str(va_arg(args, char *));
1245                            tex_print_char('\'');
1246                            break;
1247                        case 'x':
1248                            tex_print_qhex(va_arg(args, int));
1249                            break;
1250                        /*
1251                        case 'u':
1252                            tex_print_unit(va_arg(args, int));
1253                            break;
1254                        */
1255                        case 'B': /* badness */
1256                            {
1257                                scaled b = va_arg(args, halfword);
1258                                if (b == awful_bad) {
1259                                    tex_print_char('*');
1260                                } else {
1261                                    tex_print_int(b);
1262                                }
1263                                break;
1264                            }
1265                        case 'C':
1266                            {
1267                                int cmd = va_arg(args, int);
1268                                int val = va_arg(args, int);
1269                                tex_print_cmd_chr((singleword) cmd, val); /* inlining doesn't work */
1270                                break;
1271                            }
1272                        case 'D': /* dimension */
1273                            {
1274                                scaled s = va_arg(args, scaled);
1275                                int u = va_arg(args, int);
1276                                tex_print_dimension(s, u);
1277                                break;
1278                            }
1279                        case 'E':
1280                            tex_print_str_esc(va_arg(args, char *));
1281                            break;
1282                        case 'G':
1283                            {
1284                                halfword g = va_arg(args, int);
1285                                tex_print_group(g);
1286                                break;
1287                            }
1288                        case 'F':
1289                            {
1290                                halfword i = va_arg(args, int);
1291                                tex_print_font_identifier(i);
1292                                break;
1293                            }
1294                        case 'L':
1295                            {
1296                                /* typically used for if line */
1297                                halfword line = va_arg(args, int);
1298                                if (line) {
1299                                    tex_print_str(" entered on line ");
1300                                    tex_print_int(line);
1301                                }
1302                                break;
1303                            }
1304                        case 'N':
1305                            {
1306                                halfword node = va_arg(args, int);
1307                                if (node) {
1308                                    tex_print_str(lmt_interface.node_data[node_type(node)].name);
1309                                }
1310                                break;
1311                            }
1312                        case 'M':
1313                            {
1314                                halfword mode = va_arg(args, int);
1315                                tex_print_str(tex_string_mode(mode));
1316                                break;
1317                            }
1318                        case 'P':
1319                            {
1320                                 scaled total = va_arg(args, int);
1321                                 scaled stretch = va_arg(args, int);
1322                                 scaled fistretch = va_arg(args, int);
1323                                 scaled filstretch = va_arg(args, int);
1324                                 scaled fillstretch = va_arg(args, int);
1325                                 scaled filllstretch = va_arg(args, int);
1326                                 scaled shrink = va_arg(args, int);
1327                                 tex_print_dimension(total, pt_unit);
1328                                 if (stretch) {
1329                                     tex_print_str(" plus ");
1330                                     tex_print_dimension(stretch, pt_unit);
1331                                 } else if (fistretch) {
1332                                     tex_print_str(" plus ");
1333                                     tex_print_dimension(fistretch, no_unit);
1334                                     tex_print_str(" fi");
1335                                 } else if (filstretch) {
1336                                     tex_print_str(" plus ");
1337                                     tex_print_dimension(filstretch, no_unit);
1338                                     tex_print_str(" fil");
1339                                 } else if (fillstretch) {
1340                                     tex_print_str(" plus ");
1341                                     tex_print_dimension(fillstretch, no_unit);
1342                                     tex_print_str(" fill");
1343                                 } else if (filllstretch) {
1344                                     tex_print_str(" plus ");
1345                                     tex_print_dimension(fillstretch, no_unit);
1346                                     tex_print_str(" filll");
1347                                 }
1348                                 if (shrink) {
1349                                     tex_print_str(" minus ");
1350                                     tex_print_dimension(shrink, pt_unit);
1351                                 }
1352                                 break;
1353                            }
1354                        case 'Q':
1355                            {
1356                                scaled s = va_arg(args, scaled);
1357                                int u = va_arg(args, int);
1358                                tex_print_spec(s, u);
1359                                break;
1360                            }
1361                        case 'R':
1362                            {
1363                                halfword d = va_arg(args, int);
1364                                tex_print_rule_dimension(d);
1365                                break;
1366                            }
1367                        case 'S':
1368                            {
1369                                halfword cs = va_arg(args, int);
1370                                tex_print_cs(cs);
1371                                break;
1372                            }
1373                        case 'T':
1374                            {
1375                                strnumber s = va_arg(args, int);
1376                                tex_print_tex_str(s);
1377                                break;
1378                            }
1379                        case 'U':
1380                            {
1381                                halfword c = va_arg(args, int);
1382                                tex_print_uhex(c);
1383                                break;
1384                            }
1385                        case 'X':
1386                            { 
1387                                halfword x = va_arg(args, int);
1388                                tex_print_xhex(x);
1389                                break;
1390                            }
1391                        case '2':
1392                            {
1393                                halfword c = va_arg(args, int);
1394                                switch (c) {
1395                                    case direction_l2r : tex_print_str("l2r"); break;
1396                                    case direction_r2l : tex_print_str("r2l"); break;
1397                                    default            : tex_print_str("unset"); break;
1398                                }
1399                                break;
1400                            }
1401                        case '%':
1402                            tex_print_char('%');
1403                            break;
1404                     // case '[':
1405                     //     tex_begin_diagnostic();
1406                     //     tex_print_char('[');
1407                     //     break;
1408                     // case ']':
1409                     //     tex_print_char(']');
1410                     //     tex_end_diagnostic();
1411                     //     break;
1412                        default:
1413                            /* ignore bad one */
1414                            break;
1415                    }
1416                }
1417                break;
1418            case '\n':
1419            case '\r':
1420                tex_print_nlp();
1421                break;
1422            default:
1423                tex_print_char(chr); /* todo: utf */
1424                break;
1425        }
1426    }
1427}
1428
1429void tex_print_format(const char *format, ...)
1430{
1431    va_list args;
1432    va_start(args, format); /* hm, weird, no number */
1433    tex_print_format_args(format, args);
1434    va_end(args);
1435}
1436
1437/*tex
1438
1439    Group codes were introduced in \ETEX\ but have been extended in the meantime in \LUATEX\ and
1440    later again in \LUAMETATEX. We might have (even) more granularity in the future.
1441
1442    Todo: combine this with an array of struct(id,name,lua) ... a rainy day + stack of new cd's job.
1443
1444*/
1445
1446void tex_print_group(int e)
1447{
1448    int line = tex_saved_line_at_level();
1449    tex_print_str(lmt_interface.group_code_values[cur_group].name);
1450    if (cur_group != bottom_level_group) {
1451        tex_print_str(" group");
1452        if (line) {
1453            tex_print_str(e ? " entered at line " : " at line ");
1454            tex_print_int(line);
1455        }
1456    }
1457}
1458
1459void tex_print_message(const char *s)
1460{
1461    tex_print_nlp();
1462    tex_print_char('(');
1463    tex_print_str(s);
1464    tex_print_char(')');
1465    tex_print_nlp();
1466}
1467