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