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 }