chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / errors.c
1
2 /* Copyright (c) 1995, 1997, 1999, 2000, 2001, 2006 by Arkkra Enterprises */
3 /* All rights reserved */
4
5 /* various functions for printing error messages and exiting when
6  * things go wrong */
7
8 #include "globals.h"
9 #include "rational.h"
10
11 #ifdef __STDC__
12 #include <stdarg.h>
13 #else
14 #include <varargs.h>
15 #endif
16
17 #ifdef Mac_BBEdit
18 #include <BBEdit_Streams.h>
19 #endif
20
21 extern void exit P((int status));
22
23         int debug_on P((int level));
24
25 #ifdef __STDC__
26         extern void abort P((void));
27 #else
28         extern int abort();
29 #endif
30
31 static void error_header P((char *filename, int lineno, char * errtype));
32
33 /* Print a message for a user error, and exit with the value of Errorcount,
34  * or of MAX_ERRORS if > MAX_ERRORS */
35
36 /*VARARGS1*/
37 #ifdef __STDC__
38
39 void
40 ufatal(char *format, ...)
41
42 #else
43
44 void
45 ufatal(format, va_alist)
46
47 char *format;   /* printf style format */
48 va_dcl
49
50 #endif
51
52 {
53         va_list args;
54
55         /* we now have one more error */
56         Errorcount++;
57
58         /* print the specified message with a newline */
59 #ifdef __STDC__
60         va_start(args, format);
61 #else
62         va_start(args);
63 #endif
64
65 #ifndef UNIX_LIKE_FILES
66         mac_cleanup();
67 #endif
68
69         (void) fprintf(stderr, "\nfatal user error: ");
70         (void) vfprintf(stderr, format, args);
71         va_end(args);
72         (void) fprintf(stderr, "\n");
73
74         error_exit();
75 }
76
77 /* Print a message with filename and linenumber for a user error,
78  * and exit with the value of Errorcount, or of MAX_ERRORS if > MAX_ERRORS */
79
80 /*VARARGS3*/
81 #ifdef __STDC__
82
83 void
84 l_ufatal(char *filename, int lineno, char *format, ...)
85
86 #else
87
88 void
89 l_ufatal(filename, lineno, format, va_alist)
90
91 char *filename;
92 int lineno;
93 char *format;   /* printf style format */
94 va_dcl
95
96 #endif
97
98 {
99         va_list args;
100
101         /* we now have one more error */
102         Errorcount++;
103
104         /* print the specified message with a newline */
105 #ifdef __STDC__
106         va_start(args, format);
107 #else
108         va_start(args);
109 #endif
110
111 #ifndef UNIX_LIKE_FILES
112         mac_cleanup();
113 #endif
114
115         error_header(filename, lineno, "fatal user error");
116
117         (void) vfprintf(stderr, format, args);
118         va_end(args);
119         (void) fprintf(stderr, "\n");
120
121 #ifdef Mac_BBEdit
122         AppendError(filename, lineno);
123 #endif
124
125         error_exit();
126 }
127 \f
128
129 /* Print a message for a program internal error and exit with MAX_ERRORS */
130
131
132 /*VARARGS1*/
133
134 #ifdef __STDC__
135
136 void
137 pfatal(char *format, ...)
138
139 #else
140
141 void
142 pfatal(format, va_alist)
143
144 char *format;
145 va_dcl
146
147 #endif
148
149 {
150         va_list args;
151
152 #ifdef __STDC__
153         va_start(args, format);
154 #else
155         va_start(args);
156 #endif
157
158 #ifndef UNIX_LIKE_FILES
159         mac_cleanup();
160 #endif
161
162         /* print specified message with newline */
163         (void) fprintf(stderr, "\nfatal internal error: ");
164         (void) vfprintf(stderr, format, args);
165         va_end(args);
166         (void) fprintf(stderr, "\n");
167
168 #ifdef CORE_MESSAGE
169         (void) fprintf(stderr, "creating a core dump\n");
170 #endif
171         abort();
172
173         Errorcount = MAX_ERRORS;
174         error_exit();
175 }
176 \f
177
178 /* fatal error with line number and file name. Note that this should be the
179  * line number and file in the program, not the input file, ie, __LINE__
180  * and __FILE__. */
181
182 /*VARARGS3*/
183
184 #ifdef __STDC__
185
186 void
187 l_pfatal(char *filename, int lineno, char *format, ...)
188
189 #else
190
191 void
192 l_pfatal(filename, lineno, format, va_alist)
193
194 char *filename;         /* name of program file */
195 int lineno;             /* pgm line where error was discovered */
196 char *format;           /* printf format */
197 va_dcl
198
199 #endif
200
201 {
202         va_list args;
203
204 #ifdef __STDC__
205         va_start(args, format);
206 #else
207         va_start(args);
208 #endif
209
210         error_header(filename, lineno, "fatal internal error");
211         pfatal(format, args);
212         va_end(args);
213 }
214 \f
215
216 /* error exit for the common problem of malloc failures */
217
218 void
219 l_no_mem(filename, lineno)
220
221 char *filename;
222 int lineno;
223
224 {
225         l_pfatal(filename, lineno, "memory allocation failed");
226 }
227 \f
228
229 /* error exit for common error of not being able to open a specified file */
230
231 void
232 cant_open(filename)
233
234 char *filename;
235
236 {
237         ufatal("can't open '%s'", filename);
238 }
239 \f
240
241 /* Exit with exit code being the number of errors, unless there were
242  * too many of them to fit in an exit code, in which case MAX_ERRORS
243  * is used. MAX_ERRORS is used for internal errors. */
244
245 void
246 error_exit()
247
248 {
249         exit(Errorcount > MAX_ERRORS ? MAX_ERRORS : Errorcount);
250 }
251 \f
252
253 /* print a warning message */
254
255 /*VARARGS1*/
256
257 #ifdef __STDC__
258
259 void
260 warning(char *format, ...)
261
262 #else
263
264 void
265 warning(format, va_alist)
266
267 char *format;
268 va_dcl
269
270 #endif
271
272 {
273         va_list args;
274
275         if (Score.warn == NO) {
276                 return;
277         }
278 #ifdef __STDC__
279         va_start(args, format);
280 #else
281         va_start(args);
282 #endif
283
284         (void) fprintf(stderr, "warning: ");
285         (void) vfprintf(stderr, format, args);
286         (void) fprintf(stderr, "\n");
287         va_end(args);
288
289 #ifdef Mac_BBEdit
290         AppendWarning((char *) 0, -1);
291 #endif
292 }
293
294
295 /* warning message with file name and line number */
296
297 /*VARARGS3*/
298
299 #ifdef __STDC__
300
301 void
302 l_warning(char * filename, int lineno, char *format, ...)
303
304 #else
305
306 void
307 l_warning(filename, lineno, format, va_alist)
308
309 char *filename; /* name of program file */
310 int lineno;             /* pgm line where error was discovered */
311 char *format;           /* printf format */
312 va_dcl
313
314 #endif
315
316 {
317         va_list args;
318
319         if (Score.warn == NO) {
320                 return;
321         }
322 #ifdef __STDC__
323         va_start(args, format);
324 #else
325         va_start(args);
326 #endif
327
328         error_header(filename, lineno, "warning");
329         (void) vfprintf(stderr, format, args);
330         (void) fprintf(stderr, "\n");
331         va_end(args);
332
333 #ifdef Mac_BBEdit
334         AppendWarning(filename, lineno);
335 #endif
336 }
337 \f
338
339 /* varargs version of yyerror, passing a file and linenumber (or -1 for the
340  * lineno if you don't want a filename and linenumber printed) */
341
342 /*VARARGS3*/
343
344 #ifdef __STDC__
345
346 void
347 l_yyerror(char *fname, int lineno, char *format, ...)
348
349 #else
350
351 void
352 l_yyerror(fname, lineno, format, va_alist)
353
354 char *fname;
355 int lineno;
356 char *format;
357 va_dcl
358
359 #endif
360
361 {
362         va_list args;
363
364
365         /* if linenumber is zero or negative, assume this is special case of
366          * not being associated with a specific line, so don't print
367          * a line number */
368         if (lineno > 0) {
369                 /* There are cases where the parser has already looked ahead
370                  * to find the newline in order to fully match a grammar
371                  * rule. When that happens, the line number will already have
372                  * been incremented and our message would be off by one.
373                  * So catch that case and compensate. */
374                 if ( (lineno == yylineno) && (fname == Curr_filename) &&
375                                                 Last_was_newline == YES) {
376                         lineno--;
377                 }
378                 error_header(fname, lineno, "error");
379         }
380
381 #ifdef __STDC__
382         va_start(args, format);
383 #else
384         va_start(args);
385 #endif
386
387         (void) vfprintf(stderr, format, args);
388         va_end(args);
389         (void) fprintf(stderr, "\n");
390
391         /* if doing macro expansion, also tell where macro was defined */
392         mac_error();
393
394 #ifdef Mac_BBEdit
395         AppendError(fname, lineno);
396 #endif
397
398         Errorcount++;
399 }
400 \f
401
402
403
404 /* print a debugging message if corresponding debugging bit is on */
405
406 /*VARARGS2*/
407
408 #ifdef __STDC__
409
410 void
411 debug(int level, char *format, ...)
412
413 #else
414
415 void
416 debug(level, format, va_alist)
417
418 int level;              /* debugging flag bitmap */
419 char *format;           /* printf style format */
420 va_dcl
421
422 #endif
423
424 {
425         va_list args;
426
427         if (debug_on(level)) {
428 #ifdef __STDC__
429                 va_start(args, format);
430 #else
431                 va_start(args);
432 #endif
433                 (void) vfprintf(stderr, format, args);
434                 va_end(args);
435                 (void) fprintf(stderr, "\n");
436         }
437 }
438 \f
439
440 /* return AND of Debuglevel and argument. Useful for other debug functions
441  * that want to see if a given debug level is currently turned on */
442
443 int
444 debug_on(level)
445
446 int level;
447
448 {
449         return(Debuglevel & level);
450 }
451
452 /* if we get an error while doing rational arithmetic, we are in deep
453  * trouble, so print message and get out. */
454
455 void
456 doraterr(code)
457
458 int code;
459
460 {
461         switch (code) {
462
463         case RATOVER:
464                 pfatal("rational overflow");
465                 /*NOTREACHED*/
466                 break;
467
468         case RATDIV0:
469                 pfatal("rational division by zero");
470                 /*NOTREACHED*/
471                 break;
472
473         case RATPARM:
474                 pfatal("invalid rational number parameter");
475                 /*NOTREACHED*/
476                 break;
477
478         default:
479                 pfatal("error in rational arithmetic routines");
480                 /*NOTREACHED*/
481                 break;
482         }
483 }
484 \f
485
486 /* Print header for an error report. If the error is associated with a
487  * particular line, the file name and line number and the text of the line
488  * is printed, but only on the first of multiple errors for the same line.
489  * If not associated with any line, a blank line is produced.
490  */
491
492 static void
493 error_header(filename, lineno, errtype)
494
495 char * filename;
496 int lineno;
497 char * errtype;         /* "warning" or "error" etc */
498
499 {
500         static char *cached_filename = 0;
501         static int cached_lineno = -1;
502
503         if (filename == 0 || lineno <= 0) {
504                 (void) fprintf(stderr, "\n");
505                 return;
506         }
507
508         /* We print the text of the offending line, unless it is the
509          * same as the last error, in which case we could have already
510          * printed it, so no need to print again. */
511         if (cached_filename != filename || cached_lineno != lineno) {
512                 (void) fprintf(stderr,"\n%s: line %d:\n", filename, lineno);
513                 print_offending_line(filename, lineno);
514         }
515         (void) fprintf(stderr,"%s: ", errtype);
516         cached_filename = filename;
517         cached_lineno = lineno;
518 }
519 \f
520
521 /* Print the text of input line where error was found. */
522
523 void
524 print_offending_line(filename, lineno)
525
526 char *filename;
527 int lineno;
528
529 {
530         /* We cache file info to save time when multiple errors */
531         static FILE *f = 0;
532         static char *prev_filename = 0;
533         static int prev_lineno = 0;
534         int inp;        /* a byte read from file */
535         int skipcount;  /* how many lines to skip past */
536
537         /* We try to reuse already opened file to save time,
538          * but need to open new file when necessary. */
539         if (f == 0 || prev_filename != filename) {
540                 /* close any previously open file */
541                 if (f != 0) {
542                         (void) fclose(f);
543                 }
544                 /* Note that if Mup is reading from stdin, we will try to
545                  * open a file entitled "stdin" here. That will almost
546                  * certainly fail, but we can't easily get the line from
547                  * stdin anyway, so user just won't get the context in that
548                  * case. People who are having Mup read from stdin are probably
549                  * savvy enough that this isn't a big problem. (We never used
550                  * to print this context ever, and no one complained.)
551                  * So the only weird case is if the user happens to have a
552                  * file whose name is literally "stdin" but the actual stdin
553                  * being read is some other file, in which case they'll get
554                  * garbage. But anyone using stdin for a file name is probably
555                  * smart enough to figure out the strange results in that case.
556                  * In the case of include files found via $MUPPATH,
557                  * filename will have already been expanded to a full path,
558                  * so we don't have to do anything special here for them.
559                  */
560                 if ((f = fopen(filename, "r")) == 0) {
561                         return;
562                 }
563                 skipcount = lineno - 1;
564                 prev_filename = filename;
565         }
566         else if (lineno > prev_lineno) {
567                 /* We can continue where we left off in the file */
568                 skipcount = lineno - prev_lineno - 1;
569         }
570         else {
571                 /* Earlier line in same file; easiest to just start over.
572                  * This could happen, because sometimes we don't realize
573                  * there is an error until a later line (E.g., only when
574                  * getting to bar line we find inconsistency in the 
575                  * contents of the bar.) */
576                 rewind(f);
577                 skipcount = lineno - 1;
578         }
579
580         /* Skip to the line of interest and print it. */
581         for (inp = 0; skipcount > 0; skipcount--) {
582                 /* We read byte-by-byte so we don't need to guess
583                  * how big a buffer to use to hold a line.
584                  */
585                 while ((inp = getc(f)) != '\n' && inp != EOF) {
586                         ;
587                 }
588                 if (inp == EOF) {
589                         break;
590                 }
591         }
592         if (inp != EOF) {
593                 (void) fprintf(stderr, "    ");
594                 while ((inp = getc(f)) != '\n' && inp != EOF) {
595                         putc(inp, stderr);
596                 }
597                 putc('\n', stderr);
598         }
599         prev_lineno = lineno;
600         /* Note that we leave the file open, to save time
601          * in case we need to read more from it
602          * due to additional errors from the same file.
603          */
604 }