/** ********************************************************************** ** Module: Error \file error.c ** This module implements the ERROR abstraction ************************************************************************/ /* * Development of this code was funded by the United States Government, * and is not subject to copyright. * * $Log: error.c,v $ * Revision 1.13 1997/10/23 21:41:44 sauderd * I am backing out to version 1.10 before the changes related to va_list and * __STDC__ etc. I don't have time to finish fixing all this. * * Revision 1.12 1997/10/23 21:35:31 sauderd * Changed more mess with compiler directives for __STDC__ and HAVE_STDARG_H * but am getting deeper into problems because of how PROTO is defined for * dealing with prototypes based on standard C or not. PROTO is broken as well * so I'm not going down this road further. Next I will back out but am this * far ahead for later if we fix this. * * Revision 1.11 1997/10/22 16:10:26 sauderd * This would #include stdarg.h if __STDC__ was defined otherwise it would * #include vararg.h. I changed it to check the configure generated config file * named config.h (if HAVE_CONFIG_H is defined - it's also defined by * configure) to see if HAVE_STDARG_H is defined. If it is it #includes stdarg.h * otherwise it #includes vararg.h. If HAVE_CONFIG_H isn't defined then it works * like it used to. * * Revision 1.10 1997/01/21 19:19:51 dar * made C++ compatible * * Revision 1.9 1994/05/11 19:51:46 libes * numerous fixes * * Revision 1.8 1993/10/15 18:49:55 libes * CADDETC certified * * Revision 1.6 1993/02/22 21:44:34 libes * ANSI compat fixes * * Revision 1.5 1993/01/19 22:45:07 libes * *** empty log message *** * * Revision 1.4 1992/08/18 17:16:22 libes * rm'd extraneous error messages * * Revision 1.3 1992/06/08 18:08:05 libes * prettied up interface to print_objects_when_running */ #include #include #include #include #include #include #include "express/express.h" #include "express/error.h" #include "express/info.h" #include "express/linklist.h" static struct Error_ LibErrors[] = { /* dict.c */ [DUPLICATE_DECL] = {SEVERITY_ERROR, "Redeclaration of %s. Previous declaration was on line %d.", NULL, false}, [DUPLICATE_DECL_DIFF_FILE] = {SEVERITY_ERROR, "Redeclaration of %s. Previous declaration was on line %d in file %s.", NULL, false}, /* error.c */ [SUBORDINATE_FAILED] = {SEVERITY_ERROR, "A subordinate failed.", NULL, false}, [SYNTAX_EXPECTING] = {SEVERITY_EXIT, "%s, expecting %s in %s %s", NULL, false}, /* expr.c */ [INTEGER_EXPRESSION_EXPECTED] = {SEVERITY_WARNING, "Integer expression expected", NULL, false}, [INTERNAL_UNRECOGNISED_OP_IN_EXPRESOLVE] = {SEVERITY_ERROR, "Opcode unrecognized while trying to resolve expression", NULL, false}, [ATTRIBUTE_REF_ON_AGGREGATE] = {SEVERITY_ERROR, "Attribute %s cannot be referenced from an aggregate", NULL, false}, [ATTRIBUTE_REF_FROM_NON_ENTITY] = {SEVERITY_ERROR, "Attribute %s cannot be referenced from a non-entity", NULL, false}, [INDEXING_ILLEGAL] = {SEVERITY_ERROR, "Indexing is only permitted on aggregates", NULL, false}, [WARN_INDEXING_MIXED] = {SEVERITY_WARNING, "non-aggregates) and/or different aggregation types.", "indexing", false}, [ENUM_NO_SUCH_ITEM] = {SEVERITY_ERROR, "Enumeration type %s does not contain item %s", NULL, false}, [GROUP_REF_NO_SUCH_ENTITY] = {SEVERITY_ERROR, "Group reference failed to find entity %s", NULL, false}, [GROUP_REF_UNEXPECTED_TYPE] = {SEVERITY_ERROR, "Group reference of unusual expression %s", NULL, false}, [IMPLICIT_DOWNCAST] = {SEVERITY_WARNING, "Implicit downcast to %s.", "downcast", false}, [AMBIG_IMPLICIT_DOWNCAST] = {SEVERITY_WARNING, "Possibly ambiguous implicit downcast (%s?).", "downcast", false}, /* express.c */ [BAIL_OUT] = {SEVERITY_DUMP, "Aborting due to internal error(s)", NULL, false}, [SYNTAX] = {SEVERITY_EXIT, "%s in %s %s", NULL, false}, [REF_NONEXISTENT] = {SEVERITY_ERROR, "USE/REF of non-existent object (%s in schema %s)", NULL, false}, [TILDE_EXPANSION_FAILED] = {SEVERITY_ERROR, "Tilde expansion for %s failed in EXPRESS_PATH environment variable", NULL, false}, [SCHEMA_NOT_IN_OWN_SCHEMA_FILE] = {SEVERITY_ERROR, "Schema %s was not found in its own schema file (%s)", NULL, false}, [UNLABELLED_PARAM_TYPE] = {SEVERITY_ERROR, "Return type or local variable requires type label in `%s'", NULL, false}, [FILE_UNREADABLE] = {SEVERITY_ERROR, "Could not read file %s: %s", NULL, false}, [FILE_UNWRITABLE] = {SEVERITY_ERROR, "Could not write file %s: %s", NULL, false}, [WARN_UNSUPPORTED_LANG_FEAT] = {SEVERITY_WARNING, "Unsupported language feature (%s) at %s:%d", "unsupported", false}, [WARN_SMALL_REAL] = {SEVERITY_WARNING, "REALs with extremely small magnitude may be interpreted as zero by other EXPRESS parsers " "(IEEE 754 float denormals are sometimes rounded to zero) - fabs(%f) <= FLT_MIN.", "limits", false}, /* lexact.c */ [INCLUDE_FILE] = {SEVERITY_ERROR, "Could not open include file `%s'.", NULL, false}, [UNMATCHED_CLOSE_COMMENT] = {SEVERITY_ERROR, "unmatched close comment", NULL, false}, [UNMATCHED_OPEN_COMMENT] = {SEVERITY_ERROR, "unmatched open comment", NULL, false}, [UNTERMINATED_STRING] = {SEVERITY_ERROR, "unterminated string literal", NULL, false}, [ENCODED_STRING_BAD_DIGIT] = {SEVERITY_ERROR, "non-hex digit (%c) in encoded string literal", NULL, false}, [ENCODED_STRING_BAD_COUNT] = {SEVERITY_ERROR, "number of digits (%d) in encoded string literal is not divisible by 8", NULL, false}, [BAD_IDENTIFIER] = {SEVERITY_ERROR, "identifier (%s) cannot start with underscore", NULL, false}, [UNEXPECTED_CHARACTER] = {SEVERITY_ERROR, "character (%c) is not a valid lexical element by itself", NULL, false}, [NONASCII_CHAR] = {SEVERITY_ERROR, "character (0x%x) is not in the EXPRESS character set", NULL, false}, /* linklist.c */ [EMPTY_LIST] = {SEVERITY_ERROR, "Empty list in %s.", NULL, false}, /* resolve.c */ [UNDEFINED] = {SEVERITY_ERROR, "Reference to undefined object %s.", NULL, false}, [UNDEFINED_ATTR] = {SEVERITY_ERROR, "Reference to undefined attribute %s.", NULL, false}, [UNDEFINED_TYPE] = {SEVERITY_ERROR, "Reference to undefined type %s.", NULL, false}, [UNDEFINED_SCHEMA] = {SEVERITY_ERROR, "Reference to undefined schema %s.", NULL, false}, [UNKNOWN_ATTR_IN_ENTITY] = {SEVERITY_ERROR, "Unknown attribute %s in entity %s.", NULL, false}, [UNKNOWN_SUBTYPE] = {SEVERITY_EXIT, "Unknown subtype %s for entity %s.", "unknown_subtype", false}, [UNKNOWN_SUPERTYPE] = {SEVERITY_ERROR, "Unknown supertype %s for entity %s.", NULL, false}, [CIRCULAR_REFERENCE] = {SEVERITY_ERROR, "Circularity: definition of %s references itself.", NULL, false}, [SUBSUPER_LOOP] = {SEVERITY_ERROR, "Entity %s is a subtype of itself", "circular_subtype", false}, [SUBSUPER_CONTINUATION] = {SEVERITY_ERROR, " (via supertype entity %s)", NULL, false}, [SELECT_LOOP] = {SEVERITY_ERROR, "Select type %s selects itself", "circular_select", false}, [SELECT_CONTINUATION] = {SEVERITY_ERROR, " (via select type %s)", NULL, false}, [SUPERTYPE_RESOLVE] = {SEVERITY_ERROR, "Supertype %s is not an entity (line %d).", NULL, false}, [SUBTYPE_RESOLVE] = {SEVERITY_ERROR, "Subtype %s resolves to non-entity %s on line %d.", NULL, false}, [NOT_A_TYPE] = {SEVERITY_ERROR, "Expected a type (or entity) but %s is %s.", NULL, false}, [FUNCALL_NOT_A_FUNCTION] = {SEVERITY_ERROR, "Function call of %s which is not a function.", NULL, false}, [UNDEFINED_FUNC] = {SEVERITY_ERROR, "Function %s undefined.", NULL, false}, [EXPECTED_PROC] = {SEVERITY_ERROR, "%s is used as a procedure call but is not defined as one (line %d).", NULL, false}, [NO_SUCH_PROCEDURE] = {SEVERITY_ERROR, "No such procedure as %s.", NULL, false}, [WRONG_ARG_COUNT] = {SEVERITY_WARNING, "Call to %s uses %d arguments, but expected %d.", NULL, false}, [QUERY_REQUIRES_AGGREGATE] = {SEVERITY_ERROR, "Query expression source must be an aggregate.", NULL, false}, [SELF_IS_UNKNOWN] = {SEVERITY_ERROR, "SELF is not within an entity declaration.", NULL, false}, [INVERSE_BAD_ENTITY] = {SEVERITY_ERROR, "Attribute %s is referenced from non-entity-inheriting type.", NULL, false}, [INVERSE_BAD_ATTR] = {SEVERITY_ERROR, "Unknown attribute %s in entity %s in inverse.", NULL, false}, [MISSING_SUPERTYPE] = {SEVERITY_ERROR, "Entity %s missing from supertype list for subtype %s.", NULL, false}, [TYPE_IS_ENTITY] = {SEVERITY_ERROR, "An entity (%s) is not acceptable as an underlying type.", "entity_as_type", false}, [AMBIGUOUS_ATTR] = {SEVERITY_WARNING, "Ambiguous attribute reference %s.", NULL, false}, [AMBIGUOUS_GROUP] = {SEVERITY_WARNING, "Ambiguous group reference %s.", NULL, false}, [OVERLOADED_ATTR] = {SEVERITY_ERROR, "Attribute %s already inherited via supertype %s.", NULL, false}, [REDECL_NO_SUCH_ATTR] = {SEVERITY_ERROR, "Redeclared attribute %s not declared in supertype %s.", NULL, false}, [REDECL_NO_SUCH_SUPERTYPE] = {SEVERITY_ERROR, "No such supertype %s for redeclaration of attribute %s.", NULL, false}, [MISSING_SELF] = {SEVERITY_ERROR, "Domain rule %s must refer to SELF or attribute.", NULL, false}, [FN_SKIP_BRANCH] = {SEVERITY_WARNING, "IF statement condition is always %s. Ignoring branch that is never taken.", "invariant_condition", false}, [CASE_SKIP_LABEL] = {SEVERITY_WARNING, "CASE label %s cannot be matched. Ignoring its statements.", "invalid_case", false}, [UNIQUE_QUAL_REDECL] = {SEVERITY_WARNING, "Possibly unnecessary qualifiers on redeclared attr '%s' in a uniqueness rule of entity '%s'.", "unnecessary_qualifiers", false}, /* type.c */ [CORRUPTED_TYPE] = {SEVERITY_DUMP, "Corrupted type in %s", NULL, false}, [UNDEFINED_TAG] = {SEVERITY_ERROR, "Undefined type tag %s", NULL, false}, /* exppp.c */ [SELECT_EMPTY] = {SEVERITY_ERROR, "select type %s has no members", NULL, false}, }; bool __ERROR_buffer_errors = false; const char * current_filename = "stdin"; /* flag to remember whether non-warning errors have occurred */ bool ERRORoccurred = false; Error experrc = ERROR_none; Error ERROR_subordinate_failed = ERROR_none; Error ERROR_syntax_expecting = ERROR_none; /* all of these are 1 if true, 0 if false switches */ /* for debugging fedex */ int ERRORdebugging = 0; /* for debugging malloc during resolution */ int malloc_debug_resolve = 0; /* for debugging yacc/lex */ int debug = 0; void ( *ERRORusage_function )( void ); #define ERROR_MAX_ERRORS 100 /**< max line-numbered errors */ #define ERROR_MAX_SPACE 4000 /**< max space for line-numbered errors */ #define ERROR_MAX_STRLEN 200 /**< assuming all error messages are less than this, * if we have less than this much space remaining * in the error string buffer, call it a day and * dump the buffer */ static struct heap_element { int line; char * msg; } heap[ERROR_MAX_ERRORS + 1]; /**< NOTE! element 0 is purposely ignored, and * an additional element is at the end. This * allows the later heap calculations to be * much simpler */ static int ERROR_with_lines = 0; /**< number of warnings & errors that have occurred with a line number */ static char * ERROR_string; static char * ERROR_string_base; static char * ERROR_string_end; static bool ERROR_unsafe = false; static jmp_buf ERROR_safe_env; #define error_file stderr /**< message buffer file */ static int ERROR_vprintf( const char *format, va_list ap ) { int result = vsnprintf( ERROR_string, ERROR_string_end - ERROR_string, format, ap ); if(result < 0) { ERROR_string = ERROR_string_end; } else if(result > (ERROR_string_end - ERROR_string)) { ERROR_string = ERROR_string_end; } else { ERROR_string = ERROR_string + result; } return result; } static int ERROR_printf( const char *format, ... ) { int result; va_list ap; va_start( ap, format ); result = ERROR_vprintf( format, ap ); va_end( ap ); return result; } static void ERROR_nexterror(void) { if( ERROR_string == ERROR_string_end ) { return; } ERROR_string++; } /** Initialize the Error module */ void ERRORinitialize( void ) { ERROR_string_base = ( char * )malloc( ERROR_MAX_SPACE ); ERROR_string_end = ERROR_string_base + ERROR_MAX_SPACE; ERROR_start_message_buffer(); #ifdef SIGQUIT signal( SIGQUIT, ERRORabort ); #endif #ifdef SIGBUS signal( SIGBUS, ERRORabort ); #endif #ifdef SIGSEGV signal( SIGSEGV, ERRORabort ); #endif #ifdef SIGABRT signal( SIGABRT, ERRORabort ); #endif } /** Clean up the Error module */ void ERRORcleanup( void ) { free( ERROR_string_base ); } void ERRORset_warning(char * name, bool warn_only) { Error err; bool found = false; for (unsigned int errnum = 0; errnum < (sizeof LibErrors / sizeof LibErrors[0]); errnum++) { err = &LibErrors[errnum]; if (err->severity <= SEVERITY_WARNING && !strcmp(err->name, name)) { found = true; err->override = warn_only; } } if (!found) { fprintf( stderr, "unknown warning: %s\n", name ); if( ERRORusage_function ) { ( *ERRORusage_function )(); } else { EXPRESSusage(1); } } } void ERRORset_all_warnings( bool warn_only ) { Error err; for (unsigned int errnum = 0; errnum < (sizeof LibErrors / sizeof LibErrors[0]); errnum++) { err = &LibErrors[errnum]; if (err->severity <= SEVERITY_WARNING) { err->override = warn_only; } } } char * ERRORget_warnings_help(const char* prefix, const char *eol) { unsigned int sz = 2048, len, clen; char *buf, *nbuf; Error err; clen = strlen(prefix) + strlen(eol) + 1; buf = malloc(sz); if (!buf) { fprintf(error_file, "failed to allocate memory for warnings help!\n"); } buf[0] = '\0'; for (unsigned int errnum = 0; errnum < (sizeof LibErrors / sizeof LibErrors[0]); errnum++) { err = &LibErrors[errnum]; if (err->name) { len = strlen(buf) + strlen(err->name) + clen; if (len > sz) { sz *= 2; nbuf = realloc(buf, sz); if (!nbuf) { fprintf(error_file, "failed to reallocate / grow memory for warnings help!\n"); } buf = nbuf; } strcat(buf, prefix); strcat(buf, err->name); strcat(buf, eol); } } return buf; } bool ERRORis_enabled(enum ErrorCode errnum) { Error err = &LibErrors[errnum]; return !err->override; } /** \fn ERRORreport ** \param what error to report ** \param ... arguments for error string ** Print a report of an error ** ** Notes: The second and subsequent arguments should match the ** format fields of the message generated by 'what.' */ void ERRORreport( enum ErrorCode errnum, ... ) { va_list args; va_start( args, errnum ); Error what = &LibErrors[errnum]; if (errnum != SUBORDINATE_FAILED && ERRORis_enabled(errnum) ) { if( what->severity >= SEVERITY_ERROR ) { fprintf( error_file, "ERROR PE%03d: ", errnum ); vfprintf( error_file, what->message, args ); fputc( '\n', error_file ); ERRORoccurred = true; } else { fprintf( error_file, "WARNING PW%03d: %d", errnum, what->severity ); vfprintf( error_file, what->message, args ); fputc( '\n', error_file ); } if( what->severity >= SEVERITY_EXIT ) { ERROR_flush_message_buffer(); if( what->severity >= SEVERITY_DUMP ) { abort(); } else { exit( EXPRESS_fail( ( Express )0 ) ); } } } va_end( args ); } /** ** \param what error to report ** \param line line number of error ** \param ... arguments for error string ** Print a report of an error, including a line number ** ** \note The third and subsequent arguments should match the ** format fields of the message generated by 'what.' */ void ERRORreport_with_line( enum ErrorCode errnum, int line, ... ) { Symbol sym; va_list args; va_start( args, line ); sym.filename = current_filename; sym.line = line; ERRORreport_with_symbol(errnum, &sym, args); } void ERRORreport_with_symbol( enum ErrorCode errnum, Symbol * sym, ... ) { va_list args; va_start( args, sym ); Error what = &LibErrors[errnum]; if (errnum != SUBORDINATE_FAILED && ERRORis_enabled(errnum)) { if( __ERROR_buffer_errors ) { int child, parent; /* * add an element to the heap * by (logically) storing the new value * at the end of the array and bubbling * it up as necessary */ child = ++ERROR_with_lines; parent = child / 2; while( parent ) { if( sym->line < heap[parent].line ) { heap[child] = heap[parent]; } else { break; } child = parent; parent = child / 2; } heap[child].line = sym->line; heap[child].msg = ERROR_string; if( what->severity >= SEVERITY_ERROR ) { ERROR_printf( "%s:%d: --ERROR PE%03d: ", sym->filename, sym->line, errnum ); ERROR_vprintf( what->message, args ); ERROR_nexterror(); ERRORoccurred = true; } else { ERROR_printf( "%s:%d: WARNING PW%03d: ", sym->filename, sym->line, errnum ); ERROR_vprintf( what->message, args ); ERROR_nexterror(); } if( what->severity >= SEVERITY_EXIT || ERROR_string + ERROR_MAX_STRLEN > ERROR_string_base + ERROR_MAX_SPACE || ERROR_with_lines == ERROR_MAX_ERRORS ) { ERROR_flush_message_buffer(); if( what->severity >= SEVERITY_DUMP ) { abort(); } else { exit( EXPRESS_fail( ( Express )0 ) ); } } } else { if( what->severity >= SEVERITY_ERROR ) { fprintf( error_file, "%s:%d: --ERROR PE%03d: ", sym->filename, sym->line, errnum ); vfprintf( error_file, what->message, args ); fprintf( error_file, "\n" ); ERRORoccurred = true; } else { fprintf( error_file, "%s:%d: WARNING PW%03d: ", sym->filename, sym->line, errnum ); vfprintf( error_file, what->message, args ); fprintf( error_file, "\n" ); } if( what->severity >= SEVERITY_EXIT ) { if( what->severity >= SEVERITY_DUMP ) { abort(); } else { exit( EXPRESS_fail( ( Express )0 ) ); } } } } va_end( args ); } void ERRORnospace(void) { fprintf( stderr, "%s: out of space\n", EXPRESSprogram_name ); ERRORabort( 0 ); } /** \fn ERRORbuffer_messages ** \param flag - to buffer or not to buffer ** Selects buffering of error messages ** \note this function is inlined in error.h */ /** \fn ERRORflush_messages ** Flushes the error message buffer to standard output. ** \note this function is inlined in error.h ** ** \note The error messages are sorted by line number (which appears in the third column). */ void ERROR_start_message_buffer( void ) { ERROR_string = ERROR_string_base; ERROR_with_lines = 0; } void ERROR_flush_message_buffer( void ) { if(!__ERROR_buffer_errors) { return; } while( ERROR_with_lines ) { struct heap_element * replace; int parent, child; /* pop off the top of the heap */ fprintf( stderr, "%s", heap[1].msg ); replace = &heap[ERROR_with_lines--]; child = 1; while( 1 ) { parent = child; child = 2 * parent; if( child > ERROR_with_lines ) { break; } if( child + 1 <= ERROR_with_lines ) { if( heap[child].line > heap[child + 1].line ) { child++; } } if( replace->line <= heap[child].line ) { break; } heap[parent] = heap[child]; } heap[parent] = *replace; } } void ERRORabort( int sig ) { (void) sig; /* quell unused param warning */ /* TODO: rework - fprintf is not atomic * so ERRORflush_messages() is unsafe if __ERROR_buffer_errors is set */ /* NOTE: signals can be caught in gdb, * no need for special treatment of debugging scenario */ if( ERROR_unsafe ) { longjmp( ERROR_safe_env, 1 ); } #ifdef SIGABRT signal( SIGABRT, SIG_DFL ); #endif /* TODO: library shouldn't abort an application? */ abort(); } void ERRORsafe( jmp_buf env ) { memcpy( ERROR_safe_env, env, sizeof( jmp_buf ) ); } void ERRORunsafe(void) { ERROR_unsafe = true; }