chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / errors.c
... / ...
CommitLineData
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
21extern 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
31static 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
39void
40ufatal(char *format, ...)
41
42#else
43
44void
45ufatal(format, va_alist)
46
47char *format; /* printf style format */
48va_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
83void
84l_ufatal(char *filename, int lineno, char *format, ...)
85
86#else
87
88void
89l_ufatal(filename, lineno, format, va_alist)
90
91char *filename;
92int lineno;
93char *format; /* printf style format */
94va_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
136void
137pfatal(char *format, ...)
138
139#else
140
141void
142pfatal(format, va_alist)
143
144char *format;
145va_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
186void
187l_pfatal(char *filename, int lineno, char *format, ...)
188
189#else
190
191void
192l_pfatal(filename, lineno, format, va_alist)
193
194char *filename; /* name of program file */
195int lineno; /* pgm line where error was discovered */
196char *format; /* printf format */
197va_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
218void
219l_no_mem(filename, lineno)
220
221char *filename;
222int 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
231void
232cant_open(filename)
233
234char *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
245void
246error_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
259void
260warning(char *format, ...)
261
262#else
263
264void
265warning(format, va_alist)
266
267char *format;
268va_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
301void
302l_warning(char * filename, int lineno, char *format, ...)
303
304#else
305
306void
307l_warning(filename, lineno, format, va_alist)
308
309char *filename; /* name of program file */
310int lineno; /* pgm line where error was discovered */
311char *format; /* printf format */
312va_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
346void
347l_yyerror(char *fname, int lineno, char *format, ...)
348
349#else
350
351void
352l_yyerror(fname, lineno, format, va_alist)
353
354char *fname;
355int lineno;
356char *format;
357va_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
410void
411debug(int level, char *format, ...)
412
413#else
414
415void
416debug(level, format, va_alist)
417
418int level; /* debugging flag bitmap */
419char *format; /* printf style format */
420va_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
443int
444debug_on(level)
445
446int 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
455void
456doraterr(code)
457
458int 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
492static void
493error_header(filename, lineno, errtype)
494
495char * filename;
496int lineno;
497char * 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
523void
524print_offending_line(filename, lineno)
525
526char *filename;
527int 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}