texerrors.c /size: 19 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
7# include <string.h>
8
9/*tex
10
11    When something anomalous is detected, \TEX\ typically does something like this (in \PASCAL\
12    lingua):
13
14    \starttyping
15    print_err("Something anomalous has been detected");
16    help(
17        "This is the first line of my offer to help.\n"
18        "This is the second line. I'm trying to\n"
19        "explain the best way for you to proceed."
20    );
21    error();
22    \stoptyping
23
24    A two-line help message would be given using |help2|, etc.; these informal helps should use
25    simple vocabulary that complements the words used in the official error message that was
26    printed. (Outside the U.S.A., the help messages should preferably be translated into the local
27    vernacular. Each line of help is at most 60 characters long, in the present implementation, so
28    that |max_print_line| will not be exceeded.)
29
30    The |print_err| procedure supplies a |!| before the official message, and makes sure that the
31    terminal is awake if a stop is going to occur. The |error| procedure supplies a |.| after the
32    official message, then it shows the location of the error; and if |interaction =
33    error_stop_mode|, it also enters into a dialog with the user, during which time the help message
34    may be printed.
35
36*/
37
38error_state_info lmt_error_state = {
39    .last_error         = NULL,
40    .last_lua_error     = NULL,
41    .last_warning_tag   = NULL,
42    .last_warning       = NULL,
43    .last_error_context = NULL,
44    .help_text          = NULL,
45 /* .print_buffer       = "", */
46    .intercept          = 0,
47    .last_intercept     = 0,
48    .interaction        = 0,
49    .default_exit_code  = 0,
50    .set_box_allowed    = 0,
51    .history            = 0,
52    .error_count        = 0,
53    .saved_selector     = 0,
54    .in_error           = 0,
55    .long_help_seen     = 0,
56    .context_indent     = 4,
57    .padding            = 0,
58    .line_limits = {
59        .maximum = max_error_line,
60        .minimum = min_error_line,
61        .size    = min_error_line,
62        .top     = 0,
63    },
64    .half_line_limits = {
65        .maximum = max_half_error_line,
66        .minimum = min_half_error_line,
67        .size    = min_half_error_line,
68        .top     = 0,
69    },
70} ;
71
72/*tex
73    Because a |text_can| can be assembled we make a copy. There are not many cases where this is
74    really needed but there are seldom errors anyway so we can neglect this duplication of data.
75*/
76
77inline static void tex_aux_update_help_text(const char* str)
78{
79    if (lmt_error_state.help_text) {
80        lmt_memory_free(lmt_error_state.help_text);
81        lmt_error_state.help_text = NULL;
82    }
83    if (str) {
84        lmt_error_state.help_text = lmt_memory_strdup(str);
85    }
86}
87
88/*tex
89
90    The previously defines structure collects all relevant variables: the current level of
91    interaction: |interaction|, states like |last_error|, |last_lua_error|, |last_warning_tag|,
92    |last_warning_str| and |last_error_context|, and temporary variables like |err_old_setting| and
93    |in_error|.
94
95    This is a variant on |show_runaway| that is used when we delegate error handling to a \LUA\
96    callback. (Maybe some day that will be default.)
97
98*/
99
100static void tex_aux_set_last_error_context(void)
101{
102    int saved_selector = lmt_print_state.selector;
103    int saved_new_line_char = new_line_char_par;
104    int saved_new_string_line = lmt_print_state.new_string_line;
105    lmt_print_state.selector = new_string_selector_code;
106    new_line_char_par = 10;
107    lmt_print_state.new_string_line = 10;
108    tex_show_validity();
109    tex_show_context();
110    lmt_memory_free(lmt_error_state.last_error_context);
111    lmt_error_state.last_error_context = tex_take_string(NULL);
112    lmt_print_state.selector = saved_selector;
113    new_line_char_par = saved_new_line_char;
114    lmt_print_state.new_string_line = saved_new_string_line;
115}
116
117static void tex_aux_flush_error(void)
118{
119    if (lmt_error_state.in_error) {
120        lmt_print_state.selector = lmt_error_state.saved_selector;
121        lmt_memory_free(lmt_error_state.last_error);
122        lmt_error_state.last_error = tex_take_string(NULL);
123        if (lmt_error_state.last_error) {
124            int callback_id = lmt_callback_defined(show_error_message_callback);
125            if (callback_id > 0) {
126                lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "->");
127            } else {
128                tex_print_str(lmt_error_state.last_error);
129            }
130        }
131        lmt_error_state.in_error = 0;
132    }
133}
134
135static int tex_aux_error_callback_set(void)
136{
137    int callback_id = lmt_callback_defined(show_error_message_callback);
138    return lmt_lua_state.lua_instance && callback_id > 0 ? callback_id : 0;
139}
140
141static void tex_aux_start_error(void)
142{
143    if (tex_aux_error_callback_set()) {
144        lmt_error_state.saved_selector = lmt_print_state.selector;
145        lmt_print_state.selector = new_string_selector_code;
146        lmt_error_state.in_error = 1 ;
147        lmt_memory_free(lmt_error_state.last_error);
148        lmt_error_state.last_error = NULL;
149    } else {
150        tex_print_nlp();
151        tex_print_str("! ");
152    }
153}
154
155/*tex
156
157    \TEX\ is careful not to call |error| when the print |selector| setting might be unusual. The
158    only possible values of |selector| at the time of error messages are:
159
160    \startitemize
161        \startitem |no_print|:     |interaction=batch_mode| and |log_file| not yet open; \stopitem
162        \startitem |term_only|:    |interaction>batch_mode| and |log_file| not yet open; \stopitem
163        \startitem |log_only|:     |interaction=batch_mode| and |log_file| is open;      \stopitem
164        \startitem |term_and_log|: |interaction>batch_mode| and |log_file| is open.      \stopitem
165    \stopitemize
166
167*/
168
169void tex_fixup_selector(int logopened)
170{
171    if (lmt_error_state.interaction == batch_mode) {
172        lmt_print_state.selector = logopened ? logfile_selector_code : no_print_selector_code ;
173    } else {
174        lmt_print_state.selector = logopened ? terminal_and_logfile_selector_code : terminal_selector_code;
175    }
176}
177
178/*tex
179
180    The variable |history| records the worst level of error that has been detected. It has four
181    possible values: |spotless|, |warning_issued|, |error_message_issued|, and |fatal_error_stop|.
182
183    Another variable, |error_count|, is increased by one when an |error| occurs without an
184    interactive dialog, and it is reset to zero at the end of every paragraph. If |error_count|
185    reaches 100, \TEX\ decides that there is no point in continuing further.
186
187    The value of |history| is initially |fatal_error_stop|, but it will be changed to |spotless|
188    if \TEX\ survives the initialization process.
189
190*/
191
192void tex_initialize_errors(void)
193{
194    lmt_error_state.interaction = error_stop_mode;
195    lmt_error_state.set_box_allowed = 1;
196    if (lmt_error_state.half_line_limits.size > lmt_error_state.line_limits.size) {
197        lmt_error_state.half_line_limits.size = lmt_error_state.line_limits.size/2;
198    }
199    if (lmt_error_state.half_line_limits.size <= 30) {
200        lmt_error_state.half_line_limits.size = 31;
201    } else if (lmt_error_state.half_line_limits.size >= (lmt_error_state.line_limits.size - 15)) {
202        lmt_error_state.half_line_limits.size = lmt_error_state.line_limits.size - 16;
203    }
204}
205
206/*tex
207
208    It is possible for |error| to be called recursively if some error arises when |get_token| is
209    being used to delete a token, and/or if some fatal error occurs while \TEX\ is trying to fix
210    a non-fatal one. But such recursion is never more than two levels deep.
211
212    Individual lines of help are recorded in the string |help_text|. There can be embedded
213    newlines.
214
215    The |jump_out| procedure just cuts across all active procedure levels and exits the program.
216    It is used when there is no recovery from a particular error. The exit code can be overloaded.
217
218    We don't close the lua state because we then have to collect lots of garbage and it really
219    slows doen the run. It's not needed anyway, as we exit.
220
221*/
222
223static int tex_aux_final_exit(int code)
224{
225    exit(code);
226    return 0; /* unreachable */
227}
228
229int tex_normal_exit(void)
230{
231    tex_terminal_update();
232 /* lua_close(lua_state.lua_instance); */
233    lmt_main_state.ready_already = output_disabled_state;
234    if (lmt_error_state.history != spotless && lmt_error_state.history != warning_issued) {
235        return tex_aux_final_exit(EXIT_FAILURE);
236    } else {
237        return tex_aux_final_exit(lmt_error_state.default_exit_code);
238    }
239}
240
241static void tex_aux_jump_out(void)
242{
243    tex_close_files_and_terminate(1);
244    tex_normal_exit();
245}
246
247/*tex
248
249    This completes the job of error reporting, that is, in good old \TEX. But in \LUATEX\ it
250    doesn't make sense to suport this model of error handling, also because one cannot backtrack
251    over \LUA\ actions, so it would be a cheat. But we can keep the modes.
252
253*/
254
255static void tex_aux_error(int type)
256{
257    int callback_id = lmt_callback_defined(intercept_tex_error_callback);
258    tex_aux_flush_error();
259    if (lmt_error_state.history < error_message_issued && type !=  warning_error_type) {
260        lmt_error_state.history = error_message_issued;
261    }
262    if (lmt_lua_state.lua_instance && callback_id > 0) {
263        tex_aux_set_last_error_context();
264        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->d", lmt_error_state.interaction, type, &lmt_error_state.interaction);
265        lmt_error_state.error_count = 0;
266        tex_terminal_update();
267        switch (lmt_error_state.interaction) {
268            case batch_mode: /* Q */
269                --lmt_print_state.selector;
270                return;
271            case nonstop_mode: /* R */
272                return;
273            case scroll_mode: /* S */
274                return;
275            case error_stop_mode: /* carry on */
276                break;
277            default: /* exit */
278                lmt_error_state.interaction = scroll_mode;
279                if (type != warning_error_type) {
280                    tex_aux_jump_out();
281                }
282                break;
283        }
284    } else {
285        tex_print_char('.');
286        tex_show_context();
287    }
288    if (type != warning_error_type) {
289        ++lmt_error_state.error_count;
290        if (lmt_error_state.error_count == 100) {
291            tex_print_message("That makes 100 errors; please try again.");
292            lmt_error_state.history = fatal_error_stop;
293            tex_aux_jump_out();
294        }
295    }
296    /*tex
297        We assume that the callback handles the log file too. Otherwise we put the help message in
298        the log file.
299    */
300    if (callback_id == 0) {
301        if (lmt_error_state.interaction > batch_mode) {
302            /*tex Avoid terminal output: */
303            --lmt_print_state.selector;
304        }
305        tex_print_nlp();
306        if (lmt_error_state.help_text) {
307            tex_print_str(lmt_error_state.help_text);
308            tex_print_nlp();
309        }
310        if (lmt_error_state.interaction > batch_mode) {
311            /*tex Re-enable terminal output: */
312            ++lmt_print_state.selector;
313        }
314    }
315    tex_print_ln();
316}
317
318/*tex
319
320    In anomalous cases, the print selector might be in an unknown state; the following subroutine
321    is called to fix things just enough to keep running a bit longer.
322
323*/
324
325static void tex_aux_normalize_selector(void)
326{
327    if (lmt_fileio_state.log_opened) {
328        lmt_print_state.selector = terminal_and_logfile_selector_code;
329    } else {
330        lmt_print_state.selector = terminal_selector_code;
331    }
332    if (! lmt_fileio_state.job_name) {
333        tex_open_log_file();
334    }
335    if (lmt_error_state.interaction == batch_mode) {
336        /*tex It becomes no or terminal. */
337        --lmt_print_state.selector;
338    }
339}
340
341/*tex The following procedure prints \TEX's last words before dying: */
342
343static void tex_aux_succumb_error(void)
344{
345    if (lmt_error_state.interaction == error_stop_mode) {
346        /*tex No more interaction: */
347        lmt_error_state.interaction = scroll_mode;
348    }
349    if (lmt_fileio_state.log_opened) {
350        tex_aux_error(succumb_error_type);
351    }
352    lmt_error_state.history = fatal_error_stop;
353    /*tex Irrecoverable error: */
354    tex_aux_jump_out();
355}
356
357/*tex This prints |s|, and that's it. */
358
359void tex_fatal_error(const char *helpinfo)
360{
361    tex_aux_normalize_selector();
362    tex_handle_error(
363        succumb_error_type,
364        "Emergency stop",
365        helpinfo
366    );
367}
368
369/*tex Here is the most dreaded error message. We stop due to finiteness. */
370
371void tex_overflow_error(const char *s, int n)
372{
373    tex_aux_normalize_selector();
374    tex_handle_error(
375        succumb_error_type,
376        "TeX capacity exceeded, sorry [%s=%i]",
377        s, n,
378        "If you really absolutely need more capacity, you can ask a wizard to enlarge me."
379    );
380}
381
382/*tex
383
384    The program might sometime run completely amok, at which point there is no choice but to stop.
385    If no previous error has been detected, that's bad news; a message is printed that is really
386    intended for the \TEX\ maintenance person instead of the user (unless the user has been
387    particularly diabolical). The index entries for \quotation {this can't happen} may help to
388    pinpoint the problem.
389
390*/
391
392int tex_confusion(const char *s)
393{
394    /*tex A consistency check violated; |s| tells where: */
395    tex_aux_normalize_selector();
396    if (lmt_error_state.history < error_message_issued) {
397        tex_handle_error(
398            succumb_error_type,
399            "This can't happen (%s)",
400            s,
401            "I'm broken. Please show this to someone who can fix me."
402        );
403    } else {
404        tex_handle_error(
405            succumb_error_type,
406            "I can't go on meeting you like this",
407            "One of your faux pas seems to have wounded me deeply ... in fact, I'm barely\n"
408            "conscious. Please fix it and try again."
409        );
410    }
411    return 0;
412}
413
414/*tex
415
416    When the program is interrupted we just quit. Here is the hook to deal with it.
417
418*/
419
420void aux_quit_the_program(void) /*tex No |tex_| prefix here! */
421{
422    tex_handle_error(
423        succumb_error_type,
424        "Forced stop",
425        NULL
426    );
427}
428
429/*tex
430
431    The |back_error| routine is used when we want to replace an offending token just before issuing
432    an error message. This routine, like |back_input|, requires that |cur_tok| has been set. We
433    disable interrupts during the call of |back_input| so that the help message won't be lost.
434
435*/
436
437static void tex_aux_back_error(void)
438{
439    tex_back_input(cur_tok);
440    tex_aux_error(back_error_type);
441}
442
443/*tex Back up one inserted token and call |error|. */
444
445static void tex_aux_insert_error(void)
446{
447    tex_back_input(cur_tok);
448    lmt_input_state.cur_input.token_type = inserted_text;
449    tex_aux_error(insert_error_type);
450}
451
452int tex_normal_error(const char *t, const char *p)
453{
454   if (lmt_engine_state.lua_only) {
455        /*tex Normally ending up here means that we call the wrong error function. */
456        tex_emergency_message(t, p);
457    } else {
458        tex_aux_normalize_selector();
459        if (! tex_aux_error_callback_set()) {
460            tex_print_nlp();
461            tex_print_str("! ");
462        }
463        tex_print_str("error");
464        if (t) {
465            tex_print_format(" (%s)", t);
466        }
467        tex_print_str(": ");
468        if (p) {
469            tex_print_str(p);
470        }
471        lmt_error_state.history = fatal_error_stop;
472        tex_print_str("\n");
473    }
474    return tex_aux_final_exit(EXIT_FAILURE);
475}
476
477void tex_normal_warning(const char *t, const char *p)
478{
479   if (lmt_engine_state.lua_only) {
480        /*tex Normally ending up here means that we call the wrong error function. */
481        tex_emergency_message(t, p);
482    } else if (strcmp(t, "lua") == 0) {
483        int callback_id = lmt_callback_defined(intercept_lua_error_callback);
484        int saved_new_line_char = new_line_char_par;
485        new_line_char_par = 10;
486        if (lmt_lua_state.lua_instance && callback_id) {
487            (void) lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "->");
488            /* error(); */
489        } else {
490            tex_handle_error(
491                normal_error_type,
492                p ? p : "unspecified lua error",
493                "The lua interpreter ran into a problem, so the remainder of this lua chunk will\n"
494                "be ignored."
495            );
496        }
497        new_line_char_par = saved_new_line_char;
498    } else {
499        int callback_id = lmt_callback_defined(show_warning_message_callback);
500        if (callback_id > 0) {
501            /*tex Free the last ones, */
502            lmt_memory_free(lmt_error_state.last_warning);
503            lmt_memory_free(lmt_error_state.last_warning_tag);
504            lmt_error_state.last_warning = lmt_memory_strdup(p);
505            lmt_error_state.last_warning_tag = lmt_memory_strdup(t);
506            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "->");
507        } else {
508            tex_print_ln();
509            tex_print_str("warning");
510            if (t) {
511                tex_print_format(" (%s)", t);
512            }
513            tex_print_str(": ");
514            if (p) {
515                tex_print_str(p);
516            }
517            tex_print_ln();
518        }
519        if (lmt_error_state.history == spotless) {
520            lmt_error_state.history = warning_issued;
521        }
522    }
523}
524
525int tex_formatted_error(const char *t, const char *fmt, ...)
526{
527    char print_buffer[print_buffer_size]; 
528    va_list args;
529    va_start(args, fmt);
530    vsnprintf(print_buffer, print_buffer_size, fmt, args);
531    return tex_normal_error(t, print_buffer);
532    /*
533    va_end(args);
534    return 0;
535    */
536}
537
538void tex_formatted_warning(const char *t, const char *fmt, ...)
539{
540    char print_buffer[print_buffer_size]; 
541    va_list args;
542    va_start(args, fmt);
543    vsnprintf(print_buffer, print_buffer_size, fmt, args);
544    tex_normal_warning(t, print_buffer);
545    va_end(args);
546}
547
548void tex_emergency_message(const char *t, const char *fmt, ...)
549{
550    char print_buffer[print_buffer_size]; 
551    va_list args;
552    va_start(args, fmt);
553    vsnprintf(print_buffer, print_buffer_size, fmt, args);
554    fprintf(stdout,"%s : %s\n", t, print_buffer);
555    va_end(args);
556}
557
558int tex_emergency_exit(void)
559{
560    return tex_aux_final_exit(EXIT_FAILURE);
561}
562
563/*tex A prelude to more abstraction and maybe using sprint etc.*/
564
565static void tex_aux_do_handle_error_type(
566    int type
567) {
568    switch (type) {
569        case normal_error_type:
570        case eof_error_type:
571        case condition_error_type:
572        case runaway_error_type:
573        case warning_error_type:
574            tex_aux_error(type);
575            break;
576        case back_error_type:
577            tex_aux_back_error();
578            break;
579        case insert_error_type:
580            tex_aux_insert_error();
581            break;
582        case succumb_error_type:
583            tex_aux_succumb_error();
584            break;
585    }
586}
587
588void tex_handle_error_message_only(
589    const char *message
590)
591{
592    tex_aux_start_error();
593    tex_print_str(message);
594    if (tex_aux_error_callback_set()) {
595        lmt_error_state.in_error = 0;
596        lmt_memory_free(lmt_error_state.last_error);
597        lmt_error_state.last_error = lmt_memory_strdup(message);
598    }
599}
600
601/*tex
602
603    We had about 15 specific tuned message handlers as a prelude to a general template based one
604    and that one has arrived (we also have a print one, beginning 2021 only partially applied as
605    I'm undecided). We can now call a translation callback where we remap similar to how we do it
606    in ConTeXt but I;'m nor that sure if users really need it. The english is probably the least
607    problematic part of an error so first I will perfect the tracing bit.
608
609    Todo: a translation callback: |str, 1 => str|, or not.
610
611*/
612
613extern void tex_handle_error(error_types type, const char *format, ...)
614{
615    const char *str = NULL;
616    va_list args;
617    va_start(args, format); /* hm, weird, no number */
618    tex_aux_start_error();
619    str = tex_print_format_args(format, args);
620    tex_aux_update_help_text(str);
621    tex_aux_do_handle_error_type(type);
622    va_end(args);
623}
624