summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex/errors.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
committerKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
commitad547a6b5986815fda458221149728d9d9ab1d87 (patch)
tree16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/luatexdir/tex/errors.c
parent947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff)
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/errors.c')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/errors.c1055
1 files changed, 1055 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.c b/Build/source/texk/web2c/luatexdir/tex/errors.c
new file mode 100644
index 00000000000..ce80df99e7e
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/errors.c
@@ -0,0 +1,1055 @@
+/*
+
+errors.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+#define edit_var "TEXEDIT"
+
+/*tex
+
+When something anomalous is detected, \TeX\ typically does something like this:
+$$\vbox{\halign{#\hfil\cr |print_err("Something anomalous has been
+detected");|\cr |help3("This is the first line of my offer to help.")|\cr |("This
+is the second line. I'm trying to")|\cr |("explain the best way for you to
+proceed.");|\cr |error;|\cr}}$$ A two-line help message would be given using
+|help2|, etc.; these informal helps should use simple vocabulary that complements
+the words used in the official error message that was printed. (Outside the
+U.S.A., the help messages should preferably be translated into the local
+vernacular. Each line of help is at most 60 characters long, in the present
+implementation, so that |max_print_line| will not be exceeded.)
+
+The |print_err| procedure supplies a `\.!' before the official message, and makes
+sure that the terminal is awake if a stop is going to occur. The |error|
+procedure supplies a `\..' after the official message, then it shows the location
+of the error; and if |interaction=error_stop_mode|, it also enters into a dialog
+with the user, during which time the help message may be printed. @^system
+dependencies@>
+
+*/
+
+/*tex The current level of interaction: */
+
+int interaction;
+
+/*tex Set from the command line: */
+
+int interactionoption;
+
+char *last_error = NULL;
+char *last_lua_error = NULL;
+char *last_warning_tag = NULL;
+char *last_warning_str = NULL;
+char *last_error_context = NULL;
+
+int err_old_setting = 0 ;
+int in_error = 0 ;
+
+void set_last_error_context(void)
+{
+ str_number str;
+ int sel = selector;
+ int saved_new_line_char;
+ int saved_new_string_line;
+ selector = new_string;
+ saved_new_line_char = new_line_char_par;
+ saved_new_string_line = new_string_line;
+ new_line_char_par = 10;
+ new_string_line = 10;
+ show_context();
+ xfree(last_error_context);
+ str = make_string();
+ last_error_context = makecstring(str);
+ flush_str(str);
+ selector = sel;
+ new_line_char_par = saved_new_line_char;
+ new_string_line = saved_new_string_line;
+ return;
+}
+
+void flush_err(void)
+{
+ str_number s_error;
+ char *s = NULL;
+ int callback_id ;
+ if (in_error) {
+ selector = err_old_setting;
+ str_room(1);
+ s_error = make_string();
+ s = makecstring(s_error);
+ flush_str(s_error);
+ if (interaction == error_stop_mode) {
+ wake_up_terminal();
+ }
+ xfree(last_error);
+ last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
+ strcpy(last_error,s);
+ callback_id = callback_defined(show_error_message_callback);
+ if (callback_id > 0) {
+ run_callback(callback_id, "->");
+ } else {
+ tprint(s);
+ }
+ in_error = 0 ;
+ }
+}
+
+void print_err(const char *s)
+{
+ int callback_id = callback_defined(show_error_message_callback);
+ if (interaction == error_stop_mode) {
+ wake_up_terminal();
+ }
+ if (callback_id > 0) {
+ err_old_setting = selector;
+ selector = new_string;
+ in_error = 1 ;
+ }
+ if (filelineerrorstylep) {
+ print_file_line();
+ } else {
+ tprint_nl("! ");
+ }
+ tprint(s);
+ if (callback_id <= 0) {
+ xfree(last_error);
+ last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
+ strcpy(last_error,s);
+ }
+}
+
+/*tex
+
+\TeX\ is careful not to call |error| when the print |selector| setting might be
+unusual. The only possible values of |selector| at the time of error messages are
+
+|no_print| (when |interaction=batch_mode| and |log_file| not yet open);
+
+|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
+
+|log_only| (when |interaction=batch_mode| and |log_file| is open);
+
+|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
+
+*/
+
+void fixup_selector(boolean logopened)
+{
+ if (interaction == batch_mode)
+ selector = no_print;
+ else
+ selector = term_only;
+ if (logopened)
+ selector = selector + 2;
+}
+
+/*tex
+
+A global variable |deletions_allowed| is set |false| if the |get_next| routine is
+active when |error| is called; this ensures that |get_next| and related routines
+like |get_token| will never be called recursively. A similar interlock is
+provided by |set_box_allowed|. @^recursion@>
+
+The global variable |history| records the worst level of error that has been
+detected. It has four possible values: |spotless|, |warning_issued|,
+|error_message_issued|, and |fatal_error_stop|.
+
+Another global variable, |error_count|, is increased by one when an |error|
+occurs without an interactive dialog, and it is reset to zero at the end of every
+paragraph. If |error_count| reaches 100, \TeX\ decides that there is no point in
+continuing further.
+
+*/
+
+/*tex Is it safe for |error| to call |get_token|? */
+
+boolean deletions_allowed;
+
+/*tex Is it safe to do a \.{\\setbox} assignment? */
+
+boolean set_box_allowed;
+/*tex Has the source input been clean so far? */
+
+int history;
+
+/*tex The number of scrolled errors since the last paragraph ended. */
+
+int error_count;
+
+/*tex Should \TeX\ pause for instructions? */
+
+int interrupt;
+
+/*tex Should interrupts be observed? */
+
+boolean OK_to_interrupt;
+
+/*tex
+
+The value of |history| is initially |fatal_error_stop|, but it will be changed to
+|spotless| if \TeX\ survives the initialization process.
+
+*/
+
+void initialize_errors(void)
+{
+ if (interactionoption == unspecified_mode)
+ interaction = error_stop_mode;
+ else
+ interaction = interactionoption;
+ deletions_allowed = true;
+ set_box_allowed = true;
+ OK_to_interrupt = true;
+}
+
+/*tex
+
+It is possible for |error| to be called recursively if some error arises when
+|get_token| is being used to delete a token, and/or if some fatal error occurs
+while \TeX\ is trying to fix a non-fatal one. But such recursion @^recursion@> is
+never more than two levels deep.
+
+Individual lines of help are recorded in the array |help_line|.
+
+*/
+
+const char *help_line[7];
+
+/*tex
+ Should the |err_help| list be shown?
+*/
+
+boolean use_err_help;
+
+/*tex
+
+The |jump_out| procedure just cuts across all active procedure levels and exits
+the program. It is used when there is no recovery from a particular error. The
+exit code can be overloaded.
+
+*/
+
+int defaultexitcode = 0;
+
+__attribute__ ((noreturn))
+void do_final_end(void)
+{
+ update_terminal();
+ ready_already = 0;
+ lua_close(Luas);
+ if ((history != spotless) && (history != warning_issued))
+ uexit(1);
+ else
+ uexit(defaultexitcode);
+}
+
+__attribute__ ((noreturn))
+void jump_out(void)
+{
+ close_files_and_terminate();
+ do_final_end();
+}
+
+/*tex
+
+Here is the function that calls the editor, if one is defined. This is loosely
+based on a similar function in kpathsea, but the calling convention is quite
+different.
+
+*/
+
+static const_string edit_value = EDITOR;
+
+#if defined(WIN32)
+
+static int Isspace (char c)
+{
+ return (c == ' ' || c == '\t');
+}
+
+#endif /* WIN32 */
+
+__attribute__ ((noreturn))
+static void luatex_calledit (int baseptr, int linenumber)
+{
+ char *temp, *command;
+ char *fullcmd = NULL; /* avoid compiler warning */
+ char c;
+ int sdone, ddone, i;
+ char *filename = makecstring(input_stack[base_ptr].name_field);
+ int fnlength = strlen(filename);
+#ifdef WIN32
+ char *fp, *ffp, *env, editorname[256], buffer[256];
+ int cnt = 0;
+ int dontchange = 0;
+#endif
+ sdone = ddone = 0;
+ /*tex
+ Close any open input files, since we're going to kill the job.
+ */
+ close_files_and_terminate();
+ /*tex
+ Replace the default with the value of the appropriate environment
+ variable or config file value, if it's set.
+ */
+ temp = kpse_var_value (edit_var);
+ if (temp != NULL)
+ edit_value = temp;
+ /*tex
+ Construct the command string. The `11' is the maximum length an
+ integer might be.
+ */
+ command = xmalloc (strlen (edit_value) + fnlength + 11);
+ /*tex
+ So we can construct it as we go.
+ */
+ temp = command;
+#ifdef WIN32
+ fp = editorname;
+ if ((isalpha(*edit_value) && *(edit_value + 1) == ':' && IS_DIR_SEP (*(edit_value + 2)))
+ || (*edit_value == '"' && isalpha(*(edit_value + 1))
+ && *(edit_value + 2) == ':' && IS_DIR_SEP (*(edit_value + 3)))) {
+ dontchange = 1;
+ }
+#endif
+ while ((c = *edit_value++) != 0) {
+ if (c == '%') {
+ switch (c = *edit_value++) {
+ case 'd':
+ if (ddone)
+ FATAL1 ("call_edit: `%%d' appears twice in editor command: `%s'", edit_value);
+ sprintf (temp, "%ld", (long int)linenumber);
+ while (*temp != '\0')
+ temp++;
+ ddone = 1;
+ break;
+ case 's':
+ if (sdone)
+ FATAL1 ("call_edit: `%%s' appears twice in editor command: `%s'", edit_value);
+ for (i =0; i < fnlength; i++)
+ *temp++ = filename[i];
+ sdone = 1;
+ break;
+ case '\0':
+ *temp++ = '%';
+ /*tex
+ Back up to the null to force termination.
+ */
+ edit_value--;
+ break;
+ default:
+ *temp++ = '%';
+ *temp++ = c;
+ break;
+ }
+ } else {
+#ifdef WIN32
+ if (dontchange) {
+ *temp++ = c;
+ } else if(Isspace(c) && cnt == 0) {
+ cnt++;
+ temp = command;
+ *temp++ = c;
+ *fp = '\0';
+ } else if(!Isspace(c) && cnt == 0) {
+ *fp++ = c;
+ } else {
+ *temp++ = c;
+ }
+#else
+ *temp++ = c;
+#endif
+ }
+ }
+ *temp = 0;
+#ifdef WIN32
+ if (dontchange == 0) {
+ if(editorname[0] == '.' || editorname[0] == '/' || editorname[0] == '\\') {
+ fprintf(stderr, "%s is not allowed to execute.\n", editorname);
+ do_final_end();
+ }
+ env = (char *)getenv("PATH");
+ if(SearchPath(env, editorname, ".exe", 256, buffer, &ffp)==0) {
+ if(SearchPath(env, editorname, ".bat", 256, buffer, &ffp)==0) {
+ fprintf(stderr, "I cannot find %s in the PATH.\n", editorname);
+ do_final_end();
+ }
+ }
+ fullcmd = (char *)xmalloc(strlen(buffer)+strlen(command)+5);
+ strcpy(fullcmd, "\"");
+ strcat(fullcmd, buffer);
+ strcat(fullcmd, "\"");
+ strcat(fullcmd, command);
+ }
+#else
+ fullcmd = command;
+#endif
+ /*tex Execute the command. */
+ if (system (fullcmd) != 0) {
+ fprintf (stderr, "! Trouble executing `%s'.\n", command);
+ }
+ /*tex Quit, since we found an error. */
+ do_final_end ();
+}
+
+/*tex
+
+ This completes the job of error reporting.
+
+*/
+
+void error(void)
+{
+ /*tex What the user types :*/
+ ASCII_code c;
+ int callback_id;
+ /*tex Used to save global variables when deleting tokens: */
+ int s1, s2, s3, s4;
+ int i;
+ flush_err();
+ if (history < error_message_issued)
+ history = error_message_issued;
+ callback_id = callback_defined(show_error_hook_callback);
+ if (callback_id > 0) {
+ set_last_error_context();
+ run_callback(callback_id, "->");
+ } else {
+ print_char('.');
+ show_context();
+ }
+ if (haltonerrorp) {
+ history = fatal_error_stop;
+ jump_out();
+ }
+ if (interaction == error_stop_mode) {
+ /*tex Get user's advice and |return|. */
+ while (1) {
+ CONTINUE:
+ /*tex
+ Original reports:
+
+ https://tex.stackexchange.com/questions/551313/
+ https://tug.org/pipermail/tex-live/2020-June/045876.html
+
+ This will probably be fixed by DEK in the 2021 tuneup in a different
+ way (so we'll have to remove or alter this change), but the interaction
+ sequence in the reports above causes a segmentation fault in web2c -
+ writing to the closed \write15 stream because we wrongly decrement
+ selector from 16 to 15 in term_input, due to the lack of this check in
+ recursive error() call.
+ */
+ if (interaction !=error_stop_mode)
+ return;
+ clear_for_error_prompt();
+ prompt_input("? ");
+ if (last == first)
+ return;
+ c = buffer[first];
+ if (c >= 'a')
+ c = c + 'A' - 'a';
+ /*tex
+ Interpret code |c| and |return| if done. It is desirable to
+ provide an `\.E' option here that gives the user an easy way
+ to return from \TeX\ to the system editor, with the offending
+ line ready to be edited. But such an extension requires some
+ system wizardry, so the present implementation simply types
+ out the name of the file that should be edited and the
+ relevant line number.
+ */
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (deletions_allowed) {
+ /*tex
+ Delete |c-"0"| tokens and |goto continue|. We allow
+ deletion of up to 99 tokens at a time.
+ */
+ s1 = cur_tok;
+ s2 = cur_cmd;
+ s3 = cur_chr;
+ s4 = align_state;
+ align_state = 1000000;
+ OK_to_interrupt = false;
+ if ((last > first + 1) && (buffer[first + 1] >= '0')
+ && (buffer[first + 1] <= '9'))
+ c = c * 10 + buffer[first + 1] - '0' * 11;
+ else
+ c = c - '0';
+ while (c > 0) {
+ /*tex One-level recursive call of |error| is possible. */
+ get_token();
+ decr(c);
+ }
+ cur_tok = s1;
+ cur_cmd = s2;
+ cur_chr = s3;
+ align_state = s4;
+ OK_to_interrupt = true;
+ help2(
+ "I have just deleted some text, as you asked.",
+ "You can now delete more, or insert, or whatever."
+ );
+ show_context();
+ goto CONTINUE;
+ }
+ break;
+ case 'E':
+ if (base_ptr > 0) {
+ int callback_id = callback_defined(call_edit_callback);
+ if (callback_id>0) {
+ (void)run_callback(callback_id, "Sd->", makecstring(input_stack[base_ptr].name_field), line);
+ /*tex This should not be reached. */
+ jump_out();
+ } else {
+ tprint_nl("You want to edit file ");
+ print(input_stack[base_ptr].name_field);
+ tprint(" at line ");
+ print_int(line);
+ interaction = scroll_mode;
+ if (kpse_init) {
+ luatex_calledit(base_ptr, line);
+ } else {
+ /*tex This should not be reached. */
+ tprint_nl("There is no valid callback defined.");
+ jump_out();
+ }
+ }
+ }
+ break;
+ case 'H':
+ /*tex Print the help information and |goto continue| */
+ if (use_err_help) {
+ give_err_help();
+ } else {
+ if (help_line[0] == NULL) {
+ help2(
+ "Sorry, I don't know how to help in this situation.",
+ "Maybe you should try asking a human?"
+ );
+ }
+ i = 0;
+ while (help_line[i] != NULL)
+ tprint_nl(help_line[i++]);
+ help4(
+ "Sorry, I already gave what help I could...",
+ "Maybe you should try asking a human?",
+ "An error might have occurred before I noticed any problems.",
+ "``If all else fails, read the instructions.''"
+ );
+ goto CONTINUE;
+ }
+ break;
+ case 'I':
+ /*tex
+
+ Introduce new material from the terminal and |return|. When
+ the following code is executed, |buffer[(first+1)..(last-1)]|
+ may contain the material inserted by the user; otherwise
+ another prompt will be given. In order to understand this
+ part of the program fully, you need to be familiar with
+ \TeX's input stacks.
+
+ We enter a new syntactic level for terminal input:
+
+ */
+ begin_file_reading();
+ /*tex
+ Now |state=mid_line|, so an initial blank space will count as
+ a blank.
+ */
+ if (last > first + 1) {
+ iloc = first + 1;
+ buffer[first] = ' ';
+ } else {
+ prompt_input("insert>");
+ iloc = first;
+ }
+ first = last;
+ /*tex No |end_line_char| ends this line. */
+ ilimit = last - 1;
+ return;
+ break;
+ case 'Q':
+ case 'R':
+ case 'S':
+ /*tex
+
+ Change the interaction level and |return|. Here the author of
+ \TeX\ apologizes for making use of the numerical relation
+ between |"Q"|, |"R"|, |"S"|, and the desired interaction
+ settings |batch_mode|, |nonstop_mode|, |scroll_mode|.
+
+ */
+ error_count = 0;
+ interaction = batch_mode + c - 'Q';
+ tprint("OK, entering ");
+ switch (c) {
+ case 'Q':
+ tprint_esc("batchmode");
+ decr(selector);
+ break;
+ case 'R':
+ tprint_esc("nonstopmode");
+ break;
+ case 'S':
+ tprint_esc("scrollmode");
+ break;
+ }
+ tprint("...");
+ print_ln();
+ update_terminal();
+ return;
+ break;
+ case 'X':
+ interaction = scroll_mode;
+ jump_out();
+ break;
+ default:
+ break;
+ }
+ if (!use_err_help) {
+ /* Print the menu of available options */
+ tprint("Type <return> to proceed, S to scroll future error messages,");
+ tprint_nl("R to run without stopping, Q to run quietly,");
+ tprint_nl("I to insert something, ");
+ if (base_ptr > 0)
+ tprint("E to edit your file,");
+ if (deletions_allowed)
+ tprint_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+ tprint_nl("H for help, X to quit.");
+ }
+ use_err_help = false;
+ }
+
+ }
+ incr(error_count);
+ if (error_count == 100) {
+ tprint_nl("(That makes 100 errors; please try again.)");
+ history = fatal_error_stop;
+ jump_out();
+ }
+ /*tex Put help message on the transcript file. */
+ if (interaction > batch_mode) {
+ /*tex Avoid terminal output: */
+ decr(selector);
+ }
+ if (use_err_help) {
+ print_ln();
+ give_err_help();
+ } else {
+ int i1 = 0;
+ while (help_line[i1] != NULL)
+ tprint_nl(help_line[i1++]);
+ }
+ print_ln();
+ if (interaction > batch_mode) {
+ /*tex Re-enable terminal output: */
+ incr(selector);
+ }
+ print_ln();
+}
+
+/*tex
+
+A dozen or so error messages end with a parenthesized integer, so we save a teeny
+bit of program space by declaring the following procedure:
+
+*/
+
+void int_error(int n)
+{
+ tprint(" (");
+ print_int(n);
+ print_char(')');
+ error();
+}
+
+/*tex
+
+In anomalous cases, the print selector might be in an unknown state; the
+following subroutine is called to fix things just enough to keep running a bit
+longer.
+
+*/
+
+void normalize_selector(void)
+{
+ if (log_opened_global)
+ selector = term_and_log;
+ else
+ selector = term_only;
+ if (job_name == 0)
+ open_log_file();
+ if (interaction == batch_mode)
+ decr(selector);
+}
+
+/*tex
+
+The following procedure prints \TeX's last words before dying.
+
+*/
+
+void succumb(void)
+{
+ if (interaction == error_stop_mode) {
+ /*tex No more interaction: */
+ interaction = scroll_mode;
+ }
+ if (log_opened_global) {
+ error();
+ }
+ history = fatal_error_stop;
+ /*tex Irrecoverable error: */
+ jump_out();
+}
+
+/*tex
+
+This prints |s|, and that's it.
+
+*/
+
+void fatal_error(const char *s)
+{
+ normalize_selector();
+ print_err("Emergency stop");
+ help1(s);
+ succumb();
+}
+
+/*tex
+
+Here is the most dreaded error message. We stop due to finiteness.
+
+*/
+
+void overflow(const char *s, unsigned int n)
+{
+ normalize_selector();
+ print_err("TeX capacity exceeded, sorry [");
+ tprint(s);
+ print_char('=');
+ print_int((int) n);
+ print_char(']');
+ if (varmem == NULL) {
+ print_err("Sorry, I ran out of memory.");
+ print_ln();
+ exit(EXIT_FAILURE);
+ }
+ help2(
+ "If you really absolutely need more capacity,",
+ "you can ask a wizard to enlarge me."
+ );
+ succumb();
+}
+
+/*tex
+
+The program might sometime run completely amok, at which point there is no choice
+but to stop. If no previous error has been detected, that's bad news; a message
+is printed that is really intended for the \TeX\ maintenance person instead of
+the user (unless the user has been particularly diabolical). The index entries
+for `this can't happen' may help to pinpoint the problem. @^dry rot@>
+
+*/
+
+void confusion(const char *s)
+{ /* consistency check violated; |s| tells where */
+ normalize_selector();
+ if (history < error_message_issued) {
+ print_err("This can't happen (");
+ tprint(s);
+ print_char(')');
+ help1(
+ "I'm broken. Please show this to someone who can fix"
+ );
+ } else {
+ print_err("I can't go on meeting you like this");
+ help2(
+ "One of your faux pas seems to have wounded me deeply...",
+ "in fact, I'm barely conscious. Please fix it and try again."
+ );
+ }
+ succumb();
+}
+
+/*tex
+
+Users occasionally want to interrupt \TeX\ while it's running. If the runtime
+system allows this, one can implement a routine that sets the global variable
+|interrupt| to some nonzero value when such an interrupt is signalled. Otherwise
+there is probably at least a way to make |interrupt| nonzero using the debugger.
+@^system dependencies@> @^debugging@>
+
+*/
+
+void check_interrupt(void)
+{
+ if (interrupt != 0)
+ pause_for_instructions();
+}
+
+/*tex
+
+When an interrupt has been detected, the program goes into its highest
+interaction level and lets the user have nearly the full flexibility of the
+|error| routine. \TeX\ checks for interrupts only at times when it is safe to do
+this.
+
+*/
+
+void pause_for_instructions(void)
+{
+ if (OK_to_interrupt) {
+ interaction = error_stop_mode;
+ if ((selector == log_only) || (selector == no_print))
+ incr(selector);
+ print_err("Interruption");
+ help3(
+ "You rang?",
+ "Try to insert some instructions for me (e.g.,`I\\showlists'),",
+ "unless you just want to quit by typing `X'."
+ );
+ deletions_allowed = false;
+ error();
+ deletions_allowed = true;
+ interrupt = 0;
+ }
+}
+
+void tex_error(const char *msg, const char **hlp)
+{
+ print_err(msg);
+ if (hlp != NULL) {
+ int i;
+ for (i = 0; (hlp[i] != NULL && i <= 5); i++) {
+ help_line[i] = hlp[i];
+ }
+ help_line[i] = NULL;
+ } else {
+ help_line[0] = NULL;
+ }
+ error();
+}
+
+/*tex
+
+The |back_error| routine is used when we want to replace an offending token just
+before issuing an error message. This routine, like |back_input|, requires that
+|cur_tok| has been set. We disable interrupts during the call of |back_input| so
+that the help message won't be lost.
+
+*/
+
+void back_error(void)
+{
+ OK_to_interrupt = false;
+ back_input();
+ OK_to_interrupt = true;
+ error();
+}
+
+/*tex
+
+ Back up one inserted token and call |error|.
+*/
+
+void ins_error(void)
+{
+ OK_to_interrupt = false;
+ back_input();
+ token_type = inserted;
+ OK_to_interrupt = true;
+ error();
+}
+
+/*tex
+
+When \TeX\ wants to typeset a character that doesn't exist, the character node is
+not created; thus the output routine can assume that characters exist when it
+sees them. The following procedure prints a warning message unless the user has
+suppressed it.
+If |tracing_lost_chars_par| (i.e. \.{\\tracinglostchar}) is greater than 2,
+it's considered as an error.
+
+*/
+
+void char_warning(internal_font_number f, int c)
+{
+ /*tex saved value of |tracing_online| */
+ int old_setting;
+ /* index to current digit; we assume that $0\L n<16^{22}$ */
+ int k;
+ if (tracing_lost_chars_par > 0) {
+ old_setting = tracing_online_par;
+ if (tracing_lost_chars_par > 1)
+ tracing_online_par = 1;
+ begin_diagnostic();
+ tprint_nl("Missing character: There is no ");
+ print(c);
+ tprint(" (U+");
+ k = 0;
+ if (c < 16)
+ print_char('0');
+ if (c < 256)
+ print_char('0');
+ if (c < 4096)
+ print_char('0');
+ do {
+ dig[k] = c % 16;
+ c = c / 16;
+ incr(k);
+ } while (c != 0);
+ print_the_digs((eight_bits) k);
+ tprint(") in font ");
+ print_font_name(f);
+ print_char('!');
+ end_diagnostic(false);
+ tracing_online_par = old_setting;
+ }
+ if (tracing_lost_chars_par > 2) {
+ error();
+ }
+}
+
+void wrapup_backend(void) {
+ ensure_output_state(static_pdf, ST_OMODE_FIX);
+ if (output_mode_used == OMODE_NONE) {
+ print_err(" ==> Fatal error occurred, no FMT file produced!");
+ } else {
+ backend_out_control[backend_control_finish_file](static_pdf,history == fatal_error_stop);
+ }
+}
+
+void normal_error(const char *t, const char *p)
+{
+ normalize_selector();
+ if (interaction == error_stop_mode) {
+ wake_up_terminal();
+ }
+ if (filelineerrorstylep) {
+ print_file_line();
+ } else {
+ tprint_nl("! ");
+ }
+ tprint("error: ");
+ if (cur_file_name) {
+ tprint(" (file ");
+ tprint(cur_file_name);
+ tprint(")");
+ }
+ if (t != NULL) {
+ tprint(" (");
+ tprint(t);
+ tprint(")");
+ }
+ tprint(": ");
+ if (p != NULL)
+ tprint(p);
+ history = fatal_error_stop;
+ wrapup_backend();
+ exit(EXIT_FAILURE);
+}
+
+void normal_warning(const char *t, const char *p)
+{
+ int report_id ;
+ if (strcmp(t,"lua") == 0) {
+ int saved_new_line_char;
+ saved_new_line_char = new_line_char_par;
+ new_line_char_par = 10;
+ report_id = callback_defined(show_lua_error_hook_callback);
+ if (report_id == 0) {
+ if (p != NULL)
+ tprint(p);
+ help2(
+ "The lua interpreter ran into a problem, so the",
+ "remainder of this lua chunk will be ignored."
+ );
+ } else {
+ (void) run_callback(report_id, "->");
+ }
+ error();
+ new_line_char_par = saved_new_line_char;
+ } else {
+ report_id = callback_defined(show_warning_message_callback);
+ if (report_id > 0) {
+ /*tex Free the last ones, */
+ xfree(last_warning_str);
+ xfree(last_warning_tag);
+ last_warning_str = (string) xmalloc(strlen(p) + 1);
+ last_warning_tag = (string) xmalloc(strlen(t) + 1);
+ strcpy(last_warning_str,p);
+ strcpy(last_warning_tag,t);
+ run_callback(report_id, "->");
+ } else {
+ print_ln();
+ tprint("warning ");
+ if (cur_file_name) {
+ tprint(" (file ");
+ tprint(cur_file_name);
+ tprint(")");
+ }
+ if (t != NULL) {
+ tprint(" (");
+ tprint(t);
+ tprint(")");
+ }
+ tprint(": ");
+ if (p != NULL)
+ tprint(p);
+ print_ln();
+ }
+ if (history == spotless)
+ history = warning_issued;
+ }
+}
+
+static char print_buf[PRINTF_BUF_SIZE];
+
+__attribute__ ((format(printf, 2,3)))
+void formatted_error(const char *t, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
+ normal_error(t,print_buf);
+ va_end(args);
+}
+
+__attribute__ ((format(printf, 2,3)))
+void formatted_warning(const char *t, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
+ normal_warning(t,print_buf);
+ va_end(args);
+}