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 - 2020 */
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)
96 /* Convert the location to a brief string.
97 (Some error messages need to report a secondary location.)
98 This uses the static buffer other_pos_buff. */
100 ErrorPosition errpos;
101 errpos.file_number = -1;
102 errpos.source = NULL;
103 errpos.line_number = 0;
104 errpos.main_flag = 0;
105 errpos.orig_source = NULL;
106 export_brief_location(report_line, &errpos);
111 j = errpos.file_number;
112 if (j <= 0 || j > total_files) p = errpos.source;
113 else p = InputFiles[j-1].filename;
119 if (!(errpos.main_flag)) {
120 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
122 len = strlen(other_pos_buff);
124 snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
125 "line %d", errpos.line_number);
127 return other_pos_buff;
130 static void ellipsize_error_message_buff(void)
132 /* If the error buffer was actually filled up by a message, it was
133 probably truncated too. Add an ellipsis, for which we left
134 extra room. (Yes, yes; errors that are *exactly* 255 characters
135 long will suffer an unnecessary ellipsis.) */
136 if (strlen(error_message_buff) == ERROR_BUFLEN-1)
137 strcat(error_message_buff, "...");
140 /* ------------------------------------------------------------------------- */
141 /* Fatal errors (which have style 0) */
142 /* ------------------------------------------------------------------------- */
144 extern void fatalerror(char *s)
147 printf("Fatal error: %s\n",s);
148 if (no_compiler_errors > 0) print_sorry_message();
156 if (temporary_files_switch) remove_temp_files();
157 abort_transcript_file();
160 my_free(&all_text,"transcription text");
161 longjmp(g_fallback, 1);
166 extern void fatalerror_named(char *m, char *fn)
167 { snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"", m, fn);
168 ellipsize_error_message_buff();
169 fatalerror(error_message_buff);
172 extern void memory_out_error(int32 size, int32 howmany, char *name)
174 snprintf(error_message_buff, ERROR_BUFLEN,
175 "Run out of memory allocating %d bytes for %s", size, name);
177 snprintf(error_message_buff, ERROR_BUFLEN,
178 "Run out of memory allocating array of %dx%d bytes for %s",
179 howmany, size, name);
180 ellipsize_error_message_buff();
181 fatalerror(error_message_buff);
184 extern void memoryerror(char *s, int32 size)
186 snprintf(error_message_buff, ERROR_BUFLEN,
187 "The memory setting %s (which is %ld at present) has been \
188 exceeded. Try running Inform again with $%s=<some-larger-number> on the \
189 command line.",s,(long int) size,s);
190 ellipsize_error_message_buff();
191 fatalerror(error_message_buff);
194 /* ------------------------------------------------------------------------- */
195 /* Survivable diagnostics: */
196 /* compilation errors style 1 */
197 /* warnings style 2 */
198 /* linkage errors style 3 */
199 /* compiler errors style 4 (these should never happen and */
200 /* indicate a bug in Inform) */
201 /* ------------------------------------------------------------------------- */
203 static int errors[MAX_ERRORS];
205 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
208 char *forerrors_buff;
209 int forerrors_pointer;
211 static void message(int style, char *s)
212 { int throw_style = style;
213 if (hash_printed_since_newline) printf("\n");
214 hash_printed_since_newline = FALSE;
217 { case 1: printf("Error: "); no_errors++; break;
218 case 2: printf("Warning: "); no_warnings++; break;
219 case 3: printf("Error: [linking '%s'] ", current_module_filename);
220 no_link_errors++; no_errors++; throw_style=1; break;
221 case 4: printf("*** Compiler error: ");
222 no_compiler_errors++; throw_style=1; break;
226 throwback(throw_style, s);
229 ProcessEvents (&g_proc);
233 my_free(&all_text,"transcription text");
235 if (temporary_files_switch) remove_temp_files();
236 abort_transcript_file();
237 longjmp (g_fallback, 1);
240 if ((!concise_switch) && (forerrors_pointer > 0) && (style <= 2))
241 { forerrors_buff[forerrors_pointer] = 0;
242 sprintf(forerrors_buff+68," ...etc");
243 printf("> %s\n",forerrors_buff);
247 /* ------------------------------------------------------------------------- */
248 /* Style 1: Error message routines */
249 /* ------------------------------------------------------------------------- */
251 extern void error(char *s)
252 { if (no_errors == MAX_ERRORS)
253 fatalerror("Too many errors: giving up");
254 errors[no_errors] = no_syntax_lines;
258 extern void error_named(char *s1, char *s2)
259 { snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
260 ellipsize_error_message_buff();
261 error(error_message_buff);
264 extern void error_numbered(char *s1, int val)
266 snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val);
267 ellipsize_error_message_buff();
268 error(error_message_buff);
271 extern void error_named_at(char *s1, char *s2, brief_location report_line)
274 ErrorPosition E = ErrorReport;
275 export_brief_location(report_line, &ErrorReport);
277 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
278 ellipsize_error_message_buff();
280 i = concise_switch; concise_switch = TRUE;
281 error(error_message_buff);
282 ErrorReport = E; concise_switch = i;
285 extern void no_such_label(char *lname)
286 { error_named("No such label as",lname);
289 extern void ebf_error(char *s1, char *s2)
290 { snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s1, s2);
291 ellipsize_error_message_buff();
292 error(error_message_buff);
295 extern void ebf_symbol_error(char *s1, char *name, char *type, brief_location report_line)
296 { 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));
297 ellipsize_error_message_buff();
298 error(error_message_buff);
301 extern void char_error(char *s, int ch)
304 uni = iso_to_unicode(ch);
306 if (character_set_unicode)
307 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
308 else if (uni >= 0x100)
309 { snprintf(error_message_buff, ERROR_BUFLEN,
310 "%s (unicode) $%04x = (ISO %s) $%02x", s, uni,
311 name_of_iso_set(character_set_setting), ch);
314 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
316 /* If the character set is set to Latin-1, and the char in question
317 is a printable Latin-1 character, we print it in the error message.
318 This conflates the source-text charset with the terminal charset,
319 really, but it's not a big deal. */
321 if (((uni>=32) && (uni<127))
322 || (((uni >= 0xa1) && (uni <= 0xff))
323 && (character_set_setting==1) && (!character_set_unicode)))
324 { int curlen = strlen(error_message_buff);
325 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
326 ", i.e., '%c'", uni);
329 ellipsize_error_message_buff();
330 error(error_message_buff);
333 extern void unicode_char_error(char *s, int32 uni)
336 snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
338 snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
340 /* See comment above. */
342 if (((uni>=32) && (uni<127))
343 || (((uni >= 0xa1) && (uni <= 0xff))
344 && (character_set_setting==1) && (!character_set_unicode)))
345 { int curlen = strlen(error_message_buff);
346 snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
347 ", i.e., '%c'", uni);
350 ellipsize_error_message_buff();
351 error(error_message_buff);
354 /* ------------------------------------------------------------------------- */
355 /* Style 2: Warning message routines */
356 /* ------------------------------------------------------------------------- */
358 extern void warning(char *s1)
359 { if (nowarnings_switch) { no_suppressed_warnings++; return; }
363 extern void warning_numbered(char *s1, int val)
364 { if (nowarnings_switch) { no_suppressed_warnings++; return; }
365 snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.", s1, val);
366 ellipsize_error_message_buff();
367 message(2,error_message_buff);
370 extern void warning_named(char *s1, char *s2)
372 if (nowarnings_switch) { no_suppressed_warnings++; return; }
373 snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"", s1, s2);
374 ellipsize_error_message_buff();
375 message(2,error_message_buff);
378 extern void dbnu_warning(char *type, char *name, brief_location report_line)
380 ErrorPosition E = ErrorReport;
381 if (nowarnings_switch) { no_suppressed_warnings++; return; }
382 export_brief_location(report_line, &ErrorReport);
383 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" declared but not used", type, name);
384 ellipsize_error_message_buff();
385 i = concise_switch; concise_switch = TRUE;
386 message(2,error_message_buff);
391 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
393 /* This is called for functions which have been detected by the
394 track-unused-routines module. These will often (but not always)
395 be also caught by dbnu_warning(), which tracks symbols rather
396 than routine addresses. */
397 ErrorPosition E = ErrorReport;
398 if (nowarnings_switch) { no_suppressed_warnings++; return; }
399 export_brief_location(report_line, &ErrorReport);
400 if (OMIT_UNUSED_ROUTINES)
401 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused and omitted", type, name);
403 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused (not omitted)", type, name);
404 ellipsize_error_message_buff();
405 i = concise_switch; concise_switch = TRUE;
406 message(2,error_message_buff);
411 extern void obsolete_warning(char *s1)
412 { if (is_systemfile()==1) return;
413 if (obsolete_switch || nowarnings_switch)
414 { no_suppressed_warnings++; return; }
415 snprintf(error_message_buff, ERROR_BUFLEN, "Obsolete usage: %s",s1);
416 ellipsize_error_message_buff();
417 message(2,error_message_buff);
420 /* ------------------------------------------------------------------------- */
421 /* Style 3: Link error message routines */
422 /* ------------------------------------------------------------------------- */
424 extern void link_error(char *s)
425 { if (no_errors==MAX_ERRORS) fatalerror("Too many errors: giving up");
426 errors[no_errors] = no_syntax_lines;
430 extern void link_error_named(char *s1, char *s2)
431 { snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
432 ellipsize_error_message_buff();
433 link_error(error_message_buff);
436 /* ------------------------------------------------------------------------- */
437 /* Style 4: Compiler error message routines */
438 /* ------------------------------------------------------------------------- */
440 extern void print_sorry_message(void)
442 "***********************************************************************\n\
443 Compiler errors should never occur if Inform is working properly.\n\
444 Check to see if there is a more recent version available, from which\n\
445 the problem may have been removed. If not, please report this fault\n\
446 and if at all possible, please include your source code, as faults\n\
447 such as these are rare and often difficult to reproduce. Sorry.\n\
448 ***********************************************************************\n");
451 extern int compiler_error(char *s)
452 { if (no_link_errors > 0) return FALSE;
453 if (no_errors > 0) return FALSE;
454 if (no_compiler_errors==MAX_ERRORS)
455 fatalerror("Too many compiler errors: giving up");
460 extern int compiler_error_named(char *s1, char *s2)
461 { if (no_link_errors > 0) return FALSE;
462 if (no_errors > 0) return FALSE;
463 snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"",s1,s2);
464 ellipsize_error_message_buff();
465 compiler_error(error_message_buff);
469 /* ------------------------------------------------------------------------- */
470 /* Code for the Acorn RISC OS operating system, donated by Robin Watts, */
471 /* to provide error throwback under the DDE environment */
472 /* ------------------------------------------------------------------------- */
476 #define DDEUtils_ThrowbackStart 0x42587
477 #define DDEUtils_ThrowbackSend 0x42588
478 #define DDEUtils_ThrowbackEnd 0x42589
482 extern void throwback_start(void)
483 { _kernel_swi_regs regs;
484 if (throwback_switch)
485 _kernel_swi(DDEUtils_ThrowbackStart, ®s, ®s);
488 extern void throwback_end(void)
489 { _kernel_swi_regs regs;
490 if (throwback_switch)
491 _kernel_swi(DDEUtils_ThrowbackEnd, ®s, ®s);
494 int throwback_started = FALSE;
496 extern void throwback(int severity, char * error)
497 { _kernel_swi_regs regs;
498 if (!throwback_started)
499 { throwback_started = TRUE;
502 if (throwback_switch)
504 if ((ErrorReport.file_number == -1)
505 || (ErrorReport.file_number == 0))
506 regs.r[2] = (int) (InputFiles[0].filename);
507 else regs.r[2] = (int) (InputFiles[ErrorReport.file_number-1].filename);
508 regs.r[3] = ErrorReport.line_number;
509 regs.r[4] = (2-severity);
510 regs.r[5] = (int) error;
511 _kernel_swi(DDEUtils_ThrowbackSend, ®s, ®s);
517 /* ========================================================================= */
518 /* Data structure management routines */
519 /* ------------------------------------------------------------------------- */
521 extern void init_errors_vars(void)
522 { forerrors_buff = NULL;
523 no_errors = 0; no_warnings = 0; no_suppressed_warnings = 0;
524 no_compiler_errors = 0;
527 extern void errors_begin_pass(void)
528 { ErrorReport.line_number = 0;
529 ErrorReport.file_number = -1;
530 ErrorReport.source = "<no text read yet>";
531 ErrorReport.main_flag = FALSE;
532 ErrorReport.orig_source = NULL;
533 ErrorReport.orig_file = 0;
534 ErrorReport.orig_line = 0;
535 ErrorReport.orig_char = 0;
538 extern void errors_allocate_arrays(void)
539 { forerrors_buff = my_malloc(512, "errors buffer");
542 extern void errors_free_arrays(void)
543 { my_free(&forerrors_buff, "errors buffer");
546 /* ========================================================================= */