c1e62412873f35669fdde5852bfb38fd56ceef0e
[inform.git] / src / errors.c
1 /* ------------------------------------------------------------------------- */
2 /*   "errors" : Warnings, errors and fatal errors                            */
3 /*              (with error throwback code for RISC OS machines)             */
4 /*                                                                           */
5 /*   Part of Inform 6.35                                                     */
6 /*   copyright (c) Graham Nelson 1993 - 2020                                 */
7 /*                                                                           */
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.                                       */
12 /*                                                                           */
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.                              */
17 /*                                                                           */
18 /* You should have received a copy of the GNU General Public License         */
19 /* along with Inform. If not, see https://gnu.org/licenses/                  */
20 /*                                                                           */
21 /* ------------------------------------------------------------------------- */
22
23 #include "header.h"
24
25 #define ERROR_BUFLEN (256)
26 static char error_message_buff[ERROR_BUFLEN+4]; /* room for ellipsis */
27
28 /* ------------------------------------------------------------------------- */
29 /*   Error preamble printing.                                                */
30 /* ------------------------------------------------------------------------- */
31
32 ErrorPosition ErrorReport;             /*  Maintained by "lexer.c"           */
33
34 static char other_pos_buff[ERROR_BUFLEN+1];       /* Used by location_text() */
35
36 static void print_preamble(void)
37 {
38     /*  Only really prints the preamble to an error or warning message:
39
40         e.g.  "jigsaw.apollo", line 24:
41
42         The format is controllable (from an ICL switch) since this assists
43         the working of some development environments.                        */
44
45     int j, with_extension_flag = FALSE; char *p;
46
47     j = ErrorReport.file_number;
48     if (j <= 0 || j > total_files) p = ErrorReport.source;
49     else p = InputFiles[j-1].filename;
50
51     if (!p) p = "";
52     
53     switch(error_format)
54     {
55         case 0:  /* RISC OS error message format */
56
57             if (!(ErrorReport.main_flag)) printf("\"%s\", ", p);
58             printf("line %d: ", ErrorReport.line_number);
59             if (ErrorReport.orig_file) {
60                 char *op;
61                 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
62                     op = ErrorReport.orig_source;
63                 else
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);
70                     }
71                 }
72                 printf("): ");
73             }
74             break;
75
76         case 1:  /* Microsoft error message format */
77
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;
81             }
82             printf("%s", p);
83             if (with_extension_flag) printf("%s", Source_Extension);
84             printf("(%d): ", ErrorReport.line_number);
85             break;
86
87         case 2:  /* Macintosh Programmer's Workshop error message format */
88
89             printf("File \"%s\"; Line %d\t# ", p, ErrorReport.line_number);
90             break;
91     }
92 }
93
94 static char *location_text(brief_location report_line)
95 {
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. */
99     
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);
107
108     int j;
109     char *p;
110     
111     j = errpos.file_number;
112     if (j <= 0 || j > total_files) p = errpos.source;
113     else p = InputFiles[j-1].filename;
114     
115     if (!p) p = "";
116
117     int len = 0;
118     
119     if (!(errpos.main_flag)) {
120         snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
121                  "\"%s\", ", p);
122         len = strlen(other_pos_buff);
123     }
124     snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
125              "line %d", errpos.line_number);
126
127     return other_pos_buff;
128 }
129
130 static void ellipsize_error_message_buff(void)
131 {
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, "...");
138 }
139
140 /* ------------------------------------------------------------------------- */
141 /*   Fatal errors (which have style 0)                                       */
142 /* ------------------------------------------------------------------------- */
143
144 extern void fatalerror(char *s)
145 {   print_preamble();
146
147     printf("Fatal error: %s\n",s);
148     if (no_compiler_errors > 0) print_sorry_message();
149
150 #ifdef ARC_THROWBACK
151     throwback(0, s);
152     throwback_end();
153 #endif
154 #ifdef MAC_FACE
155     close_all_source();
156     if (temporary_files_switch) remove_temp_files();
157     abort_transcript_file();
158     free_arrays();
159     if (store_the_text)
160         my_free(&all_text,"transcription text");
161     longjmp(g_fallback, 1);
162 #endif
163     exit(1);
164 }
165
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);
170 }
171
172 extern void memory_out_error(int32 size, int32 howmany, char *name)
173 {   if (howmany == 1)
174         snprintf(error_message_buff, ERROR_BUFLEN,
175             "Run out of memory allocating %d bytes for %s", size, name);
176     else
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);
182 }
183
184 extern void memoryerror(char *s, int32 size)
185 {
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);
192 }
193
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 /* ------------------------------------------------------------------------- */
202
203 static int errors[MAX_ERRORS];
204
205 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
206     no_compiler_errors;
207
208 char *forerrors_buff;
209 int  forerrors_pointer;
210
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;
215     print_preamble();
216     switch(style)
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;
223     }
224     printf(" %s\n", s);
225 #ifdef ARC_THROWBACK
226     throwback(throw_style, s);
227 #endif
228 #ifdef MAC_FACE
229     ProcessEvents (&g_proc);
230     if (g_proc != true)
231     {   free_arrays();
232         if (store_the_text)
233             my_free(&all_text,"transcription text");
234         close_all_source ();
235         if (temporary_files_switch) remove_temp_files();
236         abort_transcript_file();
237         longjmp (g_fallback, 1);
238     }
239 #endif
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);
244     }
245 }
246
247 /* ------------------------------------------------------------------------- */
248 /*   Style 1: Error message routines                                         */
249 /* ------------------------------------------------------------------------- */
250
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;
255     message(1,s);
256 }
257
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);
262 }
263
264 extern void error_numbered(char *s1, int val)
265 {
266     snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val);
267     ellipsize_error_message_buff();
268     error(error_message_buff);
269 }
270
271 extern void error_named_at(char *s1, char *s2, brief_location report_line)
272 {   int i;
273
274     ErrorPosition E = ErrorReport;
275     export_brief_location(report_line, &ErrorReport);
276
277     snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
278     ellipsize_error_message_buff();
279
280     i = concise_switch; concise_switch = TRUE;
281     error(error_message_buff);
282     ErrorReport = E; concise_switch = i;
283 }
284
285 extern void no_such_label(char *lname)
286 {   error_named("No such label as",lname);
287 }
288
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);
293 }
294
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);
299 }
300
301 extern void char_error(char *s, int ch)
302 {   int32 uni;
303
304     uni = iso_to_unicode(ch);
305
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);
312     }
313     else
314         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
315
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. */
320
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);
327     }
328
329     ellipsize_error_message_buff();
330     error(error_message_buff);
331 }
332
333 extern void unicode_char_error(char *s, int32 uni)
334 {
335     if (uni >= 0x100)
336         snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
337     else
338         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
339
340     /* See comment above. */
341
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);
348     }
349
350     ellipsize_error_message_buff();
351     error(error_message_buff);
352 }
353
354 /* ------------------------------------------------------------------------- */
355 /*   Style 2: Warning message routines                                       */
356 /* ------------------------------------------------------------------------- */
357
358 extern void warning(char *s1)
359 {   if (nowarnings_switch) { no_suppressed_warnings++; return; }
360     message(2,s1);
361 }
362
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);
368 }
369
370 extern void warning_named(char *s1, char *s2)
371 {
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);
376 }
377
378 extern void dbnu_warning(char *type, char *name, brief_location report_line)
379 {   int i;
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);
387     concise_switch = i;
388     ErrorReport = E;
389 }
390
391 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
392 {   int i;
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);
402     else
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);
407     concise_switch = i;
408     ErrorReport = E;
409 }
410
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);
418 }
419
420 /* ------------------------------------------------------------------------- */
421 /*   Style 3: Link error message routines                                    */
422 /* ------------------------------------------------------------------------- */
423
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;
427     message(3,s);
428 }
429
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);
434 }
435
436 /* ------------------------------------------------------------------------- */
437 /*   Style 4: Compiler error message routines                                */
438 /* ------------------------------------------------------------------------- */
439
440 extern void print_sorry_message(void)
441 {   printf(
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");
449 }
450
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");
456     message(4,s);
457     return TRUE;
458 }
459
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);
466     return TRUE;
467 }
468
469 /* ------------------------------------------------------------------------- */
470 /*   Code for the Acorn RISC OS operating system, donated by Robin Watts,    */
471 /*   to provide error throwback under the DDE environment                    */
472 /* ------------------------------------------------------------------------- */
473
474 #ifdef ARC_THROWBACK
475
476 #define DDEUtils_ThrowbackStart 0x42587
477 #define DDEUtils_ThrowbackSend  0x42588
478 #define DDEUtils_ThrowbackEnd   0x42589
479
480 #include "kernel.h"
481
482 extern void throwback_start(void)
483 {    _kernel_swi_regs regs;
484      if (throwback_switch)
485          _kernel_swi(DDEUtils_ThrowbackStart, &regs, &regs);
486 }
487
488 extern void throwback_end(void)
489 {   _kernel_swi_regs regs;
490     if (throwback_switch)
491         _kernel_swi(DDEUtils_ThrowbackEnd, &regs, &regs);
492 }
493
494 int throwback_started = FALSE;
495
496 extern void throwback(int severity, char * error)
497 {   _kernel_swi_regs regs;
498     if (!throwback_started)
499     {   throwback_started = TRUE;
500         throwback_start();
501     }
502     if (throwback_switch)
503     {   regs.r[0] = 1;
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, &regs, &regs);
512     }
513 }
514
515 #endif
516
517 /* ========================================================================= */
518 /*   Data structure management routines                                      */
519 /* ------------------------------------------------------------------------- */
520
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;
525 }
526
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;
536 }
537
538 extern void errors_allocate_arrays(void)
539 {   forerrors_buff = my_malloc(512, "errors buffer");
540 }
541
542 extern void errors_free_arrays(void)
543 {   my_free(&forerrors_buff, "errors buffer");
544 }
545
546 /* ========================================================================= */