1
2
3 /** **********************************************************************
4 ** Module: Error \file error.c
5 ** This module implements the ERROR abstraction
6 ************************************************************************/
7
8 /*
9 * Development of this code was funded by the United States Government,
10 * and is not subject to copyright.
11 *
12 * $Log: error.c,v $
13 * Revision 1.13 1997/10/23 21:41:44 sauderd
14 * I am backing out to version 1.10 before the changes related to va_list and
15 * __STDC__ etc. I don't have time to finish fixing all this.
16 *
17 * Revision 1.12 1997/10/23 21:35:31 sauderd
18 * Changed more mess with compiler directives for __STDC__ and HAVE_STDARG_H
19 * but am getting deeper into problems because of how PROTO is defined for
20 * dealing with prototypes based on standard C or not. PROTO is broken as well
21 * so I'm not going down this road further. Next I will back out but am this
22 * far ahead for later if we fix this.
23 *
24 * Revision 1.11 1997/10/22 16:10:26 sauderd
25 * This would #include stdarg.h if __STDC__ was defined otherwise it would
26 * #include vararg.h. I changed it to check the configure generated config file
27 * named sc_cf.h (if HAVE_CONFIG_H is defined - it's also defined by
28 * configure) to see if HAVE_STDARG_H is defined. If it is it #includes stdarg.h
29 * otherwise it #includes vararg.h. If HAVE_CONFIG_H isn't defined then it works
30 * like it used to.
31 *
32 * Revision 1.10 1997/01/21 19:19:51 dar
33 * made C++ compatible
34 *
35 * Revision 1.9 1994/05/11 19:51:46 libes
36 * numerous fixes
37 *
38 * Revision 1.8 1993/10/15 18:49:55 libes
39 * CADDETC certified
40 *
41 * Revision 1.6 1993/02/22 21:44:34 libes
42 * ANSI compat fixes
43 *
44 * Revision 1.5 1993/01/19 22:45:07 libes
45 * *** empty log message ***
46 *
47 * Revision 1.4 1992/08/18 17:16:22 libes
48 * rm'd extraneous error messages
49 *
50 * Revision 1.3 1992/06/08 18:08:05 libes
51 * prettied up interface to print_objects_when_running
52 */
53
54 #include <sc_memmgr.h>
55 #include <stdlib.h>
56 #include <setjmp.h>
57 #include <signal.h>
58 #include <string.h>
59
60 #ifdef __STDC__
61 #include <stdarg.h>
62 #else
63 #include <varargs.h>
64 #endif
65
66 #include "express/error.h"
67 #include "express/info.h"
68 #include "express/linklist.h"
69
70
71 bool __ERROR_buffer_errors = false;
72 const char * current_filename = "stdin";
73
74 /* flag to remember whether non-warning errors have occurred */
75 bool ERRORoccurred = false;
76
77
78 Error experrc = ERROR_none;
79 Error ERROR_subordinate_failed = ERROR_none;
80 Error ERROR_syntax_expecting = ERROR_none;
81
82 /* all of these are 1 if true, 0 if false switches */
83 /* for debugging fedex */
84 int ERRORdebugging = 0;
85 /* for debugging malloc during resolution */
86 int malloc_debug_resolve = 0;
87 /* for debugging yacc/lex */
88 int debug = 0;
89
90 struct Linked_List_ * ERRORwarnings;
91 struct freelist_head ERROR_OPT_fl;
92
93 void ( *ERRORusage_function )( void );
94
95 #include "express/express.h"
96
97 #define ERROR_MAX_ERRORS 100 /**< max line-numbered errors */
98 #define ERROR_MAX_SPACE 4000 /**< max space for line-numbered errors */
99 #define ERROR_MAX_STRLEN 200 /**< assuming all error messages are less than this,
100 * if we have less than this much space remaining
101 * in the error string buffer, call it a day and
102 * dump the buffer */
103
104 static struct heap_element {
105 int line;
106 char * msg;
107 } heap[ERROR_MAX_ERRORS + 1]; /**< NOTE! element 0 is purposely ignored, and
108 * an additional element is at the end. This
109 * allows the later heap calculations to be
110 * much simpler */
111
112 static int ERROR_with_lines = 0; /**< number of warnings & errors that have occurred with a line number */
113 static char * ERROR_string;
114 static char * ERROR_string_base;
115
116 static bool ERROR_unsafe = false;
117 static jmp_buf ERROR_safe_env;
118
119
120 #define error_file stderr /**< message buffer file */
121
122 /** Initialize the Error module */
ERRORinitialize(void)123 void ERRORinitialize( void ) {
124 ERROR_subordinate_failed =
125 ERRORcreate( "A subordinate failed.", SEVERITY_ERROR );
126 ERROR_syntax_expecting =
127 ERRORcreate( "%s, expecting %s in %s %s", SEVERITY_EXIT );
128
129 ERROR_string_base = ( char * )sc_malloc( ERROR_MAX_SPACE );
130 ERROR_start_message_buffer();
131
132
133 #ifdef SIGQUIT
134 signal( SIGQUIT, ERRORabort );
135 #endif
136 #ifdef SIGBUS
137 signal( SIGBUS, ERRORabort );
138 #endif
139 #ifdef SIGSEGV
140 signal( SIGSEGV, ERRORabort );
141 #endif
142 #ifdef SIGABRT
143 signal( SIGABRT, ERRORabort );
144 #endif
145 }
146
147 /** Clean up the Error module */
ERRORcleanup(void)148 void ERRORcleanup( void ) {
149 ERRORdestroy( ERROR_subordinate_failed );
150 ERRORdestroy( ERROR_syntax_expecting );
151
152 sc_free( ERROR_string_base );
153 }
154
155 /** Need the LIST routines to complete ERROR initialization */
ERRORinitialize_after_LIST(void)156 void ERRORinitialize_after_LIST( void ) {
157 ERRORwarnings = LISTcreate();
158
159 MEMinitialize( &ERROR_OPT_fl, sizeof( struct Error_Warning_ ), 5, 5 );
160 }
161
ERRORcreate_warning(char * name,Error error)162 void ERRORcreate_warning( char * name, Error error ) {
163 struct Error_Warning_ *o;
164
165 /* first check if we know about this type of error */
166 LISTdo( ERRORwarnings, opt, Error_Warning ) {
167 if( streq( name, opt->name ) ) {
168 LISTadd_last( opt->errors, ( Generic )error );
169 return;
170 }
171 } LISTod
172
173 /* new error */
174 o = ERROR_OPT_new();
175 o->name = name;
176 o->errors = LISTcreate();
177 LISTadd_last( o->errors, ( Generic )error );
178 LISTadd_last( ERRORwarnings, ( Generic )o );
179 }
180
ERRORset_warning(char * name,int set)181 void ERRORset_warning( char * name, int set ) {
182
183 if( streq( name, "all" ) ) {
184 ERRORset_all_warnings( set );
185 } else if( streq( name, "none" ) ) {
186 ERRORset_all_warnings( !set );
187 } else {
188 bool found = false;
189 LISTdo( ERRORwarnings, opt, Error_Warning ) {
190 if( streq( opt->name, name ) ) {
191 found = true;
192 LISTdo_n( opt->errors, err, Error, b ) {
193 err->enabled = set;
194 } LISTod
195 }
196 } LISTod
197 if( found ) {
198 return;
199 }
200
201 fprintf( stderr, "unknown warning: %s\n", name );
202 if( ERRORusage_function ) {
203 ( *ERRORusage_function )();
204 } else {
205 EXPRESSusage(1);
206 }
207 }
208 }
209
210 /** \fn ERRORdisable
211 ** \param error error to disable
212 ** Disable an error (ERRORreport*() will ignore it)
213 ** \note this function is inlined in error.h
214 */
215
216 /** \fn ERRORenable
217 ** \param error error to enable
218 ** Enable an error (ERRORreport*() will report it)
219 ** \note this function is inlined in error.h
220 */
221
222 /** \fn ERRORis_enabled
223 ** \param error error to test
224 ** \return is reporting of the error enabled?
225 ** Check whether an error is enabled
226 ** \note this function is inlined in error.h
227 */
228
229 /** \fn ERRORreport
230 ** \param what error to report
231 ** \param ... arguments for error string
232 ** Print a report of an error
233 **
234 ** Notes: The second and subsequent arguments should match the
235 ** format fields of the message generated by 'what.'
236 */
ERRORset_all_warnings(int set)237 void ERRORset_all_warnings( int set ) {
238 LISTdo( ERRORwarnings, opts, Error_Warning ) {
239 LISTdo_n( opts->errors, err, Error, b ) {
240 err->enabled = set;
241 } LISTod
242 } LISTod
243 }
244
245 void
246 #ifdef __STDC__
ERRORreport(Error what,...)247 ERRORreport( Error what, ... ) {
248 #else
249 ERRORreport( va_alist )
250 va_dcl {
251 Error what;
252 #endif
253 /* extern void abort(void);*/
254 va_list args;
255
256 #ifdef __STDC__
257 va_start( args, what );
258 #else
259 va_start( args );
260 what = va_arg( args, Error );
261 #endif
262
263 if( ( what != ERROR_none ) &&
264 ( what != ERROR_subordinate_failed ) &&
265 what->enabled ) {
266 if( what->severity >= SEVERITY_ERROR ) {
267 fprintf( error_file, "ERROR PE%03d: ", what->serial );
268 vfprintf( error_file, what->message, args );
269 fputc( '\n', error_file );
270 ERRORoccurred = true;
271 } else {
272 fprintf( error_file, "WARNING PW%03d: %d", what->serial, what->severity );
273 vfprintf( error_file, what->message, args );
274 fputc( '\n', error_file );
275 }
276 if( what->severity >= SEVERITY_EXIT ) {
277 ERROR_flush_message_buffer();
278 if( what->severity >= SEVERITY_DUMP ) {
279 abort();
280 } else {
281 exit( EXPRESS_fail( ( Express )0 ) );
282 }
283 }
284 }
285 experrc = ERROR_none;
286 va_end( args );
287 }
288
289 /**
290 ** \param what error to report
291 ** \param line line number of error
292 ** \param ... arguments for error string
293 ** Print a report of an error, including a line number
294 **
295 ** \note The third and subsequent arguments should match the
296 ** format fields of the message generated by 'what.'
297 */
298 void
299 #ifdef __STDC__
300 ERRORreport_with_line( Error what, int line, ... ) {
301 #else
302 ERRORreport_with_line( va_alist )
303 va_dcl {
304 Error what;
305 int line;
306 #endif
307
308 char buf[BUFSIZ];
309 char * savemsg; /* save what->message here while we fool */
310 /* ERRORreport_with_line */
311 Symbol sym;
312 va_list args;
313 #ifdef __STDC__
314 va_start( args, line );
315 #else
316 va_start( args );
317 what = va_arg( args, Error );
318 line = va_arg( args, int );
319 #endif
320
321 sym.filename = current_filename;
322 sym.line = line;
323
324 vsprintf( buf, what->message, args );
325
326 /* gross, but there isn't any way to do this more directly */
327 /* without writing yet another variant of ERRORreport_with_line */
328 savemsg = what->message;
329 what->message = "%s";
330 ERRORreport_with_symbol( what, &sym, buf );
331 what->message = savemsg;
332 }
333
334 void
335 #ifdef __STDC__
336 ERRORreport_with_symbol( Error what, Symbol * sym, ... ) {
337 #else
338 ERRORreport_with_symbol( va_alist )
339 va_dcl {
340 Error what;
341 Symbol * sym;
342 #endif
343 /* extern void abort(void);*/
344 va_list args;
345
346 #ifdef __STDC__
347 va_start( args, sym );
348 #else
349 va_start( args );
350 what = va_arg( args, Error );
351 sym = va_arg( args, Symbol * );
352 #endif
353
354 if( ( what != ERROR_none ) && ( what != ERROR_subordinate_failed ) && what->enabled ) {
355 if( __ERROR_buffer_errors ) {
356 int child, parent;
357
358 /*
359 * add an element to the heap
360 * by (logically) storing the new value
361 * at the end of the array and bubbling
362 * it up as necessary
363 */
364
365 child = ++ERROR_with_lines;
366 parent = child / 2;
367 while( parent ) {
368 if( sym->line < heap[parent].line ) {
369 heap[child] = heap[parent];
370 } else {
371 break;
372 }
373 child = parent;
374 parent = child / 2;
375 }
376 heap[child].line = sym->line;
377 heap[child].msg = ERROR_string;
378
379 if( what->severity >= SEVERITY_ERROR ) {
380 sprintf( ERROR_string, "%s:%d: --ERROR PE%03d: ", sym->filename, sym->line, what->serial );
381 ERROR_string += strlen( ERROR_string );
382 vsprintf( ERROR_string, what->message, args );
383 ERROR_string += strlen( ERROR_string );
384 *ERROR_string++ = '\n';
385 *ERROR_string++ = '\0';
386 ERRORoccurred = true;
387 } else {
388 sprintf( ERROR_string, "%s:%d: WARNING PW%03d: ", sym->filename, sym->line, what->serial );
389 ERROR_string += strlen( ERROR_string );
390 vsprintf( ERROR_string, what->message, args );
391 ERROR_string += strlen( ERROR_string );
392 *ERROR_string++ = '\n';
393 *ERROR_string++ = '\0';
394 }
395 if( what->severity >= SEVERITY_EXIT ||
396 ERROR_string + ERROR_MAX_STRLEN > ERROR_string_base + ERROR_MAX_SPACE ||
397 ERROR_with_lines == ERROR_MAX_ERRORS ) {
398 ERROR_flush_message_buffer();
399 if( what->severity >= SEVERITY_DUMP ) {
400 abort();
401 } else {
402 exit( EXPRESS_fail( ( Express )0 ) );
403 }
404 }
405 } else {
406 if( what->severity >= SEVERITY_ERROR ) {
407 fprintf( error_file, "%s:%d: --ERROR PE%03d: ", sym->filename, sym->line, what->serial );
408 vfprintf( error_file, what->message, args );
409 fprintf( error_file, "\n" );
410 ERRORoccurred = true;
411 } else {
412 fprintf( error_file, "%s:%d: WARNING PW%03d: ", sym->filename, sym->line, what->serial );
413 ERROR_string += strlen( ERROR_string ) + 1;
414 vfprintf( error_file, what->message, args );
415 fprintf( error_file, "\n" );
416 }
417 if( what->severity >= SEVERITY_EXIT ) {
418 if( what->severity >= SEVERITY_DUMP ) {
419 abort();
420 } else {
421 exit( EXPRESS_fail( ( Express )0 ) );
422 }
423 }
424 }
425 }
426 experrc = ERROR_none;
427 va_end( args );
428 }
429
430 void ERRORnospace() {
431 fprintf( stderr, "%s: out of space\n", EXPRESSprogram_name );
432 ERRORabort( 0 );
433 }
434
435 /**
436 ** \param message error message
437 ** \param severity severity of error
438 ** \return newly created error
439 ** Create a new error
440 */
441 Error ERRORcreate( char * message, Severity severity ) {
442 static int errnum = 0; /* give each error type a unique identifier */
443 Error n;
444
445 n = ( struct Error_ * )sc_malloc( sizeof( struct Error_ ) );
446 n->message = message;
447 n->severity = severity;
448 n->enabled = true;
449 n->serial = errnum++;
450 return n;
451 }
452
453 void ERRORdestroy( Error error ) {
454 sc_free( error );
455 }
456
457 /** \fn ERRORbuffer_messages
458 ** \param flag - to buffer or not to buffer
459 ** Selects buffering of error messages
460 ** \note this function is inlined in error.h
461 */
462
463 /** \fn ERRORflush_messages
464 ** Flushes the error message buffer to standard output.
465 ** \note this function is inlined in error.h
466 **
467 ** \note The error messages are sorted by line number (which appears in the third column).
468 */
469
470 void ERROR_start_message_buffer( void ) {
471 ERROR_string = ERROR_string_base;
472 ERROR_with_lines = 0;
473 }
474
475 void ERROR_flush_message_buffer( void ) {
476 if( __ERROR_buffer_errors == false ) {
477 return;
478 }
479
480 while( ERROR_with_lines ) {
481 struct heap_element * replace;
482 int parent, child;
483
484 /* pop off the top of the heap */
485 fprintf( stderr, "%s", heap[1].msg );
486
487 replace = &heap[ERROR_with_lines--];
488
489 child = 1;
490 while( 1 ) {
491 parent = child;
492 child = 2 * parent;
493 if( child > ERROR_with_lines ) {
494 break;
495 }
496 if( child + 1 <= ERROR_with_lines ) {
497 if( heap[child].line > heap[child + 1].line ) {
498 child++;
499 }
500 }
501 if( replace->line <= heap[child].line ) {
502 break;
503 }
504 heap[parent] = heap[child];
505 }
506 heap[parent] = *replace;
507 }
508 }
509
510 void ERRORabort( int sig ) {
511 (void) sig; /* quell unused param warning */
512 ERRORflush_messages();
513 if( !ERRORdebugging ) {
514 if( ERROR_unsafe ) {
515 longjmp( ERROR_safe_env, 1 );
516 }
517 #ifdef SIGABRT
518 signal( SIGABRT, SIG_DFL );
519 #endif
520 abort();
521 }
522 }
523
524 void ERRORsafe( jmp_buf env ) {
525 memcpy( ERROR_safe_env, env, sizeof( jmp_buf ) );
526 }
527
528 void ERRORunsafe() {
529 ERROR_unsafe = true;
530 }
531