Update to Inform v6.35
[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             
60             if (ErrorReport.orig_file) {
61                 char *op;
62                 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
63                     op = ErrorReport.orig_source;
64                 else
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);
71                     }
72                 }
73                 printf("): ");
74             }
75             break;
76
77         case 1:  /* Microsoft error message format */
78
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;
82             }
83             printf("%s", p);
84             if (with_extension_flag) printf("%s", Source_Extension);
85             printf("(%d)", ErrorReport.line_number);
86             
87             if (ErrorReport.orig_file) {
88                 char *op;
89                 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
90                     op = ErrorReport.orig_source;
91                 else
92                     op = InputFiles[ErrorReport.orig_file-1].filename;
93                 printf("|%s", op);
94                 if (ErrorReport.orig_line) {
95                     printf("(%d", ErrorReport.orig_line);
96                     if (ErrorReport.orig_char) {
97                         printf(":%d", ErrorReport.orig_char);
98                     }
99                     printf(")");
100                 }
101             }
102             
103             printf(": ");
104             break;
105
106         case 2:  /* Macintosh Programmer's Workshop error message format */
107
108             printf("File \"%s\"; Line %d", p, ErrorReport.line_number);
109             
110             if (ErrorReport.orig_file) {
111                 char *op;
112                 if (ErrorReport.orig_file <= 0 || ErrorReport.orig_file > total_files)
113                     op = ErrorReport.orig_source;
114                 else
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);
121                     }
122                 }
123                 printf(")");
124             }
125
126             printf("\t# ");
127             break;
128     }
129 }
130
131 static char *location_text(brief_location report_line)
132 {
133     int j;
134     char *p;
135     int len;
136
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. */
140     
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);
148     
149     j = errpos.file_number;
150     if (j <= 0 || j > total_files) p = errpos.source;
151     else p = InputFiles[j-1].filename;
152     
153     if (!p && errpos.line_number == 0) {
154         /* Special case */
155         strcpy(other_pos_buff, "compiler setup");
156         return other_pos_buff;
157     }
158     
159     if (!p) p = "";
160
161     len = 0;
162     
163     if (!(errpos.main_flag)) {
164         snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
165                  "\"%s\", ", p);
166         len = strlen(other_pos_buff);
167     }
168     snprintf(other_pos_buff+len, ERROR_BUFLEN-len,
169              "line %d", errpos.line_number);
170
171     return other_pos_buff;
172 }
173
174 static void ellipsize_error_message_buff(void)
175 {
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, "...");
182 }
183
184 /* ------------------------------------------------------------------------- */
185 /*   Fatal errors (which have style 0)                                       */
186 /* ------------------------------------------------------------------------- */
187
188 extern void fatalerror(char *s)
189 {   print_preamble();
190
191     printf("Fatal error: %s\n",s);
192     if (no_compiler_errors > 0) print_sorry_message();
193
194 #ifdef ARC_THROWBACK
195     throwback(0, s);
196     throwback_end();
197 #endif
198 #ifdef MAC_FACE
199     close_all_source();
200     if (temporary_files_switch) remove_temp_files();
201     abort_transcript_file();
202     free_arrays();
203     if (store_the_text)
204         my_free(&all_text,"transcription text");
205     longjmp(g_fallback, 1);
206 #endif
207     exit(1);
208 }
209
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);
214 }
215
216 extern void memory_out_error(int32 size, int32 howmany, char *name)
217 {   if (howmany == 1)
218         snprintf(error_message_buff, ERROR_BUFLEN,
219             "Run out of memory allocating %d bytes for %s", size, name);
220     else
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);
226 }
227
228 extern void memoryerror(char *s, int32 size)
229 {
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);
236 }
237
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 /* ------------------------------------------------------------------------- */
246
247 static int errors[MAX_ERRORS];
248
249 int no_errors, no_warnings, no_suppressed_warnings, no_link_errors,
250     no_compiler_errors;
251
252 char *forerrors_buff;
253 int  forerrors_pointer;
254
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;
259     print_preamble();
260     switch(style)
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;
267     }
268     printf(" %s\n", s);
269 #ifdef ARC_THROWBACK
270     throwback(throw_style, s);
271 #endif
272 #ifdef MAC_FACE
273     ProcessEvents (&g_proc);
274     if (g_proc != true)
275     {   free_arrays();
276         if (store_the_text)
277             my_free(&all_text,"transcription text");
278         close_all_source ();
279         if (temporary_files_switch) remove_temp_files();
280         abort_transcript_file();
281         longjmp (g_fallback, 1);
282     }
283 #endif
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);
288     }
289 }
290
291 /* ------------------------------------------------------------------------- */
292 /*   Style 1: Error message routines                                         */
293 /* ------------------------------------------------------------------------- */
294
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;
299     message(1,s);
300 }
301
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);
306 }
307
308 extern void error_numbered(char *s1, int val)
309 {
310     snprintf(error_message_buff, ERROR_BUFLEN,"%s %d.",s1,val);
311     ellipsize_error_message_buff();
312     error(error_message_buff);
313 }
314
315 extern void error_named_at(char *s1, char *s2, brief_location report_line)
316 {   int i;
317
318     ErrorPosition E = ErrorReport;
319     export_brief_location(report_line, &ErrorReport);
320
321     snprintf(error_message_buff, ERROR_BUFLEN,"%s \"%s\"",s1,s2);
322     ellipsize_error_message_buff();
323
324     i = concise_switch; concise_switch = TRUE;
325     error(error_message_buff);
326     ErrorReport = E; concise_switch = i;
327 }
328
329 extern void no_such_label(char *lname)
330 {   error_named("No such label as",lname);
331 }
332
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);
337 }
338
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);
343 }
344
345 extern void char_error(char *s, int ch)
346 {   int32 uni;
347
348     uni = iso_to_unicode(ch);
349
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);
356     }
357     else
358         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
359
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. */
364
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);
371     }
372
373     ellipsize_error_message_buff();
374     error(error_message_buff);
375 }
376
377 extern void unicode_char_error(char *s, int32 uni)
378 {
379     if (uni >= 0x100)
380         snprintf(error_message_buff, ERROR_BUFLEN, "%s (unicode) $%04x", s, uni);
381     else
382         snprintf(error_message_buff, ERROR_BUFLEN, "%s (ISO Latin1) $%02x", s, uni);
383
384     /* See comment above. */
385
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);
392     }
393
394     ellipsize_error_message_buff();
395     error(error_message_buff);
396 }
397
398 /* ------------------------------------------------------------------------- */
399 /*   Style 2: Warning message routines                                       */
400 /* ------------------------------------------------------------------------- */
401
402 extern void warning(char *s1)
403 {   if (nowarnings_switch) { no_suppressed_warnings++; return; }
404     message(2,s1);
405 }
406
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);
412 }
413
414 extern void warning_named(char *s1, char *s2)
415 {
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);
420 }
421
422 extern void dbnu_warning(char *type, char *name, brief_location report_line)
423 {   int i;
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);
431     concise_switch = i;
432     ErrorReport = E;
433 }
434
435 extern void uncalled_routine_warning(char *type, char *name, brief_location report_line)
436 {   int i;
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);
446     else
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);
451     concise_switch = i;
452     ErrorReport = E;
453 }
454
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);
462 }
463
464 /* ------------------------------------------------------------------------- */
465 /*   Style 3: Link error message routines                                    */
466 /* ------------------------------------------------------------------------- */
467
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;
471     message(3,s);
472 }
473
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);
478 }
479
480 /* ------------------------------------------------------------------------- */
481 /*   Style 4: Compiler error message routines                                */
482 /* ------------------------------------------------------------------------- */
483
484 extern void print_sorry_message(void)
485 {   printf(
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");
493 }
494
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");
500     message(4,s);
501     return TRUE;
502 }
503
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);
510     return TRUE;
511 }
512
513 /* ------------------------------------------------------------------------- */
514 /*   Code for the Acorn RISC OS operating system, donated by Robin Watts,    */
515 /*   to provide error throwback under the DDE environment                    */
516 /* ------------------------------------------------------------------------- */
517
518 #ifdef ARC_THROWBACK
519
520 #define DDEUtils_ThrowbackStart 0x42587
521 #define DDEUtils_ThrowbackSend  0x42588
522 #define DDEUtils_ThrowbackEnd   0x42589
523
524 #include "kernel.h"
525
526 extern void throwback_start(void)
527 {    _kernel_swi_regs regs;
528      if (throwback_switch)
529          _kernel_swi(DDEUtils_ThrowbackStart, &regs, &regs);
530 }
531
532 extern void throwback_end(void)
533 {   _kernel_swi_regs regs;
534     if (throwback_switch)
535         _kernel_swi(DDEUtils_ThrowbackEnd, &regs, &regs);
536 }
537
538 int throwback_started = FALSE;
539
540 extern void throwback(int severity, char * error)
541 {   _kernel_swi_regs regs;
542     if (!throwback_started)
543     {   throwback_started = TRUE;
544         throwback_start();
545     }
546     if (throwback_switch)
547     {   regs.r[0] = 1;
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, &regs, &regs);
556     }
557 }
558
559 #endif
560
561 /* ========================================================================= */
562 /*   Data structure management routines                                      */
563 /* ------------------------------------------------------------------------- */
564
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;
569 }
570
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;
580 }
581
582 extern void errors_allocate_arrays(void)
583 {   forerrors_buff = my_malloc(512, "errors buffer");
584 }
585
586 extern void errors_free_arrays(void)
587 {   my_free(&forerrors_buff, "errors buffer");
588 }
589
590 /* ========================================================================= */