1 /* ------------------------------------------------------------------------- */
2 /* "errors" : Warnings, errors and fatal errors */
3 /* (with error throwback code for RISC OS machines) */
5 /* Part of Inform 6.35 */
6 /* copyright (c) Graham Nelson 1993 - 2021 */
8 /* Inform is free software: you can redistribute it and/or modify */
9 /* it under the terms of the GNU General Public License as published by */
10 /* the Free Software Foundation, either version 3 of the License, or */
11 /* (at your option) any later version. */
13 /* Inform is distributed in the hope that it will be useful, */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16 /* GNU General Public License for more details. */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with Inform. If not, see https://gnu.org/licenses/ */
21 /* ------------------------------------------------------------------------- */
25 #define ERROR_BUFLEN (256)
26 static char error_message_buff[ERROR_BUFLEN+4]; /* room for ellipsis */
28 /* ------------------------------------------------------------------------- */
29 /* Error preamble printing. */
30 /* ------------------------------------------------------------------------- */
32 ErrorPosition ErrorReport; /* Maintained by "lexer.c" */
34 static char other_pos_buff[ERROR_BUFLEN+1]; /* Used by location_text() */
36 static void print_preamble(void)
38 /* Only really prints the preamble to an error or warning message:
40 e.g. "jigsaw.apollo", line 24:
42 The format is controllable (from an ICL switch) since this assists
43 the working of some development environments. */
45 int j, with_extension_flag = FALSE; char *p;
47 j = ErrorReport.file_number;
48 if (j <= 0 || j > total_files) p = ErrorReport.source;
49 else p = InputFiles[j-1].filename;
55 case 0: /* RISC OS error message format */
57 if (!(ErrorReport.main_flag)) printf("\"%s\", ", p);
58 printf("line %d: ", ErrorReport.line_number);
59 if (ErrorReport.orig_file) {
61 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
62 op = ErrorReport.orig_source;
64 op = InputFiles[ErrorReport.orig_file-1].filename;
65 printf("(\"%s\"", op);
66 if (ErrorReport.orig_line) {
67 printf(", %d", ErrorReport.orig_line);
68 if (ErrorReport.orig_char) {
69 printf(":%d", ErrorReport.orig_char);
76 case 1: /* Microsoft error message format */
78 for (j=0; p[j]!=0; j++)
79 { if (p[j] == FN_SEP) with_extension_flag = TRUE;
80 if (p[j] == '.') with_extension_flag = FALSE;
83 if (with_extension_flag) printf("%s", Source_Extension);
84 printf("(%d): ", ErrorReport.line_number);
87 case 2: /* Macintosh Programmer's Workshop error message format */
89 printf("File \"%s\"; Line %d\t# ", p, ErrorReport.line_number);
94 static char *location_text(brief_location report_line)
100 /* Convert the location to a brief string.
101 (Some error messages need to report a secondary location.)
102 This uses the static buffer other_pos_buff. */
104 ErrorPosition errpos;
105 errpos.file_number = -1;
106 errpos.source = NULL;
107 errpos.line_number = 0;
108 errpos.main_flag = 0;
109 errpos.orig_source = NULL;
110 export_brief_location(report_line, &errpos);
112 j = errpos.file_number;
113 if (j <= 0 || j > total_files) p = errpos.source;
114 else p = InputFiles[j-1].filename;
120 if (!(errpos.main_flag)) {
121 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
123 len = strlen(other_pos_buff);
125 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
126 "line %d", errpos.line_number);
128 return other_pos_buff;
131 static void ellipsize_error_message_buff(void)
133 /* If the error buffer was actually filled up by a message, it was
134 probably truncated too. Add an ellipsis, for which we left
135 extra room. (Yes, yes; errors that are *exactly* 255 characters
136 long will suffer an unnecessary ellipsis.) */
137 if (strlen(error_message_buff) == ERROR_BUFLEN-1)
138 strcat(error_message_buff, "...");
141 /* ------------------------------------------------------------------------- */
142 /* Fatal errors (which have style 0) */
143 /* ------------------------------------------------------------------------- */
145 extern void fatalerror(char *s)
148 printf("Fatal error: %s\n",s);
149 if (no_compiler_errors > 0) print_sorry_message();
157 if (temporary_files_switch) remove_temp_files();
158 abort_transcript_file();
161 my_free(&all_text,"transcription text");
162 longjmp(g_fallback, 1);
167 extern void fatalerror_named(char *m, char *fn)
168 { snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"", m, fn);
169 ellipsize_error_message_buff();
170 fatalerror(error_message_buff);
173 extern void memory_out_error(int32 size, int32 howmany, char *name)
175 snprintf(error_message_buff, ERROR_BUFLEN,
176 "Run out of memory allocating %d bytes for %s", size, name);
178 snprintf(error_message_buff, ERROR_BUFLEN,
179 "Run out of memory allocating array of %dx%d bytes for %s",
180 howmany, size, name);
181 ellipsize_error_message_buff();
182 fatalerror(error_message_buff);
185 extern void memoryerror(char *s, int32 size)
187 snprintf(error_message_buff, ERROR_BUFLEN,
188 "The memory setting %s (which is %ld at present) has been \
189 exceeded. Try running Inform again with $%s=<some-larger-number> on the \
190 command line.",s,(long int) size,s);
191 ellipsize_error_message_buff();
192 fatalerror(error_message_buff);
195 /* ------------------------------------------------------------------------- */
196 /* Survivable diagnostics: */
197 /* compilation errors style 1 */
198 /* warnings style 2 */
199 /* linkage errors style 3 */
200 /* compiler errors style 4 (these should never happen and */
201 /* indicate a bug in Inform) */
202 /* ------------------------------------------------------------------------- */
204 static int errors[MAX_ERRORS];
206 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
209 char *forerrors_buff;
210 int forerrors_pointer;
212 static void message(int style, char *s)
213 { int throw_style = style;
214 if (hash_printed_since_newline) printf("\n");
215 hash_printed_since_newline = FALSE;
218 { case 1: printf("Error: "); no_errors++; break;
219 case 2: printf("Warning: "); no_warnings++; break;
220 case 3: printf("Error: [linking '%s'] ", current_module_filename);
221 no_link_errors++; no_errors++; throw_style=1; break;
222 case 4: printf("*** Compiler error: ");
223 no_compiler_errors++; throw_style=1; break;
227 throwback(throw_style, s);
230 ProcessEvents (&g_proc);
234 my_free(&all_text,"transcription text");
236 if (temporary_files_switch) remove_temp_files();
237 abort_transcript_file();
238 longjmp (g_fallback, 1);
241 if ((!concise_switch) && (forerrors_pointer > 0) && (style <= 2))
242 { forerrors_buff[forerrors_pointer] = 0;
243 sprintf(forerrors_buff+68," ...etc");
244 printf("> %s\n",forerrors_buff);
248 /* ------------------------------------------------------------------------- */
249 /* Style 1: Error message routines */
250 /* ------------------------------------------------------------------------- */
252 extern void error(char *s)
253 { if (no_errors == MAX_ERRORS)
254 fatalerror("Too many errors: giving up");
255 errors[no_errors] = no_syntax_lines;
259 extern void error_named(char *s1, char *s2)
260 { snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
261 ellipsize_error_message_buff();
262 error(error_message_buff);
265 extern void error_numbered(char *s1, int val)
267 snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val);
268 ellipsize_error_message_buff();
269 error(error_message_buff);
272 extern void error_named_at(char *s1, char *s2, brief_location report_line)
275 ErrorPosition E = ErrorReport;
276 export_brief_location(report_line, &ErrorReport);
278 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
279 ellipsize_error_message_buff();
281 i = concise_switch; concise_switch = TRUE;
282 error(error_message_buff);
283 ErrorReport = E; concise_switch = i;
286 extern void no_such_label(char *lname)
287 { error_named("No such label as",lname);
290 extern void ebf_error(char *s1, char *s2)
291 { snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s1, s2);
292 ellipsize_error_message_buff();
293 error(error_message_buff);
296 extern void ebf_symbol_error(char *s1, char *name, char *type, brief_location report_line)
297 { snprintf(error_message_buff, ERROR_BUFLEN, "\"%s\" is a name already in use and may not be used as a %s (%s \"%s\" was defined at %s)", name, s1, type, name, location_text(report_line));
298 ellipsize_error_message_buff();
299 error(error_message_buff);
302 extern void char_error(char *s, int ch)
305 uni = iso_to_unicode(ch);
307 if (character_set_unicode)
308 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
309 else if (uni >= 0x100)
310 { snprintf(error_message_buff, ERROR_BUFLEN,
311 "%s (unicode) $%04x = (ISO %s) $%02x", s, uni,
312 name_of_iso_set(character_set_setting), ch);
315 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
317 /* If the character set is set to Latin-1, and the char in question
318 is a printable Latin-1 character, we print it in the error message.
319 This conflates the source-text charset with the terminal charset,
320 really, but it's not a big deal. */
322 if (((uni>=32) && (uni<127))
323 || (((uni >= 0xa1) && (uni <= 0xff))
324 && (character_set_setting==1) && (!character_set_unicode)))
325 { int curlen = strlen(error_message_buff);
326 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
327 ", i.e., '%c'", uni);
330 ellipsize_error_message_buff();
331 error(error_message_buff);
334 extern void unicode_char_error(char *s, int32 uni)
337 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
339 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
341 /* See comment above. */
343 if (((uni>=32) && (uni<127))
344 || (((uni >= 0xa1) && (uni <= 0xff))
345 && (character_set_setting==1) && (!character_set_unicode)))
346 { int curlen = strlen(error_message_buff);
347 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
348 ", i.e., '%c'", uni);
351 ellipsize_error_message_buff();
352 error(error_message_buff);
355 /* ------------------------------------------------------------------------- */
356 /* Style 2: Warning message routines */
357 /* ------------------------------------------------------------------------- */
359 extern void warning(char *s1)
360 { if (nowarnings_switch) { no_suppressed_warnings++; return; }
364 extern void warning_numbered(char *s1, int val)
365 { if (nowarnings_switch) { no_suppressed_warnings++; return; }
366 snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.", s1, val);
367 ellipsize_error_message_buff();
368 message(2,error_message_buff);
371 extern void warning_named(char *s1, char *s2)
373 if (nowarnings_switch) { no_suppressed_warnings++; return; }
374 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"", s1, s2);
375 ellipsize_error_message_buff();
376 message(2,error_message_buff);
379 extern void dbnu_warning(char *type, char *name, brief_location report_line)
381 ErrorPosition E = ErrorReport;
382 if (nowarnings_switch) { no_suppressed_warnings++; return; }
383 export_brief_location(report_line, &ErrorReport);
384 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" declared but not used", type, name);
385 ellipsize_error_message_buff();
386 i = concise_switch; concise_switch = TRUE;
387 message(2,error_message_buff);
392 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
394 /* This is called for functions which have been detected by the
395 track-unused-routines module. These will often (but not always)
396 be also caught by dbnu_warning(), which tracks symbols rather
397 than routine addresses. */
398 ErrorPosition E = ErrorReport;
399 if (nowarnings_switch) { no_suppressed_warnings++; return; }
400 export_brief_location(report_line, &ErrorReport);
401 if (OMIT_UNUSED_ROUTINES)
402 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused and omitted", type, name);
404 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused (not omitted)", type, name);
405 ellipsize_error_message_buff();
406 i = concise_switch; concise_switch = TRUE;
407 message(2,error_message_buff);
412 extern void obsolete_warning(char *s1)
413 { if (is_systemfile()==1) return;
414 if (obsolete_switch || nowarnings_switch)
415 { no_suppressed_warnings++; return; }
416 snprintf(error_message_buff, ERROR_BUFLEN, "Obsolete usage: %s",s1);
417 ellipsize_error_message_buff();
418 message(2,error_message_buff);
421 /* ------------------------------------------------------------------------- */
422 /* Style 3: Link error message routines */
423 /* ------------------------------------------------------------------------- */
425 extern void link_error(char *s)
426 { if (no_errors==MAX_ERRORS) fatalerror("Too many errors: giving up");
427 errors[no_errors] = no_syntax_lines;
431 extern void link_error_named(char *s1, char *s2)
432 { snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
433 ellipsize_error_message_buff();
434 link_error(error_message_buff);
437 /* ------------------------------------------------------------------------- */
438 /* Style 4: Compiler error message routines */
439 /* ------------------------------------------------------------------------- */
441 extern void print_sorry_message(void)
443 "***********************************************************************\n\
444 Compiler errors should never occur if Inform is working properly.\n\
445 Check to see if there is a more recent version available, from which\n\
446 the problem may have been removed. If not, please report this fault\n\
447 and if at all possible, please include your source code, as faults\n\
448 such as these are rare and often difficult to reproduce. Sorry.\n\
449 ***********************************************************************\n");
452 extern int compiler_error(char *s)
453 { if (no_link_errors > 0) return FALSE;
454 if (no_errors > 0) return FALSE;
455 if (no_compiler_errors==MAX_ERRORS)
456 fatalerror("Too many compiler errors: giving up");
461 extern int compiler_error_named(char *s1, char *s2)
462 { if (no_link_errors > 0) return FALSE;
463 if (no_errors > 0) return FALSE;
464 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"",s1,s2);
465 ellipsize_error_message_buff();
466 compiler_error(error_message_buff);
470 /* ------------------------------------------------------------------------- */
471 /* Code for the Acorn RISC OS operating system, donated by Robin Watts, */
472 /* to provide error throwback under the DDE environment */
473 /* ------------------------------------------------------------------------- */
477 #define DDEUtils_ThrowbackStart 0x42587
478 #define DDEUtils_ThrowbackSend 0x42588
479 #define DDEUtils_ThrowbackEnd 0x42589
483 extern void throwback_start(void)
484 { _kernel_swi_regs regs;
485 if (throwback_switch)
486 _kernel_swi(DDEUtils_ThrowbackStart, ®s, ®s);
489 extern void throwback_end(void)
490 { _kernel_swi_regs regs;
491 if (throwback_switch)
492 _kernel_swi(DDEUtils_ThrowbackEnd, ®s, ®s);
495 int throwback_started = FALSE;
497 extern void throwback(int severity, char * error)
498 { _kernel_swi_regs regs;
499 if (!throwback_started)
500 { throwback_started = TRUE;
503 if (throwback_switch)
505 if ((ErrorReport.file_number == -1)
506 || (ErrorReport.file_number == 0))
507 regs.r[2] = (int) (InputFiles[0].filename);
508 else regs.r[2] = (int) (InputFiles[ErrorReport.file_number-1].filename);
509 regs.r[3] = ErrorReport.line_number;
510 regs.r[4] = (2-severity);
511 regs.r[5] = (int) error;
512 _kernel_swi(DDEUtils_ThrowbackSend, ®s, ®s);
518 /* ========================================================================= */
519 /* Data structure management routines */
520 /* ------------------------------------------------------------------------- */
522 extern void init_errors_vars(void)
523 { forerrors_buff = NULL;
524 no_errors = 0; no_warnings = 0; no_suppressed_warnings = 0;
525 no_compiler_errors = 0;
528 extern void errors_begin_pass(void)
529 { ErrorReport.line_number = 0;
530 ErrorReport.file_number = -1;
531 ErrorReport.source = "<no text read yet>";
532 ErrorReport.main_flag = FALSE;
533 ErrorReport.orig_source = NULL;
534 ErrorReport.orig_file = 0;
535 ErrorReport.orig_line = 0;
536 ErrorReport.orig_char = 0;
539 extern void errors_allocate_arrays(void)
540 { forerrors_buff = my_malloc(512, "errors buffer");
543 extern void errors_free_arrays(void)
544 { my_free(&forerrors_buff, "errors buffer");
547 /* ========================================================================= */