Commit | Line | Data |
---|---|---|
69695f33 MW |
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 | } |