d13039890700112fc767ad8b63a7931833ac07df
[inform.git] / src / errors.c
1 /* ------------------------------------------------------------------------- */
2 /*   "errors" : Warnings, errors and fatal errors                            */
3 /*              (with error throwback code for RISC OS machines)             */
4 /*                                                                           */
5 /* Copyright (c) Graham Nelson 1993 - 2020                                   */
6 /*                                                                           */
7 /* This file is part of Inform.                                              */
8 /*                                                                           */
9 /* Inform is free software: you can redistribute it and/or modify            */
10 /* it under the terms of the GNU General Public License as published by      */
11 /* the Free Software Foundation, either version 3 of the License, or         */
12 /* (at your option) any later version.                                       */
13 /*                                                                           */
14 /* Inform is distributed in the hope that it will be useful,                 */
15 /* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
16 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
17 /* GNU General Public License for more details.                              */
18 /*                                                                           */
19 /* You should have received a copy of the GNU General Public License         */
20 /* along with Inform. If not, see https://gnu.org/licenses/                  */
21 /*                                                                           */
22 /* ------------------------------------------------------------------------- */
23
24 #include "header.h"
25
26 #define ERROR_BUFLEN (256)
27 static char error_message_buff[ERROR_BUFLEN+4]; /* room for ellipsis */
28
29 /* ------------------------------------------------------------------------- */
30 /*   Error preamble printing.                                                */
31 /* ------------------------------------------------------------------------- */
32
33 ErrorPosition ErrorReport;             /*  Maintained by "lexer.c"           */
34
35 static void print_preamble(void)
36 {
37     /*  Only really prints the preamble to an error or warning message:
38
39         e.g.  "jigsaw.apollo", line 24:
40
41         The format is controllable (from an ICL switch) since this assists
42         the working of some development environments.                        */
43
44     int j, with_extension_flag = FALSE; char *p;
45
46     j = ErrorReport.file_number;
47     if (j <= 0 || j > total_files) p = ErrorReport.source;
48     else p = InputFiles[j-1].filename;
49
50     if (!p) p = "";
51     
52     switch(error_format)
53     {
54         case 0:  /* RISC OS error message format */
55
56             if (!(ErrorReport.main_flag)) printf("\"%s\", ", p);
57             printf("line %d: ", ErrorReport.line_number);
58             if (ErrorReport.orig_file) {
59                 char *op;
60                 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
61                     op = ErrorReport.orig_source;
62                 else
63                     op = InputFiles[ErrorReport.orig_file-1].filename;
64                 printf("(\"%s\"", op);
65                 if (ErrorReport.orig_line) {
66                     printf(", %d", ErrorReport.orig_line);
67                     if (ErrorReport.orig_char) {
68                         printf(":%d", ErrorReport.orig_char);
69                     }
70                 }
71                 printf("): ");
72             }
73             break;
74
75         case 1:  /* Microsoft error message format */
76
77             for (j=0; p[j]!=0; j++)
78             {   if (p[j] == FN_SEP) with_extension_flag = TRUE;
79                 if (p[j] == '.') with_extension_flag = FALSE;
80             }
81             printf("%s", p);
82             if (with_extension_flag) printf("%s", Source_Extension);
83             printf("(%d): ", ErrorReport.line_number);
84             break;
85
86         case 2:  /* Macintosh Programmer's Workshop error message format */
87
88             printf("File \"%s\"; Line %d\t# ", p, ErrorReport.line_number);
89             break;
90     }
91 }
92
93 static void ellipsize_error_message_buff(void)
94 {
95     /* If the error buffer was actually filled up by a message, it was
96        probably truncated too. Add an ellipsis, for which we left
97        extra room. (Yes, yes; errors that are *exactly* 255 characters
98        long will suffer an unnecessary ellipsis.) */
99     if (strlen(error_message_buff) == ERROR_BUFLEN-1)
100         strcat(error_message_buff, "...");
101 }
102
103 /* ------------------------------------------------------------------------- */
104 /*   Fatal errors (which have style 0)                                       */
105 /* ------------------------------------------------------------------------- */
106
107 extern void fatalerror(char *s)
108 {   print_preamble();
109
110     printf("Fatal error: %s\n",s);
111     if (no_compiler_errors > 0) print_sorry_message();
112
113 #ifdef ARC_THROWBACK
114     throwback(0, s);
115     throwback_end();
116 #endif
117 #ifdef MAC_FACE
118     close_all_source();
119     if (temporary_files_switch) remove_temp_files();
120     abort_transcript_file();
121     free_arrays();
122     if (store_the_text)
123         my_free(&all_text,"transcription text");
124     longjmp(g_fallback, 1);
125 #endif
126     exit(1);
127 }
128
129 extern void fatalerror_named(char *m, char *fn)
130 {   snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"", m, fn);
131     ellipsize_error_message_buff();
132     fatalerror(error_message_buff);
133 }
134
135 extern void memory_out_error(int32 size, int32 howmany, char *name)
136 {   if (howmany == 1)
137         snprintf(error_message_buff, ERROR_BUFLEN,
138             "Run out of memory allocating %d bytes for %s", size, name);
139     else
140         snprintf(error_message_buff, ERROR_BUFLEN,
141             "Run out of memory allocating array of %dx%d bytes for %s",
142                 howmany, size, name);
143     ellipsize_error_message_buff();
144     fatalerror(error_message_buff);
145 }
146
147 extern void memoryerror(char *s, int32 size)
148 {
149     snprintf(error_message_buff, ERROR_BUFLEN,
150         "The memory setting %s (which is %ld at present) has been \
151 exceeded.  Try running Inform again with $%s=<some-larger-number> on the \
152 command line.",s,(long int) size,s);
153     ellipsize_error_message_buff();
154     fatalerror(error_message_buff);
155 }
156
157 /* ------------------------------------------------------------------------- */
158 /*   Survivable diagnostics:                                                 */
159 /*      compilation errors   style 1                                         */
160 /*      warnings             style 2                                         */
161 /*      linkage errors       style 3                                         */
162 /*      compiler errors      style 4 (these should never happen and          */
163 /*                                    indicate a bug in Inform)              */
164 /* ------------------------------------------------------------------------- */
165
166 static int errors[MAX_ERRORS];
167
168 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
169     no_compiler_errors;
170
171 char *forerrors_buff;
172 int  forerrors_pointer;
173
174 static void message(int style, char *s)
175 {   int throw_style = style;
176     if (hash_printed_since_newline) printf("\n");
177     hash_printed_since_newline = FALSE;
178     print_preamble();
179     switch(style)
180     {   case 1: printf("Error: "); no_errors++; break;
181         case 2: printf("Warning: "); no_warnings++; break;
182         case 3: printf("Error:  [linking '%s']  ", current_module_filename);
183                 no_link_errors++; no_errors++; throw_style=1; break;
184         case 4: printf("*** Compiler error: ");
185                 no_compiler_errors++; throw_style=1; break;
186     }
187     printf(" %s\n", s);
188 #ifdef ARC_THROWBACK
189     throwback(throw_style, s);
190 #endif
191 #ifdef MAC_FACE
192     ProcessEvents (&g_proc);
193     if (g_proc != true)
194     {   free_arrays();
195         if (store_the_text)
196             my_free(&all_text,"transcription text");
197         close_all_source ();
198         if (temporary_files_switch) remove_temp_files();
199         abort_transcript_file();
200         longjmp (g_fallback, 1);
201     }
202 #endif
203     if ((!concise_switch) && (forerrors_pointer > 0) && (style <= 2))
204     {   forerrors_buff[forerrors_pointer] = 0;
205         sprintf(forerrors_buff+68,"  ...etc");
206         printf("> %s\n",forerrors_buff);
207     }
208 }
209
210 /* ------------------------------------------------------------------------- */
211 /*   Style 1: Error message routines                                         */
212 /* ------------------------------------------------------------------------- */
213
214 extern void error(char *s)
215 {   if (no_errors == MAX_ERRORS)
216         fatalerror("Too many errors: giving up");
217     errors[no_errors] = no_syntax_lines;
218     message(1,s);
219 }
220
221 extern void error_named(char *s1, char *s2)
222 {   snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
223     ellipsize_error_message_buff();
224     error(error_message_buff);
225 }
226
227 extern void error_numbered(char *s1, int val)
228 {
229     snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val);
230     ellipsize_error_message_buff();
231     error(error_message_buff);
232 }
233
234 extern void error_named_at(char *s1, char *s2, brief_location report_line)
235 {   int i;
236
237     ErrorPosition E = ErrorReport;
238     export_brief_location(report_line, &ErrorReport);
239
240     snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
241     ellipsize_error_message_buff();
242
243     i = concise_switch; concise_switch = TRUE;
244     error(error_message_buff);
245     ErrorReport = E; concise_switch = i;
246 }
247
248 extern void no_such_label(char *lname)
249 {   error_named("No such label as",lname);
250 }
251
252 extern void ebf_error(char *s1, char *s2)
253 {   snprintf(error_message_buff, ERROR_BUFLEN, "Expected %s but found %s", s1, s2);
254     ellipsize_error_message_buff();
255     error(error_message_buff);
256 }
257
258 extern void char_error(char *s, int ch)
259 {   int32 uni;
260
261     uni = iso_to_unicode(ch);
262
263     if (character_set_unicode)
264         snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
265     else if (uni >= 0x100)
266     {   snprintf(error_message_buff, ERROR_BUFLEN,
267             "%s (unicode) $%04x = (ISO %s) $%02x", s, uni,
268             name_of_iso_set(character_set_setting), ch);
269     }
270     else
271         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
272
273     /* If the character set is set to Latin-1, and the char in question
274        is a printable Latin-1 character, we print it in the error message.
275        This conflates the source-text charset with the terminal charset,
276        really, but it's not a big deal. */
277
278     if (((uni>=32) && (uni<127))
279         || (((uni >= 0xa1) && (uni <= 0xff))
280         && (character_set_setting==1) && (!character_set_unicode))) 
281     {   int curlen = strlen(error_message_buff);
282         snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
283             ", i.e., '%c'", uni);
284     }
285
286     ellipsize_error_message_buff();
287     error(error_message_buff);
288 }
289
290 extern void unicode_char_error(char *s, int32 uni)
291 {
292     if (uni >= 0x100)
293         snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
294     else
295         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
296
297     /* See comment above. */
298
299     if (((uni>=32) && (uni<127))
300         || (((uni >= 0xa1) && (uni <= 0xff))
301         && (character_set_setting==1) && (!character_set_unicode)))
302     {   int curlen = strlen(error_message_buff);
303         snprintf(error_message_buff+curlen, ERROR_BUFLEN-curlen,
304             ", i.e., '%c'", uni);
305     }
306
307     ellipsize_error_message_buff();
308     error(error_message_buff);
309 }
310
311 /* ------------------------------------------------------------------------- */
312 /*   Style 2: Warning message routines                                       */
313 /* ------------------------------------------------------------------------- */
314
315 extern void warning(char *s1)
316 {   if (nowarnings_switch) { no_suppressed_warnings++; return; }
317     message(2,s1);
318 }
319
320 extern void warning_numbered(char *s1, int val)
321 {   if (nowarnings_switch) { no_suppressed_warnings++; return; }
322     snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.", s1, val);
323     ellipsize_error_message_buff();
324     message(2,error_message_buff);
325 }
326
327 extern void warning_named(char *s1, char *s2)
328 {
329     if (nowarnings_switch) { no_suppressed_warnings++; return; }
330     snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"", s1, s2);
331     ellipsize_error_message_buff();
332     message(2,error_message_buff);
333 }
334
335 extern void dbnu_warning(char *type, char *name, brief_location report_line)
336 {   int i;
337     ErrorPosition E = ErrorReport;
338     if (nowarnings_switch) { no_suppressed_warnings++; return; }
339     export_brief_location(report_line, &ErrorReport);
340     snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" declared but not used", type, name);
341     ellipsize_error_message_buff();
342     i = concise_switch; concise_switch = TRUE;
343     message(2,error_message_buff);
344     concise_switch = i;
345     ErrorReport = E;
346 }
347
348 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
349 {   int i;
350     /* This is called for functions which have been detected by the
351        track-unused-routines module. These will often (but not always)
352        be also caught by dbnu_warning(), which tracks symbols rather
353        than routine addresses. */
354     ErrorPosition E = ErrorReport;
355     if (nowarnings_switch) { no_suppressed_warnings++; return; }
356     export_brief_location(report_line, &ErrorReport);
357     if (OMIT_UNUSED_ROUTINES)
358         snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused and omitted", type, name);
359     else
360         snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\" unused (not omitted)", type, name);
361     ellipsize_error_message_buff();
362     i = concise_switch; concise_switch = TRUE;
363     message(2,error_message_buff);
364     concise_switch = i;
365     ErrorReport = E;
366 }
367
368 extern void obsolete_warning(char *s1)
369 {   if (is_systemfile()==1) return;
370     if (obsolete_switch || nowarnings_switch)
371     {   no_suppressed_warnings++; return; }
372     snprintf(error_message_buff, ERROR_BUFLEN, "Obsolete usage: %s",s1);
373     ellipsize_error_message_buff();
374     message(2,error_message_buff);
375 }
376
377 /* ------------------------------------------------------------------------- */
378 /*   Style 3: Link error message routines                                    */
379 /* ------------------------------------------------------------------------- */
380
381 extern void link_error(char *s)
382 {   if (no_errors==MAX_ERRORS) fatalerror("Too many errors: giving up");
383     errors[no_errors] = no_syntax_lines;
384     message(3,s);
385 }
386
387 extern void link_error_named(char *s1, char *s2)
388 {   snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
389     ellipsize_error_message_buff();
390     link_error(error_message_buff);
391 }
392
393 /* ------------------------------------------------------------------------- */
394 /*   Style 4: Compiler error message routines                                */
395 /* ------------------------------------------------------------------------- */
396
397 extern void print_sorry_message(void)
398 {   printf(
399 "***********************************************************************\n\
400 Compiler errors should never occur if Inform is working properly.\n\
401 Check to see if there is a more recent version available, from which\n\
402 the problem may have been removed. If not, please report this fault\n\
403 and if at all possible, please include your source code, as faults\n\
404 such as these are rare and often difficult to reproduce. Sorry.\n\
405 ***********************************************************************\n",
406     (RELEASE_NUMBER/100)%10, RELEASE_NUMBER%100, RELEASE_DATE);
407 }
408
409 extern int compiler_error(char *s)
410 {   if (no_link_errors > 0) return FALSE;
411     if (no_errors > 0) return FALSE;
412     if (no_compiler_errors==MAX_ERRORS)
413         fatalerror("Too many compiler errors: giving up");
414     message(4,s);
415     return TRUE;
416 }
417
418 extern int compiler_error_named(char *s1, char *s2)
419 {   if (no_link_errors > 0) return FALSE;
420     if (no_errors > 0) return FALSE;
421     snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"",s1,s2);
422     ellipsize_error_message_buff();
423     compiler_error(error_message_buff);
424     return TRUE;
425 }
426
427 /* ------------------------------------------------------------------------- */
428 /*   Code for the Acorn RISC OS operating system, donated by Robin Watts,    */
429 /*   to provide error throwback under the DDE environment                    */
430 /* ------------------------------------------------------------------------- */
431
432 #ifdef ARC_THROWBACK
433
434 #define DDEUtils_ThrowbackStart 0x42587
435 #define DDEUtils_ThrowbackSend  0x42588
436 #define DDEUtils_ThrowbackEnd   0x42589
437
438 #include "kernel.h"
439
440 extern void throwback_start(void)
441 {    _kernel_swi_regs regs;
442      if (throwback_switch)
443          _kernel_swi(DDEUtils_ThrowbackStart, &regs, &regs);
444 }
445
446 extern void throwback_end(void)
447 {   _kernel_swi_regs regs;
448     if (throwback_switch)
449         _kernel_swi(DDEUtils_ThrowbackEnd, &regs, &regs);
450 }
451
452 int throwback_started = FALSE;
453
454 extern void throwback(int severity, char * error)
455 {   _kernel_swi_regs regs;
456     if (!throwback_started)
457     {   throwback_started = TRUE;
458         throwback_start();
459     }
460     if (throwback_switch)
461     {   regs.r[0] = 1;
462         if ((ErrorReport.file_number == -1)
463             || (ErrorReport.file_number == 0))
464             regs.r[2] = (int) (InputFiles[0].filename);
465         else regs.r[2] = (int) (InputFiles[ErrorReport.file_number-1].filename);
466         regs.r[3] = ErrorReport.line_number;
467         regs.r[4] = (2-severity);
468         regs.r[5] = (int) error;
469        _kernel_swi(DDEUtils_ThrowbackSend, &regs, &regs);
470     }
471 }
472
473 #endif
474
475 /* ========================================================================= */
476 /*   Data structure management routines                                      */
477 /* ------------------------------------------------------------------------- */
478
479 extern void init_errors_vars(void)
480 {   forerrors_buff = NULL;
481     no_errors = 0; no_warnings = 0; no_suppressed_warnings = 0;
482     no_compiler_errors = 0;
483 }
484
485 extern void errors_begin_pass(void)
486 {   ErrorReport.line_number = 0;
487     ErrorReport.file_number = -1;
488     ErrorReport.source = "<no text read yet>";
489     ErrorReport.main_flag = FALSE;
490     ErrorReport.orig_source = NULL;
491     ErrorReport.orig_file = 0;
492     ErrorReport.orig_line = 0;
493     ErrorReport.orig_char = 0;
494 }
495
496 extern void errors_allocate_arrays(void)
497 {   forerrors_buff = my_malloc(512, "errors buffer");
498 }
499
500 extern void errors_free_arrays(void)
501 {   my_free(&forerrors_buff, "errors buffer");
502 }
503
504 /* ========================================================================= */