texprinting.c /size: 47 Kb    last modification: 2025-02-21 11:03
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             // fwrite(s, sizeof(char), len, lmt_print_state.logfile);
368                if (newline) {
369                    lmt_print_state.logfile_offset = 0;
370                } else {
371                    lmt_print_state.logfile_offset += len;
372                }
373            }
374            if (terminal) {
375                fputs(s, stdout);
376             // fwrite(s, sizeof(char), len, stdout);
377                if (newline) {
378                    lmt_print_state.terminal_offset = 0;
379                } else {
380                    lmt_print_state.terminal_offset += len;
381                }
382            }
383        }
384    }
385}
386
387/*tex
388
389    Here is the very first thing that \TEX\ prints: a headline that identifies the version number
390    and format package. The |term_offset| variable is temporarily incorrect, but the discrepancy is
391    not serious since we assume that the banner and format identifier together will occupy at most
392    |max_print_line| character positions. Well, we dropped that check in this variant.
393
394    Maybe we should drop printing the format identifier.
395
396*/
397
398void tex_print_banner(void)
399{
400    fprintf(
401        stdout,
402        "%s %s\n",
403        lmt_engine_state.luatex_banner,
404        lmt_engine_state.dump_name
405    );
406}
407
408void tex_print_log_banner(void)
409{
410    fprintf(
411        lmt_print_state.logfile,
412        "engine: %s, format id: %s, time stamp: %d-%d-%d %d:%d, startup file: %s, job name: %s",
413        lmt_engine_state.luatex_banner,
414        lmt_engine_state.dump_name,
415        year_par, month_par > 12 ? 0 : month_par, day_par, time_par / 60, time_par % 60,
416        lmt_engine_state.startup_filename ? lmt_engine_state.startup_filename : "-",
417        lmt_engine_state.startup_jobname ? lmt_engine_state.startup_jobname : "-"
418    );
419}
420
421void tex_print_version_banner(void)
422{
423    fputs(lmt_engine_state.luatex_banner, stdout);
424}
425
426/*tex
427
428    The procedure |print_esc| prints a string that is preceded by the user's escape character
429    (which is usually a backslash).
430
431*/
432
433void tex_print_tex_str_esc(strnumber s)
434{
435    /*tex Set variable |c| to the current escape character: */
436    int c = escape_char_par;
437    if (c >= 0) {
438        tex_print_tex_str(c);
439    }
440    if (s) {
441        tex_print_tex_str(s);
442    }
443}
444
445/*tex This prints escape character, then |s|. */
446
447void tex_print_str_esc(const char *s)
448{
449    /*tex Set variable |c| to the current escape character: */
450    int c = escape_char_par;
451    if (c >= 0) {
452        tex_print_tex_str(c);
453    }
454    if (s) {
455        tex_print_str(s);
456    }
457}
458
459/*tex
460
461    The following procedure, which prints out the decimal representation of a given integer |n|,
462    has been written carefully so that it works properly if |n = 0| or if |(-n)| would cause
463    overflow. It does not apply |mod| or |div| to negative arguments, since such operations are not
464    implemented consistently by all \PASCAL\ compilers.
465
466*/
467
468void tex_print_int(int n)
469{
470    /*tex In the end a 0..9 fast path works out best; using |sprintf| is slower. */
471    if (n < 0) {
472        tex_print_char('-');
473        n = -n; 
474    }
475    if (n >= 0 && n <= 9) { 
476        tex_print_char('0' + n);
477    } else if (n >= 0 && n <= 99) { 
478        tex_print_char('0' + n/10);
479        tex_print_char('0' + n%10);
480    } else { 
481        int k = 0;
482        unsigned char digits[24];
483//        if (n < 0) {
484//            tex_print_char('-');
485//            n = -n; 
486//        }
487        do {
488            digits[k] = '0' + (unsigned char) (n % 10);
489            n = n / 10;
490            ++k;
491        } while (n != 0);
492        while (k-- > 0) {
493            tex_print_char(digits[k]);
494        }
495    }
496}
497
498/*tex
499
500    Conversely, here is a procedure analogous to |print_int|. If the output of this procedure is
501    subsequently read by \TEX\ and converted by the |round_decimals| routine above, it turns out
502    that the original value will be reproduced exactly; the \quote {simplest} such decimal number
503    is output, but there is always at least one digit following the decimal point.
504
505    The invariant relation in the |repeat| loop is that a sequence of decimal digits yet to be
506    printed will yield the original number if and only if they form a fraction~$f$ in the range $s
507    - \delta \L10 \cdot 2^{16} f < s$. We can stop if and only if $f = 0$ satisfies this condition;
508    the loop will terminate before $s$ can possibly become zero.
509
510    The next one prints a scaled real, rounded to five digits.
511
512*/
513
514void tex_print_dimension(scaled s, int unit)
515{
516    if (s == 0) {
517        tex_print_str("0.0"); /* really .. just 0 is not ok for some applications */
518    } else {
519        /*tex The amount of allowable inaccuracy: */
520        scaled delta = 10;
521        char buffer[20] = { 0 } ;
522        int i = 0;
523        if (s < 0) {
524            /*tex Print the sign, if negative. */
525            tex_print_char('-');
526            s = -s;
527        }
528        /*tex Print the integer part. */
529        tex_print_int(s / unity);
530        buffer[i++] = '.';
531        s = 10 * (s % unity) + 5;
532        do {
533            if (delta > unity) {
534                /*tex Round the last digit, so: |s + 32768 - 50000| it is. */
535                s = s + 0x8000 - 50000;
536            }
537            buffer[i++] = (unsigned char) ('0' + (s / unity));
538            s = 10 * (s % unity);
539            delta *= 10;
540        } while (s > delta);
541     // buffer[i++] = '\0';
542        tex_print_str(buffer);
543    }
544    if (unit != no_unit) {
545        tex_print_unit(unit);
546    }
547}
548
549void tex_print_sparse_dimension(scaled s, int unit)
550{
551    if (s == 0) {
552        tex_print_char('0');
553    } else if (s == unity) {
554        tex_print_char('1');
555    } else {
556        /*tex The amount of allowable inaccuracy: */
557        scaled delta = 10;
558        char buffer[20];
559        int i = 0;
560        if (s < 0) {
561            /*tex Print the sign, if negative. */
562            tex_print_char('-');
563            /*tex So we trust it here while in printing int we mess around. */
564            s = -s; 
565        }
566        /*tex Print the integer part. */
567        tex_print_int(s / unity);
568        s = 10 * (s % unity) + 5;
569        do {
570            if (delta > unity) {
571                /*tex Round the last digit. */
572                s = s + 0100000 - 50000;
573            }
574            buffer[i++] = (unsigned char) ('0' + (s / unity));
575            s = 10 * (s % unity);
576            delta *= 10;
577        } while (s > delta);
578        if (i == 1 && buffer[i-1] == '0') {
579            /* no need */
580        } else { 
581            buffer[i++] = '\0';
582            tex_print_char('.');
583            tex_print_str(buffer);
584        }
585    }
586    if (unit != no_unit) {
587        tex_print_unit(unit);
588    }
589}
590
591/*tex 
592    Good enough.
593*/
594
595void tex_print_posit(halfword s)
596{
597    char b[32];
598    sprintf(b, "%.20g", tex_posit_to_double(s));
599    tex_print_str(b);
600}
601
602/*tex
603
604    Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. We have a few 
605    variants. Because we have bitsets that can give upto |0xFFFFFFFF| we treat the given integer
606    as an unsigned. 
607*/
608
609void tex_print_hex(long long sn)
610{
611    if (sn == 0) { 
612        tex_print_char('0');
613    } else { 
614        unsigned long long n = 0;
615        int k = 0;
616        unsigned char digits[24];
617        if (sn < 0) { 
618            tex_print_char('-');
619            n = (unsigned long long) -sn;
620        } else { 
621            n = (unsigned long long) sn;
622        }
623        do {
624            unsigned char d = (unsigned char) (n % 16);
625            if (d < 10) {
626                digits[k] = '0' + d;
627            } else {
628                digits[k] = 'A' - 10 + d;
629            }
630            n = n / 16;
631            ++k;
632        } while (n != 0);
633        while (k-- > 0) {
634            tex_print_char(digits[k]);
635        }
636    }
637}
638
639void tex_print_qhex(long long n)
640{
641    tex_print_char('"');
642    tex_print_hex(n);
643}
644
645void tex_print_uhex(long long n)
646{
647    tex_print_str("U+");
648    /* todo: loop */
649    if (n < 16) {
650        tex_print_char('0');
651    }
652    if (n < 256) {
653        tex_print_char('0');
654    }
655    if (n < 4096) {
656        tex_print_char('0');
657    }
658    tex_print_hex(n);
659}
660
661static void tex_print_xhex(long long n)
662{
663    tex_print_char('"');
664    /* todo: loop */
665    if (n < 0xF) {
666        tex_print_char('0');
667    }
668    if (n < 0xFF) {
669        tex_print_char('0');
670    }
671    if (n < 0xFFF) {
672        tex_print_char('0');
673    }
674    if (n < 0xFFFF) {
675        tex_print_char('0');
676    }
677    if (n < 0xFFFFF) {
678        tex_print_char('0');
679    }
680    if (n < 0xFFFFFF) {
681        tex_print_char('0');
682    }
683    if (n < 0xFFFFFFF) {
684        tex_print_char('0');
685    }
686    tex_print_hex(n);
687}
688
689/*tex
690
691    Roman numerals are produced by the |print_roman_int| routine. Readers who like puzzles might
692    enjoy trying to figure out how this tricky code works; therefore no explanation will be given.
693    Notice that 1990 yields |mcmxc|, not |mxm|.
694
695*/
696
697void tex_print_roman_int(int n)
698{
699    char mystery[] = "m2d5c2l5x2v5i";
700    char *j = (char *) mystery;
701    int v = 1000;
702    while (1) {
703        while (n >= v) {
704            tex_print_char(*j);
705            n = n - v;
706        }
707        if (n <= 0) {
708            /*tex nonpositive input produces no output */
709            return;
710        } else {
711            char *k = j + 2;
712            int u = v / (*(k - 1) - '0');
713            if (*(k - 1) == '2') {
714                k = k + 2;
715                u = u / (*(k - 1) - '0');
716            }
717            if (n + u >= v) {
718                tex_print_char(*k);
719                n = n + u;
720            } else {
721                j = j + 2;
722                v = v / (*(j - 1) - '0');
723            }
724        }
725    }
726}
727
728/*tex
729
730    The |print| subroutine will not print a string that is still being created. The following
731    procedure will.
732
733*/
734
735void tex_print_current_string(void)
736{
737    for (int j = 0; j < lmt_string_pool_state.string_temp_top; j++) {
738        tex_print_char(lmt_string_pool_state.string_temp[j++]);
739    }
740}
741
742/*tex
743
744    The procedure |print_cs| prints the name of a control sequence, given a pointer to its address
745    in |eqtb|. A space is printed after the name unless it is a single nonletter or an active
746    character. This procedure might be invoked with invalid data, so it is \quote {extra robust}.
747    The individual characters must be printed one at a time using |print|, since they may be
748    unprintable.
749
750*/
751
752void tex_print_cs_checked(halfword p)
753{
754    switch (tex_cs_state(p)) {
755        case cs_no_error:
756            {
757                strnumber t = cs_text(p);
758                if (t < 0 || t >= lmt_string_pool_state.string_pool_data.ptr) {
759                    tex_print_str(error_string_nonexistent(13));
760                } else if (tex_is_active_cs(t)) {
761                    tex_print_tex_str(active_cs_value(t));
762                } else {
763                    tex_print_tex_str_esc(t);
764                    if (! tex_single_letter(t) || (tex_get_cat_code(cat_code_table_par, aux_str2uni(str_string(t))) == letter_cmd)) {
765                        tex_print_char(' ');
766                    }
767                }
768            }
769            break;
770        case cs_null_error:
771            tex_print_str_esc("csname");
772            tex_print_str_esc("endcsname");
773            tex_print_char(' ');
774            break;
775        case cs_below_base_error:
776            tex_print_str(error_string_impossible(11));
777            break;
778        case cs_undefined_error:
779            tex_print_str_esc("undefined");
780            tex_print_char(' ');
781            break;
782        case cs_out_of_range_error:
783            tex_print_str(error_string_impossible(12));
784            break;
785    } 
786}
787
788/*tex
789
790    Here is a similar procedure; it avoids the error checks, and it never prints a space after the
791    control sequence. The other one doesn't even print the bogus cs.
792
793*/
794
795void tex_print_cs(halfword p)
796{
797    if (p == null_cs) {
798        tex_print_str_esc("csname");
799        tex_print_str_esc("endcsname");
800    } else {
801        strnumber t = cs_text(p);
802        if (tex_is_active_cs(t)) {
803            tex_print_tex_str(active_cs_value(t));
804        } else {
805            tex_print_tex_str_esc(t);
806        }
807    }
808}
809
810void tex_print_cs_name(halfword p)
811{
812    if (p != null_cs) {
813        strnumber t = cs_text(p);
814        if (tex_is_active_cs(t)) {
815            tex_print_tex_str(active_cs_value(t));
816        } else {
817            tex_print_tex_str(t);
818        }
819    }
820}
821
822/*tex
823
824    Then there is a subroutine that prints glue stretch and shrink, possibly followed by the name
825    of finite units:
826
827*/
828
829void tex_print_glue(scaled d, int order, int unit)
830{
831    tex_print_dimension(d, no_unit);
832    if ((order < normal_glue_order) || (order > filll_glue_order)) {
833        /*tex For sure this will crash someplace becuase it is also used as index! */
834        tex_print_str("foul");
835    } else if (order > normal_glue_order) {
836        tex_print_str("fi");
837        while (order > fi_glue_order) {
838            tex_print_char('l');
839            --order;
840        }
841    } else {
842        tex_print_unit(unit);
843    }
844}
845
846/*tex The next subroutine prints a whole glue specification. */
847
848void tex_print_unit(int unit)
849{
850    if (unit != no_unit) {
851        tex_print_str(unit == pt_unit ? "pt" : "mu");
852    }
853}
854
855void tex_print_spec(int p, int unit)
856{
857    if (p < 0) {
858        tex_print_char('*');
859    } else if (p == 0) {
860        tex_print_dimension(0, unit);
861    } else {
862        tex_print_dimension(glue_amount(p), unit);
863        if (glue_stretch(p)) {
864            tex_print_str(" plus ");
865            tex_print_glue(glue_stretch(p), glue_stretch_order(p), unit);
866        }
867        if (glue_shrink(p)) {
868            tex_print_str(" minus ");
869            tex_print_glue(glue_shrink(p), glue_shrink_order(p), unit);
870        }
871    }
872}
873
874void tex_print_fontspec(int p)
875{
876    tex_print_int(font_spec_identifier(p));
877    if (font_spec_scale(p) != unused_scale_value) {
878        tex_print_str(" scale ");
879        tex_print_int(font_spec_scale(p));
880    }
881    if (font_spec_x_scale(p) != unused_scale_value) {
882        tex_print_str(" xscale ");
883        tex_print_int(font_spec_x_scale(p));
884    }
885    if (font_spec_y_scale(p) != unused_scale_value) {
886        tex_print_str(" yscale ");
887        tex_print_int(font_spec_y_scale(p));
888    }
889    if (font_spec_slant(p)) {
890        tex_print_str(" slant ");
891        tex_print_int(font_spec_slant(p));
892    }
893    if (font_spec_weight(p)) {
894        tex_print_str(" weight ");
895        tex_print_int(font_spec_weight(p));
896    }
897}
898
899/*tex Math characters: */
900
901void tex_print_mathspec(int p)
902{
903    if (p) {
904        mathcodeval m = tex_get_math_spec(p);
905        tex_show_mathcode_value(m, node_subtype(p));
906    } else {
907        tex_print_str("[invalid mathspec]");
908    }
909}
910
911/*tex
912
913    We can reinforce our knowledge of the data structures just introduced by considering two
914    procedures that display a list in symbolic form. The first of these, called |short_display|, is
915    used in \quotation {overfull box} messages to give the top-level description of a list. The
916    other one, called |show_node_list|, prints a detailed description of exactly what is in the
917    data structure.
918
919    The philosophy of |short_display| is to ignore the fine points about exactly what is inside
920    boxes, except that ligatures and discretionary breaks are expanded. As a result,
921    |short_display| is a recursive procedure, but the recursion is never more than one level deep.
922
923    A global variable |font_in_short_display| keeps track of the font code that is assumed to be
924    present when |short_display| begins; deviations from this font will be printed.
925
926    Boxes, rules, inserts, whatsits, marks, and things in general that are sort of \quote
927    {complicated} are indicated only by printing |[]|.
928
929    We print a bit more than original \TEX. A value of 0 or 1 or any large value will behave the
930    same as before. The reason for this extension is that a |name| not always makes sense.
931
932    \starttyping
933    0   \foo xyz
934    1   \foo (bar)
935    2   <bar> xyz
936    3   <bar @ ..> xyz
937    4   <id>
938    5   <id: bar>
939    6   <id: bar @ ..> xyz
940    \stoptyping
941
942    This is no longer the case: we now always print a full specification. The |\tracingfonts|
943    register will be dropped. 
944
945*/
946
947void tex_print_char_identifier(halfword c) // todo: use string_print_format
948{
949    if (c <= 0x10FFFF) {
950        char b[10];
951        if ( (c >= 0x00E000 && c <= 0x00F8FF) || (c >= 0x0F0000 && c <= 0x0FFFFF) ||
952             (c >= 0x100000 && c <= 0x10FFFF) || (c >= 0x00D800 && c <= 0x00DFFF) ) {
953            sprintf(b, "0x%06X", c);
954            tex_print_str(b);
955        } else {
956            sprintf(b, "U+%06X", c);
957            tex_print_str(b);
958            tex_print_char(' ');
959            tex_print_tex_str(c);
960        }
961    }
962}
963
964void tex_print_font_identifier(halfword f)
965{
966    /*tex |< >| is less likely to clash with text parenthesis */
967    if (f < 0) { 
968        f = cur_font_par; /* bonus */
969    }
970    if (tex_is_valid_font(f)) {
971        tex_print_format("<%i: %s @ %p>", f, font_name(f), font_size(f));
972    } else {
973        tex_print_str("<*>");
974    }
975}
976
977void tex_print_font_specifier(halfword e)
978{
979    if (e && tex_is_valid_font(font_spec_identifier(e))) {
980        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));
981    } else {
982        tex_print_str("<*>");
983    }
984}
985
986void tex_print_font(halfword f)
987{
988    if (f < 0) { 
989        f = cur_font_par; /* bonus */
990    }
991    if (! f) {
992        tex_print_str("nullfont");
993    } else if (tex_is_valid_font(f)) {
994        tex_print_str(font_name(f));
995     /* if (font_size(f) != font_design_size(f)) { */
996            /*tex
997                Nowadays this check for designsize is rather meaningless so we could as well
998                always enter this branch. We can even make this while blob a callback.
999            */
1000            tex_print_format(" at %p", font_size(f));
1001     /* } */
1002    } else {
1003        tex_print_str("nofont");
1004    }
1005}
1006
1007/*tex This prints highlights of list |p|. */
1008
1009void tex_short_display(halfword p)
1010{
1011 // tex_print_levels();
1012    if (p) {
1013        tex_print_short_node_contents(p);
1014    } else {
1015        tex_print_str("[empty list]");
1016    }
1017}
1018
1019/*tex This prints token list data in braces. */
1020
1021void tex_print_token_list(const char *s, halfword p)
1022{
1023    tex_print_levels();
1024    tex_print_str("..");
1025    if (s) {
1026        tex_print_str(s);
1027        tex_print_char(' ');
1028    }
1029    tex_print_char('{');
1030    if ((p >= 0) && (p <= (int) lmt_token_memory_state.tokens_data.top)) {
1031        tex_show_token_list(p, 0, 0);
1032    } else {
1033        tex_print_str(error_string_clobbered(21));
1034    }
1035    tex_print_char('}');
1036}
1037
1038/*tex This prints dimensions of a rule node. */
1039
1040void tex_print_rule_dimension(scaled d)
1041{
1042    if (d == null_flag) {
1043        tex_print_char('*');
1044    } else {
1045        tex_print_dimension(d, pt_unit);
1046    }
1047}
1048
1049/*tex
1050
1051    Since boxes can be inside of boxes, |show_node_list| is inherently recursive, up to a given
1052    maximum number of levels. The history of nesting is indicated by the current string, which
1053    will be printed at the beginning of each line; the length of this string, namely |cur_length|,
1054    is the depth of nesting.
1055
1056    A global variable called |depth_threshold| is used to record the maximum depth of nesting for
1057    which |show_node_list| will show information. If we have |depth_threshold = 0|, for example,
1058    only the top level information will be given and no sublists will be traversed. Another global
1059    variable, called |breadth_max|, tells the maximum number of items to show at each level;
1060    |breadth_max| had better be positive, or you won't see anything.
1061
1062    The maximum nesting depth in box displays is kept in |depth_threshold| and the maximum number
1063    of items shown at the same list level in |breadth_max|.
1064
1065    The recursive machinery is started by calling |show_box|. Assign the values |depth_threshold :=
1066    show_box_depth| and |breadth_max := show_box_breadth|
1067
1068*/
1069
1070void tex_show_box(halfword p)
1071{
1072    /*tex the show starts at |p| */
1073    tex_show_node_list(p, show_box_depth_par, show_box_breadth_par);
1074    tex_print_ln();
1075}
1076
1077/*tex
1078
1079    \TEX\ is occasionally supposed to print diagnostic information that goes only into the
1080    transcript file, unless |tracing_online| is positive. Here are two routines that adjust the
1081    destination of print commands:
1082
1083*/
1084
1085void tex_begin_diagnostic(void)
1086{
1087    lmt_print_state.saved_selector = lmt_print_state.selector;
1088    if ((tracing_online_par <= 0) && (lmt_print_state.selector == terminal_and_logfile_selector_code)) {
1089        lmt_print_state.selector = logfile_selector_code;
1090        if (lmt_error_state.history == spotless) {
1091            lmt_error_state.history = warning_issued;
1092        }
1093    }
1094    tex_print_levels();
1095}
1096
1097/*tex Restore proper conditions after tracing. */
1098
1099void tex_end_diagnostic(void)
1100{
1101    tex_print_nlp();
1102    lmt_print_state.selector = lmt_print_state.saved_selector;
1103}
1104
1105static void tex_print_padding(void)
1106{
1107    switch (lmt_print_state.selector) {
1108        case terminal_selector_code:
1109            if (! odd(lmt_print_state.terminal_offset)) {
1110                tex_print_char(' ');
1111            }
1112            break;
1113        case logfile_selector_code:
1114        case terminal_and_logfile_selector_code:
1115            if (! odd(lmt_print_state.logfile_offset)) {
1116                tex_print_char(' ');
1117            }
1118            break;
1119        case luabuffer_selector_code:
1120            break;
1121    }
1122}
1123
1124void tex_print_levels(void)
1125{
1126    int l0 = tracing_levels_par;
1127    tex_print_nlp();
1128    if (l0 > 0) {
1129        int l1 = (l0 & tracing_levels_group) == tracing_levels_group;
1130        int l2 = (l0 & tracing_levels_input) == tracing_levels_input;
1131        int l4 = (l0 & tracing_levels_catcodes) == tracing_levels_catcodes;
1132        if (l1) {
1133            tex_print_int(cur_level);
1134            tex_print_char(':');
1135        }
1136        if (l2) {
1137            tex_print_int(lmt_input_state.input_stack_data.ptr);
1138            tex_print_char(':');
1139        }
1140        if (l4) {
1141            tex_print_int(cat_code_table_par);
1142            tex_print_char(':');
1143        }
1144        if (l1 || l2 || l4) {
1145            tex_print_char(' ');
1146        }
1147        tex_print_padding();
1148    }
1149}
1150
1151/* maybe %GROUP% where we scan upto [UPPER][%], so %G and %GR are also is ok
1152
1153    shared with error messages, so at some point we will merge:
1154
1155    %c   int       char
1156    %s  *char      string
1157    %q  *char      'string'
1158    %i   int       integer
1159    %e             backslash (tex escape)
1160    %C   int int   symbolic representation of cmd chr
1161    %E  *char      \cs
1162    %S   int       tex cs string
1163    %M   int       mode
1164    %T   int       tex string
1165    %%             percent
1166
1167    specific for print (I need to identify the rest)
1168
1169 !  %U   int       unicode
1170 !  %D   int       dimension
1171
1172 !  %B   int       badness
1173 !  %G   int       group
1174
1175 !  %L   int      (if) linenumber
1176
1177*/
1178
1179const char *tex_print_format_args(const char *format, va_list args)
1180{
1181    while (1) {
1182        int chr = *format++;
1183        switch (chr) {
1184            case '\0':
1185                return va_arg(args, char *);
1186            case '%':
1187                {
1188                    chr = *format++;
1189                    switch (chr) {
1190                        case '\0':
1191                            return va_arg(args, char *);
1192                        case 'c':
1193                            tex_print_char(va_arg(args, int));
1194                            break;
1195                        case 'd': /* detail */
1196                            tex_print_str(tex_aux_subtype_str(va_arg(args, int)));
1197                            break;
1198                        case 'e':
1199                            tex_print_str_esc(NULL);
1200                            break;
1201                        case 'i':
1202                            tex_print_int(va_arg(args, int));
1203                            break;
1204                        case 'l':
1205                            tex_print_levels();
1206                            break;
1207                        case 'n':
1208                            tex_print_extended_subtype(null, (quarterword) va_arg(args, int));
1209                            break;
1210                        case 'm':
1211                            tex_print_cs_checked(va_arg(args, int));
1212                            break;
1213                        case 's':
1214                            tex_print_str(va_arg(args, char *));
1215                            break;
1216                        case 'p':
1217                            tex_print_dimension(va_arg(args, scaled), pt_unit);
1218                            break;
1219                        case 'q':
1220                            tex_print_char('\'');
1221                            tex_print_str(va_arg(args, char *));
1222                            tex_print_char('\'');
1223                            break;
1224                        case 'x':
1225                            tex_print_qhex(va_arg(args, int));
1226                            break;
1227                        /*
1228                        case 'u':
1229                            tex_print_unit(va_arg(args, int));
1230                            break;
1231                        */
1232                        case 'u':
1233                            {
1234                                unsigned char s[6] = { 0 };
1235                                unsigned char *b = s;
1236                                b = aux_uni2str(va_arg(args, int));
1237                                tex_print_format(", %s", b);
1238                                break;
1239                            }                       
1240                        case 'B': /* badness */
1241                            {
1242                                scaled b = va_arg(args, halfword);
1243                                if (b == awful_bad) {
1244                                    tex_print_char('*');
1245                                } else {
1246                                    tex_print_int(b);
1247                                }
1248                                break;
1249                            }
1250                        case 'C':
1251                            {
1252                                int cmd = va_arg(args, int);
1253                                int val = va_arg(args, int);
1254                                tex_print_cmd_chr((singleword) cmd, val); /* inlining doesn't work */
1255                                break;
1256                            }
1257                        case 'D': /* dimension */
1258                            {
1259                                scaled s = va_arg(args, scaled);
1260                                int u = va_arg(args, int);
1261                                tex_print_dimension(s, u);
1262                                break;
1263                            }
1264                        case 'E':
1265                            tex_print_str_esc(va_arg(args, char *));
1266                            break;
1267                        case 'G':
1268                            {
1269                                halfword g = va_arg(args, int);
1270                                tex_print_group(g);
1271                                break;
1272                            }
1273                        case 'F':
1274                            {
1275                                halfword i = va_arg(args, int);
1276                                tex_print_font_identifier(i);
1277                                break;
1278                            }
1279                        case 'L':
1280                            {
1281                                /* typically used for if line */
1282                                halfword line = va_arg(args, int);
1283                                if (line) {
1284                                    tex_print_str(" entered on line ");
1285                                    tex_print_int(line);
1286                                }
1287                                break;
1288                            }
1289                        case 'N':
1290                            {
1291                                halfword node = va_arg(args, int);
1292                                if (node) {
1293                                    tex_print_str(lmt_interface.node_data[node_type(node)].name);
1294                                }
1295                                break;
1296                            }
1297                        case 'M':
1298                            {
1299                                halfword mode = va_arg(args, int);
1300                                tex_print_str(tex_string_mode(mode));
1301                                break;
1302                            }
1303                        case 'P':
1304                            {
1305                                 scaled total = va_arg(args, int);
1306                                 scaled stretch = va_arg(args, int);
1307                                 scaled fistretch = va_arg(args, int);
1308                                 scaled filstretch = va_arg(args, int);
1309                                 scaled fillstretch = va_arg(args, int);
1310                                 scaled filllstretch = va_arg(args, int);
1311                                 scaled shrink = va_arg(args, int);
1312                                 tex_print_dimension(total, pt_unit);
1313                                 if (stretch) {
1314                                     tex_print_str(" plus ");
1315                                     tex_print_dimension(stretch, pt_unit);
1316                                 } else if (fistretch) {
1317                                     tex_print_str(" plus ");
1318                                     tex_print_dimension(fistretch, no_unit);
1319                                     tex_print_str(" fi");
1320                                 } else if (filstretch) {
1321                                     tex_print_str(" plus ");
1322                                     tex_print_dimension(filstretch, no_unit);
1323                                     tex_print_str(" fil");
1324                                 } else if (fillstretch) {
1325                                     tex_print_str(" plus ");
1326                                     tex_print_dimension(fillstretch, no_unit);
1327                                     tex_print_str(" fill");
1328                                 } else if (filllstretch) {
1329                                     tex_print_str(" plus ");
1330                                     tex_print_dimension(fillstretch, no_unit);
1331                                     tex_print_str(" filll");
1332                                 }
1333                                 if (shrink) {
1334                                     tex_print_str(" minus ");
1335                                     tex_print_dimension(shrink, pt_unit);
1336                                 }
1337                                 break;
1338                            }
1339                        case 'Q':
1340                            {
1341                                scaled s = va_arg(args, scaled);
1342                                int u = va_arg(args, int);
1343                                tex_print_spec(s, u);
1344                                break;
1345                            }
1346                        case 'R':
1347                            {
1348                                halfword d = va_arg(args, int);
1349                                tex_print_rule_dimension(d);
1350                                break;
1351                            }
1352                        case 'S':
1353                            {
1354                                halfword cs = va_arg(args, int);
1355                                tex_print_cs(cs);
1356                                break;
1357                            }
1358                        case 'T':
1359                            {
1360                                strnumber s = va_arg(args, int);
1361                                tex_print_tex_str(s);
1362                                break;
1363                            }
1364                        case 'U':
1365                            {
1366                                halfword c = va_arg(args, int);
1367                                tex_print_uhex(c);
1368                                break;
1369                            }
1370                        case 'X':
1371                            { 
1372                                halfword x = va_arg(args, int);
1373                                tex_print_xhex(x);
1374                                break;
1375                            }
1376                        case '2':
1377                            {
1378                                halfword c = va_arg(args, int);
1379                                switch (c) {
1380                                    case direction_l2r : tex_print_str("l2r"); break;
1381                                    case direction_r2l : tex_print_str("r2l"); break;
1382                                    default            : tex_print_str("unset"); break;
1383                                }
1384                                break;
1385                            }
1386                        case '%':
1387                            tex_print_char('%');
1388                            break;
1389                     // case '[':
1390                     //     tex_begin_diagnostic();
1391                     //     tex_print_char('[');
1392                     //     break;
1393                     // case ']':
1394                     //     tex_print_char(']');
1395                     //     tex_end_diagnostic();
1396                     //     break;
1397                        default:
1398                            /* ignore bad one */
1399                            break;
1400                    }
1401                }
1402                break;
1403            case '\n':
1404            case '\r':
1405                tex_print_nlp();
1406                break;
1407            default:
1408                tex_print_char(chr); /* todo: utf */
1409                break;
1410        }
1411    }
1412}
1413
1414void tex_print_format(const char *format, ...)
1415{
1416    va_list args;
1417    va_start(args, format); /* hm, weird, no number */
1418    tex_print_format_args(format, args);
1419    va_end(args);
1420}
1421
1422/*tex
1423
1424    Group codes were introduced in \ETEX\ but have been extended in the meantime in \LUATEX\ and
1425    later again in \LUAMETATEX. We might have (even) more granularity in the future.
1426
1427    Todo: combine this with an array of struct(id,name,lua) ... a rainy day + stack of new cd's job.
1428
1429*/
1430
1431void tex_print_group(int e)
1432{
1433    int line = tex_saved_line_at_level();
1434    tex_print_str(lmt_interface.group_code_values[cur_group].name);
1435    if (cur_group != bottom_level_group) {
1436        tex_print_str(" group");
1437        if (line) {
1438            tex_print_str(e ? " entered at line " : " at line ");
1439            tex_print_int(line);
1440        }
1441    }
1442}
1443
1444void tex_print_message(const char *s)
1445{
1446    tex_print_nlp();
1447    tex_print_char('(');
1448    tex_print_str(s);
1449    tex_print_char(')');
1450    tex_print_nlp();
1451}
1452