Update to commit af5309356bfa197d7a7ea09101c317f94e9b856b
[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 - 2021                                 */
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     int j;
97     char *p;
98     int len;
99
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. */
103     
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);
111     
112     j = errpos.file_number;
113     if (j <= 0 || j > total_files) p = errpos.source;
114     else p = InputFiles[j-1].filename;
115     
116     if (!p) p = "";
117
118     len = 0;
119     
120     if (!(errpos.main_flag)) {
121         snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
122                  "\"%s\", ", p);
123         len = strlen(other_pos_buff);
124     }
125     snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
126              "line %d", errpos.line_number);
127
128     return other_pos_buff;
129 }
130
131 static void ellipsize_error_message_buff(void)
132 {
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, "...");
139 }
140
141 /* ------------------------------------------------------------------------- */
142 /*   Fatal errors (which have style 0)                                       */
143 /* ------------------------------------------------------------------------- */
144
145 extern void fatalerror(char *s)
146 {   print_preamble();
147
148     printf("Fatal error: %s\n",s);
149     if (no_compiler_errors > 0) print_sorry_message();
150
151 #ifdef ARC_THROWBACK
152     throwback(0, s);
153     throwback_end();
154 #endif
155 #ifdef MAC_FACE
156     close_all_source();
157     if (temporary_files_switch) remove_temp_files();
158     abort_transcript_file();
159     free_arrays();
160     if (store_the_text)
161         my_free(&all_text,"transcription text");
162     longjmp(g_fallback, 1);
163 #endif
164     exit(1);
165 }
166
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);
171 }
172
173 extern void memory_out_error(int32 size, int32 howmany, char *name)
174 {   if (howmany == 1)
175         snprintf(error_message_buff, ERROR_BUFLEN,
176             "Run out of memory allocating %d bytes for %s", size, name);
177     else
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);
183 }
184
185 extern void memoryerror(char *s, int32 size)
186 {
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);
193 }
194
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 /* ------------------------------------------------------------------------- */
203
204 static int errors[MAX_ERRORS];
205
206 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
207     no_compiler_errors;
208
209 char *forerrors_buff;
210 int  forerrors_pointer;
211
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;
216     print_preamble();
217     switch(style)
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;
224     }
225     printf(" %s\n", s);
226 #ifdef ARC_THROWBACK
227     throwback(throw_style, s);
228 #endif
229 #ifdef MAC_FACE
230     ProcessEvents (&g_proc);
231     if (g_proc != true)
232     {   free_arrays();
233         if (store_the_text)
234             my_free(&all_text,"transcription text");
235         close_all_source ();
236         if (temporary_files_switch) remove_temp_files();
237         abort_transcript_file();
238         longjmp (g_fallback, 1);
239     }
240 #endif
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);
245     }
246 }
247
248 /* ------------------------------------------------------------------------- */
249 /*   Style 1: Error message routines                                         */
250 /* ------------------------------------------------------------------------- */
251
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;
256     message(1,s);
257 }
258
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);
263 }
264
265 extern void error_numbered(char *s1, int val)
266 {
267     snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val);
268     ellipsize_error_message_buff();
269     error(error_message_buff);
270 }
271
272 extern void error_named_at(char *s1, char *s2, brief_location report_line)
273 {   int i;
274
275     ErrorPosition E = ErrorReport;
276     export_brief_location(report_line, &ErrorReport);
277
278     snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
279     ellipsize_error_message_buff();
280
281     i = concise_switch; concise_switch = TRUE;
282     error(error_message_buff);
283     ErrorReport = E; concise_switch = i;
284 }
285
286 extern void no_such_label(char *lname)
287 {   error_named("No such label as",lname);
288 }
289
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);
294 }
295
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);
300 }
301
302 extern void char_error(char *s, int ch)
303 {   int32 uni;
304
305     uni = iso_to_unicode(ch);
306
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);
313     }
314     else
315         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
316
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. */
321
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);
328     }
329
330     ellipsize_error_message_buff();
331     error(error_message_buff);
332 }
333
334 extern void unicode_char_error(char *s, int32 uni)
335 {
336     if (uni >= 0x100)
337         snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
338     else
339         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
340
341     /* See comment above. */
342
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);
349     }
350
351     ellipsize_error_message_buff();
352     error(error_message_buff);
353 }
354
355 /* ------------------------------------------------------------------------- */
356 /*   Style 2: Warning message routines                                       */
357 /* ------------------------------------------------------------------------- */
358
359 extern void warning(char *s1)
360 {   if (nowarnings_switch) { no_suppressed_warnings++; return; }
361     message(2,s1);
362 }
363
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);
369 }
370
371 extern void warning_named(char *s1, char *s2)
372 {
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);
377 }
378
379 extern void dbnu_warning(char *type, char *name, brief_location report_line)
380 {   int i;
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);
388     concise_switch = i;
389     ErrorReport = E;
390 }
391
392 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
393 {   int i;
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);
403     else
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);
408     concise_switch = i;
409     ErrorReport = E;
410 }
411
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);
419 }
420
421 /* ------------------------------------------------------------------------- */
422 /*   Style 3: Link error message routines                                    */
423 /* ------------------------------------------------------------------------- */
424
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;
428     message(3,s);
429 }
430
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);
435 }
436
437 /* ------------------------------------------------------------------------- */
438 /*   Style 4: Compiler error message routines                                */
439 /* ------------------------------------------------------------------------- */
440
441 extern void print_sorry_message(void)
442 {   printf(
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");
450 }
451
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");
457     message(4,s);
458     return TRUE;
459 }
460
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);
467     return TRUE;
468 }
469
470 /* ------------------------------------------------------------------------- */
471 /*   Code for the Acorn RISC OS operating system, donated by Robin Watts,    */
472 /*   to provide error throwback under the DDE environment                    */
473 /* ------------------------------------------------------------------------- */
474
475 #ifdef ARC_THROWBACK
476
477 #define DDEUtils_ThrowbackStart 0x42587
478 #define DDEUtils_ThrowbackSend  0x42588
479 #define DDEUtils_ThrowbackEnd   0x42589
480
481 #include "kernel.h"
482
483 extern void throwback_start(void)
484 {    _kernel_swi_regs regs;
485      if (throwback_switch)
486          _kernel_swi(DDEUtils_ThrowbackStart, &regs, &regs);
487 }
488
489 extern void throwback_end(void)
490 {   _kernel_swi_regs regs;
491     if (throwback_switch)
492         _kernel_swi(DDEUtils_ThrowbackEnd, &regs, &regs);
493 }
494
495 int throwback_started = FALSE;
496
497 extern void throwback(int severity, char * error)
498 {   _kernel_swi_regs regs;
499     if (!throwback_started)
500     {   throwback_started = TRUE;
501         throwback_start();
502     }
503     if (throwback_switch)
504     {   regs.r[0] = 1;
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, &regs, &regs);
513     }
514 }
515
516 #endif
517
518 /* ========================================================================= */
519 /*   Data structure management routines                                      */
520 /* ------------------------------------------------------------------------- */
521
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;
526 }
527
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;
537 }
538
539 extern void errors_allocate_arrays(void)
540 {   forerrors_buff = my_malloc(512, "errors buffer");
541 }
542
543 extern void errors_free_arrays(void)
544 {   my_free(&forerrors_buff, "errors buffer");
545 }
546
547 /* ========================================================================= */