Sync with upstream
[inform.git] / 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 - 2018                                   */
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 * This is version %d.%02d of Inform, dated %20s: so      *\n\
402 * if that was more than six months ago, there may be a more recent    *\n\
403 * version available, from which the problem may have been removed.    *\n\
404 * If not, please report this fault to:   graham@gnelson.demon.co.uk   *\n\
405 * and if at all possible, please include your source code, as faults  *\n\
406 * such as these are rare and often difficult to reproduce.  Sorry.    *\n\
407 ***********************************************************************\n",
408     (RELEASE_NUMBER/100)%10, RELEASE_NUMBER%100, RELEASE_DATE);
409 }
410
411 extern int compiler_error(char *s)
412 {   if (no_link_errors > 0) return FALSE;
413     if (no_errors > 0) return FALSE;
414     if (no_compiler_errors==MAX_ERRORS)
415         fatalerror("Too many compiler errors: giving up");
416     message(4,s);
417     return TRUE;
418 }
419
420 extern int compiler_error_named(char *s1, char *s2)
421 {   if (no_link_errors > 0) return FALSE;
422     if (no_errors > 0) return FALSE;
423     snprintf(error_message_buff, ERROR_BUFLEN, "%s \"%s\"",s1,s2);
424     ellipsize_error_message_buff();
425     compiler_error(error_message_buff);
426     return TRUE;
427 }
428
429 /* ------------------------------------------------------------------------- */
430 /*   Code for the Acorn RISC OS operating system, donated by Robin Watts,    */
431 /*   to provide error throwback under the DDE environment                    */
432 /* ------------------------------------------------------------------------- */
433
434 #ifdef ARC_THROWBACK
435
436 #define DDEUtils_ThrowbackStart 0x42587
437 #define DDEUtils_ThrowbackSend  0x42588
438 #define DDEUtils_ThrowbackEnd   0x42589
439
440 #include "kernel.h"
441
442 extern void throwback_start(void)
443 {    _kernel_swi_regs regs;
444      if (throwback_switch)
445          _kernel_swi(DDEUtils_ThrowbackStart, &regs, &regs);
446 }
447
448 extern void throwback_end(void)
449 {   _kernel_swi_regs regs;
450     if (throwback_switch)
451         _kernel_swi(DDEUtils_ThrowbackEnd, &regs, &regs);
452 }
453
454 int throwback_started = FALSE;
455
456 extern void throwback(int severity, char * error)
457 {   _kernel_swi_regs regs;
458     if (!throwback_started)
459     {   throwback_started = TRUE;
460         throwback_start();
461     }
462     if (throwback_switch)
463     {   regs.r[0] = 1;
464         if ((ErrorReport.file_number == -1)
465             || (ErrorReport.file_number == 0))
466             regs.r[2] = (int) (InputFiles[0].filename);
467         else regs.r[2] = (int) (InputFiles[ErrorReport.file_number-1].filename);
468         regs.r[3] = ErrorReport.line_number;
469         regs.r[4] = (2-severity);
470         regs.r[5] = (int) error;
471        _kernel_swi(DDEUtils_ThrowbackSend, &regs, &regs);
472     }
473 }
474
475 #endif
476
477 /* ========================================================================= */
478 /*   Data structure management routines                                      */
479 /* ------------------------------------------------------------------------- */
480
481 extern void init_errors_vars(void)
482 {   forerrors_buff = NULL;
483     no_errors = 0; no_warnings = 0; no_suppressed_warnings = 0;
484     no_compiler_errors = 0;
485 }
486
487 extern void errors_begin_pass(void)
488 {   ErrorReport.line_number = 0;
489     ErrorReport.file_number = -1;
490     ErrorReport.source = "<no text read yet>";
491     ErrorReport.main_flag = FALSE;
492     ErrorReport.orig_source = NULL;
493     ErrorReport.orig_file = 0;
494     ErrorReport.orig_line = 0;
495     ErrorReport.orig_char = 0;
496 }
497
498 extern void errors_allocate_arrays(void)
499 {   forerrors_buff = my_malloc(512, "errors buffer");
500 }
501
502 extern void errors_free_arrays(void)
503 {   my_free(&forerrors_buff, "errors buffer");
504 }
505
506 /* ========================================================================= */