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);
60 if (ErrorReport.orig_file) {
62 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
63 op = ErrorReport.orig_source;
65 op = InputFiles[ErrorReport.orig_file-1].filename;
66 printf("(\"%s\"", op);
67 if (ErrorReport.orig_line) {
68 printf(", %d", ErrorReport.orig_line);
69 if (ErrorReport.orig_char) {
70 printf(":%d", ErrorReport.orig_char);
77 case 1: /* Microsoft error message format */
79 for (j=0; p[j]!=0; j++)
80 { if (p[j] == FN_SEP) with_extension_flag = TRUE;
81 if (p[j] == '.') with_extension_flag = FALSE;
84 if (with_extension_flag) printf("%s", Source_Extension);
85 printf("(%d)", ErrorReport.line_number);
87 if (ErrorReport.orig_file) {
89 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
90 op = ErrorReport.orig_source;
92 op = InputFiles[ErrorReport.orig_file-1].filename;
94 if (ErrorReport.orig_line) {
95 printf("(%d", ErrorReport.orig_line);
96 if (ErrorReport.orig_char) {
97 printf(":%d", ErrorReport.orig_char);
106 case 2: /* Macintosh Programmer's Workshop error message format */
108 printf("File \"%s\"; Line %d", p, ErrorReport.line_number);
110 if (ErrorReport.orig_file) {
112 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
113 op = ErrorReport.orig_source;
115 op = InputFiles[ErrorReport.orig_file-1].filename;
116 printf(": (\"%s\"", op);
117 if (ErrorReport.orig_line) {
118 printf("; Line %d", ErrorReport.orig_line);
119 if (ErrorReport.orig_char) {
120 printf("; Char %d", ErrorReport.orig_char);
131 static char *location_text(brief_location report_line)
137 /* Convert the location to a brief string.
138 (Some error messages need to report a secondary location.)
139 This uses the static buffer other_pos_buff. */
141 ErrorPosition errpos;
142 errpos.file_number = -1;
143 errpos.source = NULL;
144 errpos.line_number = 0;
145 errpos.main_flag = 0;
146 errpos.orig_source = NULL;
147 export_brief_location(report_line, &errpos);
149 j = errpos.file_number;
150 if (j <= 0 || j > total_files) p = errpos.source;
151 else p = InputFiles[j-1].filename;
153 if (!p && errpos.line_number == 0) {
155 strcpy(other_pos_buff, "compiler setup");
156 return other_pos_buff;
163 if (!(errpos.main_flag)) {
164 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
166 len = strlen(other_pos_buff);
168 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
169 "line %d", errpos.line_number);
171 return other_pos_buff;
174 static void ellipsize_error_message_buff(void)
176 /* If the error buffer was actually filled up by a message, it was
177 probably truncated too. Add an ellipsis, for which we left
178 extra room. (Yes, yes; errors that are *exactly* 255 characters
179 long will suffer an unnecessary ellipsis.) */
180 if (strlen(error_message_buff) == ERROR_BUFLEN-1)
181 strcat(error_message_buff, "...");
184 /* ------------------------------------------------------------------------- */
185 /* Fatal errors (which have style 0) */
186 /* ------------------------------------------------------------------------- */
188 extern void fatalerror(char *s)
191 printf("Fatal error: %s\n",s);
192 if (no_compiler_errors > 0) print_sorry_message();
200 if (temporary_files_switch) remove_temp_files();
201 abort_transcript_file();
204 my_free(&all_text,"transcription text");
205 longjmp(g_fallback, 1);
210 extern void fatalerror_named(char *m, char *fn)
211 { snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"", m, fn);
212 ellipsize_error_message_buff();
213 fatalerror(error_message_buff);
216 extern void memory_out_error(int32 size, int32 howmany, char *name)
218 snprintf(error_message_buff, ERROR_BUFLEN,
219 "Run out of memory allocating %d bytes for %s", size, name);
221 snprintf(error_message_buff, ERROR_BUFLEN,
222 "Run out of memory allocating array of %dx%d bytes for %s",
223 howmany, size, name);
224 ellipsize_error_message_buff();
225 fatalerror(error_message_buff);
228 extern void memoryerror(char *s, int32 size)
230 snprintf(error_message_buff, ERROR_BUFLEN,
231 "The memory setting %s (which is %ld at present) has been \
232 exceeded. Try running Inform again with $%s=<some-larger-number> on the \
233 command line.",s,(long int) size,s);
234 ellipsize_error_message_buff();
235 fatalerror(error_message_buff);
238 /* ------------------------------------------------------------------------- */
239 /* Survivable diagnostics: */
240 /* compilation errors style 1 */
241 /* warnings style 2 */
242 /* linkage errors style 3 */
243 /* compiler errors style 4 (these should never happen and */
244 /* indicate a bug in Inform) */
245 /* ------------------------------------------------------------------------- */
247 static int errors[MAX_ERRORS];
249 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
252 char *forerrors_buff;
253 int forerrors_pointer;
255 static void message(int style, char *s)
256 { int throw_style = style;
257 if (hash_printed_since_newline) printf("\n");
258 hash_printed_since_newline = FALSE;
261 { case 1: printf("Error: "); no_errors++; break;
262 case 2: printf("Warning: "); no_warnings++; break;
263 case 3: printf("Error: [linking '%s'] ", current_module_filename);
264 no_link_errors++; no_errors++; throw_style=1; break;
265 case 4: printf("*** Compiler error: ");
266 no_compiler_errors++; throw_style=1; break;
270 throwback(throw_style, s);
273 ProcessEvents (&g_proc);
277 my_free(&all_text,"transcription text");
279 if (temporary_files_switch) remove_temp_files();
280 abort_transcript_file();
281 longjmp (g_fallback, 1);
284 if ((!concise_switch) && (forerrors_pointer > 0) && (style <= 2))
285 { forerrors_buff[forerrors_pointer] = 0;
286 sprintf(forerrors_buff+68," ...etc");
287 printf("> %s\n",forerrors_buff);
291 /* ------------------------------------------------------------------------- */
292 /* Style 1: Error message routines */
293 /* ------------------------------------------------------------------------- */
295 extern void error(char *s)
296 { if (no_errors == MAX_ERRORS)
297 fatalerror("Too many errors: giving up");
298 errors[no_errors] = no_syntax_lines;
302 extern void error_named(char *s1, char *s2)
303 { snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
304 ellipsize_error_message_buff();
305 error(error_message_buff);
308 extern void error_numbered(char *s1, int val)
310 snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val);
311 ellipsize_error_message_buff();
312 error(error_message_buff);
315 extern void error_named_at(char *s1, char *s2, brief_location report_line)
318 ErrorPosition E = ErrorReport;
319 export_brief_location(report_line, &ErrorReport);
321 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
322 ellipsize_error_message_buff();
324 i = concise_switch; concise_switch = TRUE;
325 error(error_message_buff);
326 ErrorReport = E; concise_switch = i;
329 extern void no_such_label(char *lname)
330 { error_named("No such label as",lname);
333 extern void ebf_error(char *s1, char *s2)
334 { snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s1, s2);
335 ellipsize_error_message_buff();
336 error(error_message_buff);
339 extern void ebf_symbol_error(char *s1, char *name, char *type, brief_location report_line)
340 { 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));
341 ellipsize_error_message_buff();
342 error(error_message_buff);
345 extern void char_error(char *s, int ch)
348 uni = iso_to_unicode(ch);
350 if (character_set_unicode)
351 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
352 else if (uni >= 0x100)
353 { snprintf(error_message_buff, ERROR_BUFLEN,
354 "%s (unicode) $%04x = (ISO %s) $%02x", s, uni,
355 name_of_iso_set(character_set_setting), ch);
358 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
360 /* If the character set is set to Latin-1, and the char in question
361 is a printable Latin-1 character, we print it in the error message.
362 This conflates the source-text charset with the terminal charset,
363 really, but it's not a big deal. */
365 if (((uni>=32) && (uni<127))
366 || (((uni >= 0xa1) && (uni <= 0xff))
367 && (character_set_setting==1) && (!character_set_unicode)))
368 { int curlen = strlen(error_message_buff);
369 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
370 ", i.e., '%c'", uni);
373 ellipsize_error_message_buff();
374 error(error_message_buff);
377 extern void unicode_char_error(char *s, int32 uni)
380 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
382 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
384 /* See comment above. */
386 if (((uni>=32) && (uni<127))
387 || (((uni >= 0xa1) && (uni <= 0xff))
388 && (character_set_setting==1) && (!character_set_unicode)))
389 { int curlen = strlen(error_message_buff);
390 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
391 ", i.e., '%c'", uni);
394 ellipsize_error_message_buff();
395 error(error_message_buff);
398 /* ------------------------------------------------------------------------- */
399 /* Style 2: Warning message routines */
400 /* ------------------------------------------------------------------------- */
402 extern void warning(char *s1)
403 { if (nowarnings_switch) { no_suppressed_warnings++; return; }
407 extern void warning_numbered(char *s1, int val)
408 { if (nowarnings_switch) { no_suppressed_warnings++; return; }
409 snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.", s1, val);
410 ellipsize_error_message_buff();
411 message(2,error_message_buff);
414 extern void warning_named(char *s1, char *s2)
416 if (nowarnings_switch) { no_suppressed_warnings++; return; }
417 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"", s1, s2);
418 ellipsize_error_message_buff();
419 message(2,error_message_buff);
422 extern void dbnu_warning(char *type, char *name, brief_location report_line)
424 ErrorPosition E = ErrorReport;
425 if (nowarnings_switch) { no_suppressed_warnings++; return; }
426 export_brief_location(report_line, &ErrorReport);
427 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" declared but not used", type, name);
428 ellipsize_error_message_buff();
429 i = concise_switch; concise_switch = TRUE;
430 message(2,error_message_buff);
435 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
437 /* This is called for functions which have been detected by the
438 track-unused-routines module. These will often (but not always)
439 be also caught by dbnu_warning(), which tracks symbols rather
440 than routine addresses. */
441 ErrorPosition E = ErrorReport;
442 if (nowarnings_switch) { no_suppressed_warnings++; return; }
443 export_brief_location(report_line, &ErrorReport);
444 if (OMIT_UNUSED_ROUTINES)
445 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused and omitted", type, name);
447 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused (not omitted)", type, name);
448 ellipsize_error_message_buff();
449 i = concise_switch; concise_switch = TRUE;
450 message(2,error_message_buff);
455 extern void obsolete_warning(char *s1)
456 { if (is_systemfile()==1) return;
457 if (obsolete_switch || nowarnings_switch)
458 { no_suppressed_warnings++; return; }
459 snprintf(error_message_buff, ERROR_BUFLEN, "Obsolete usage: %s",s1);
460 ellipsize_error_message_buff();
461 message(2,error_message_buff);
464 /* ------------------------------------------------------------------------- */
465 /* Style 3: Link error message routines */
466 /* ------------------------------------------------------------------------- */
468 extern void link_error(char *s)
469 { if (no_errors==MAX_ERRORS) fatalerror("Too many errors: giving up");
470 errors[no_errors] = no_syntax_lines;
474 extern void link_error_named(char *s1, char *s2)
475 { snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
476 ellipsize_error_message_buff();
477 link_error(error_message_buff);
480 /* ------------------------------------------------------------------------- */
481 /* Style 4: Compiler error message routines */
482 /* ------------------------------------------------------------------------- */
484 extern void print_sorry_message(void)
486 "***********************************************************************\n\
487 Compiler errors should never occur if Inform is working properly.\n\
488 Check to see if there is a more recent version available, from which\n\
489 the problem may have been removed. If not, please report this fault\n\
490 and if at all possible, please include your source code, as faults\n\
491 such as these are rare and often difficult to reproduce. Sorry.\n\
492 ***********************************************************************\n");
495 extern int compiler_error(char *s)
496 { if (no_link_errors > 0) return FALSE;
497 if (no_errors > 0) return FALSE;
498 if (no_compiler_errors==MAX_ERRORS)
499 fatalerror("Too many compiler errors: giving up");
504 extern int compiler_error_named(char *s1, char *s2)
505 { if (no_link_errors > 0) return FALSE;
506 if (no_errors > 0) return FALSE;
507 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"",s1,s2);
508 ellipsize_error_message_buff();
509 compiler_error(error_message_buff);
513 /* ------------------------------------------------------------------------- */
514 /* Code for the Acorn RISC OS operating system, donated by Robin Watts, */
515 /* to provide error throwback under the DDE environment */
516 /* ------------------------------------------------------------------------- */
520 #define DDEUtils_ThrowbackStart 0x42587
521 #define DDEUtils_ThrowbackSend 0x42588
522 #define DDEUtils_ThrowbackEnd 0x42589
526 extern void throwback_start(void)
527 { _kernel_swi_regs regs;
528 if (throwback_switch)
529 _kernel_swi(DDEUtils_ThrowbackStart, ®s, ®s);
532 extern void throwback_end(void)
533 { _kernel_swi_regs regs;
534 if (throwback_switch)
535 _kernel_swi(DDEUtils_ThrowbackEnd, ®s, ®s);
538 int throwback_started = FALSE;
540 extern void throwback(int severity, char * error)
541 { _kernel_swi_regs regs;
542 if (!throwback_started)
543 { throwback_started = TRUE;
546 if (throwback_switch)
548 if ((ErrorReport.file_number == -1)
549 || (ErrorReport.file_number == 0))
550 regs.r[2] = (int) (InputFiles[0].filename);
551 else regs.r[2] = (int) (InputFiles[ErrorReport.file_number-1].filename);
552 regs.r[3] = ErrorReport.line_number;
553 regs.r[4] = (2-severity);
554 regs.r[5] = (int) error;
555 _kernel_swi(DDEUtils_ThrowbackSend, ®s, ®s);
561 /* ========================================================================= */
562 /* Data structure management routines */
563 /* ------------------------------------------------------------------------- */
565 extern void init_errors_vars(void)
566 { forerrors_buff = NULL;
567 no_errors = 0; no_warnings = 0; no_suppressed_warnings = 0;
568 no_compiler_errors = 0;
571 extern void errors_begin_pass(void)
572 { ErrorReport.line_number = 0;
573 ErrorReport.file_number = -1;
574 ErrorReport.source = "<no text read yet>";
575 ErrorReport.main_flag = FALSE;
576 ErrorReport.orig_source = NULL;
577 ErrorReport.orig_file = 0;
578 ErrorReport.orig_line = 0;
579 ErrorReport.orig_char = 0;
582 extern void errors_allocate_arrays(void)
583 { forerrors_buff = my_malloc(512, "errors buffer");
586 extern void errors_free_arrays(void)
587 { my_free(&forerrors_buff, "errors buffer");
590 /* ========================================================================= */