/* GRASP: Copyright 1997,1998 Bruce Allen */ static char *rcsid="$Id: GR_error.c,v 1.6 1998/09/03 23:00:41 ballen Exp $\ \n$Name: $"; /****************************************************************************** GR_error.cc The GRASP error reporting module. Copyright ) 1997 by Dustin Laurence. All rights reserved. Base code generated by Codemill v0.1.0 Must do: Make the function pointers static, reset init properly. Test program must install and uninstall a custom error handler. *****************************************************************************/ #define _XOPEN_SOURCE #include "grasp.h" #include #undef _XOPEN_SOURCE /****************************************************************************** GR_start_error GR_report_error GR_end_error All GRASP error calls go through these functions, which in turn call the error handlers pointed to by the GR...error_handler pointers. NULL values indicate that that function should not be called. The pointers default to the GR_default_... functions. The calls work exactly as the names imply. GR_start_error must be called exactly once before the other two are called, GR_end_error must be called exactly once to finish the report, and GR_error may be called any number of times in between to describe the error. These three functions enforce these restrictions so that the handler functions can assume that they are being called in the correct order. Calls to GR_set_errors_enabled do not take effect until the next call to GR_start_error, to avoid screwing up an error report in progress (as unlikely as that might be). As a small convenience the va_list parameter arglist of GR_error_handler is initialized with va_start() before the call and and va_end() is called afterwards, so error handlers need not (and should not) use either. *****************************************************************************/ /* The error handler pointers */ static GR_start_error_type GR_start_error_handler = NULL; static GR_report_error_type GR_report_error_handler = NULL; static GR_end_error_type GR_end_error_handler = NULL; /* Remembers the display state at the time GR_start_error was called */ static enum {kNone, kNoDisplay, kDisplay} current_error = kNone; static int initialize_default = 1; static int set_default = 1; /****************************************************************************** GR_start_error This function must be called once at the beginning of every GRASP error report. It may not be called more than once between calls to GR_end_error. *****************************************************************************/ void GR_start_error ( const char* function, const char* rcsid, const char* file, const long line ) { assert(current_error == kNone); if (initialize_default) { initialize_default = 0; if (set_default) { set_default = 0; GR_restore_default_handlers(); } GR_set_error_file(stderr); /* Changed by Dustin 9/2/98 */ if ( getenv("GRASP_NODEBUG") != NULL ) { GR_set_errors_enabled(0); } (void) GR_set_error_file_name(getenv("GRASP_ERRORFILE"), getenv("GRASP_ERRORFILE_OVERWRITE") != NULL); } if ( GR_errors_enabled() ) { current_error = kDisplay; if (GR_start_error_handler != NULL) { GR_start_error_handler(function, rcsid, file, line); } } else { current_error = kNoDisplay; } } /****************************************************************************** GR_report_error This function may be called any number of times after the call to GR_start_error to describe the error that occured. *****************************************************************************/ void GR_report_error ( const char* format, ... ) { assert(current_error == kNoDisplay || current_error == kDisplay); if ( current_error == kDisplay && GR_report_error_handler != NULL) { va_list arglist; va_start(arglist, format); GR_report_error_handler(format, arglist); va_end(arglist); } } /****************************************************************************** GR_end_error This function must be called to signal the end of an error report. It may only be called exactly once after GR_start_error has been called. *****************************************************************************/ void GR_end_error(void) { assert(current_error == kNoDisplay || current_error == kDisplay); if ( current_error == kDisplay && GR_end_error_handler != NULL ) { GR_end_error_handler(); } current_error = kNone; } /****************************************************************************** GR_is_reporting Returns whether a GRASP error report is in progress. *****************************************************************************/ int GR_is_reporting(void) { return (current_error != kNone); } /****************************************************************************** GR_set_error_handlers GR_get_error_handlers These functions set and get the error handler functions. The handlers may not be changed during a report (between the start and end function calls). *****************************************************************************/ void GR_set_error_handlers ( GR_start_error_type error_start, GR_report_error_type error_report, GR_end_error_type error_end ) { assert(current_error == kNone); GR_start_error_handler = error_start; GR_report_error_handler = error_report; GR_end_error_handler = error_end; set_default = 0; } void GR_get_error_handlers ( GR_start_error_type* error_start, GR_report_error_type* error_report, GR_end_error_type* error_end ) { *error_start = GR_start_error_handler; *error_report = GR_report_error_handler; *error_end = GR_end_error_handler; } /****************************************************************************** GR_set_errors_enabled GR_errors_enabled These functions set and report on whether GRASP error reporting is enabled or disabled. A true (non-zero) argument for GR_set_errors_enabled turns error reporting on, a false (zero) value turns them off. When errors are disabled, the GR_..._error_handler functions are never called. If the environment variable 'GRASP_NODEBUG' has a value, errors are initially disabled. If it has no value, they are initially enabled. Subsequent calls to GR_set_errors_enabled behave normally regardless of the value of this variable. *****************************************************************************/ static int errors_enabled_flag = 1; void GR_set_errors_enabled ( int on_off ) { errors_enabled_flag = on_off; } int GR_errors_enabled(void) { return errors_enabled_flag; } /****************************************************************************** GR_default_start_error Prints the file, function, and line where the error occured. All arguments are required. *****************************************************************************/ static const char* current_rcsid = NULL; /* save for GR_default_end_error */ static FILE* current_file= NULL; static int close_file = 0; /* Should we close at the end? */ void GR_default_start_error ( const char* function, const char* rcsid, const char* file, const long line ) { const char* filename; static char machinename[64]=""; FILE *pipe; assert(function != NULL); assert(rcsid != NULL); assert(file != NULL); assert(current_rcsid == NULL); assert(current_file == NULL); current_rcsid = rcsid; filename = GR_get_error_file_name(); if (filename != NULL) { current_file = fopen(filename, "a"); close_file = 1; } if (current_file == NULL) { current_file = GR_get_error_file(); close_file = 0; } assert(current_file != NULL); /* if we do not know the name of the machine, attempt to find it out */ if (machinename[0]=='\0') { pipe=popen("hostname","r"); if (pipe==NULL) { /* if we can't open the pipe, then list machine name as unknown */ strcpy(machinename,"hostname undetermined"); } else fscanf(pipe,"%s",machinename); } fprintf(current_file,"GRASP (%s): Message from function %s at line number %ld of file \"%s\".\n", machinename,function, line, file); } /****************************************************************************** GR_default_report_error *****************************************************************************/ void GR_default_report_error ( const char* format, va_list arglist ) { assert(format != NULL); assert(current_file != NULL); vfprintf(current_file, format, arglist); } /****************************************************************************** GR_default_end_error Prints the rcsid and flushes the error file. *****************************************************************************/ void GR_default_end_error(void) { assert(current_rcsid != NULL); assert(current_file != NULL); fprintf(current_file, "%s\n\n", current_rcsid); if (close_file) { fclose(current_file); } current_rcsid = NULL; current_file = NULL; } /****************************************************************************** GR_restore_default_handlers A convenience function to restore the error handlers to the default handlers. The handlers may not be changed during a report (between the start and end function calls). *****************************************************************************/ void GR_restore_default_handlers(void) { GR_set_error_handlers(GR_default_start_error, GR_default_report_error, GR_default_end_error); } /****************************************************************************** GR_set_error_file GR_get_error_file These functions set and report on what file the GR_default_... functions will use to report on errors. The file may not be changed during a report. A NULL file pointer may be set, in which case any call to GR_default_start_error() is an error (and so either errors had better be disabled or you'd better not make any errors). Any file used in a GR_default_start_error call must be open for writing. *****************************************************************************/ static FILE* error_file = NULL; /* Changed by Dustin 9/2/98 */ void GR_set_error_file ( FILE* new_error_file ) { assert( !GR_is_reporting() ); error_file = new_error_file; } FILE* GR_get_error_file(void) { return error_file; } /****************************************************************************** GR_set_error_file_name GR_get_error_file_name These functions set and report on the name of the default file, and optionally erase any existing contents. GR_set_error_file_name() attempts to open the file; if the open is successful, it sets the filename, writes an identifying message (even if errors are disabled), closes the file, and returns true. If the open fails, the filename is unchanged and false is returned. If erasefile is true, the file is erased when it is opened; otherwise, the file contents are left intact. As a special case the filename may be NULL, in which case the FILE* set by GR_set_error_file() will again be used rather than the file name. The filename may not be changed during an error report. If the environment variable GRASP_ERRORFILE is set and can be opened, that file is set as the default error file. If GRASP_ERRORFILE is used and GRASP_ERRORFILE_OVERWRITE is set to any value, the file named by GRASP_ERRORFILE is erased rather than appended to. If GRASP_ERRORFILE cannot be opened or does not exist, then the default filename is set to NULL and the FILE* set by GR_set_error_file is used instead. Note that if GRASP_ERRORFILE exists it will be set to the default file as described even if some other handler rather than the default is used--this is so that errors may revert to the default at some later time if desired. These functions override rather than replace the FILE* set by GR_set_error_file: if the filename is set to NULL, the FILE* is used. If the filename is non-NULL, then GR_default_start_error will attempt to open it for appending each time it is called, and GR_default_end_error will close it. If the open fails, then the FILE* set by GR_set_error_file is used. This system ensures that the file will be closed without further intervention even if the program terminates abnormally. It also allows another file such as stderr to be used as a backup error file if the file set with GR_set_error_file_name cannot be opened. *****************************************************************************/ static char* error_file_name = NULL; int GR_set_error_file_name ( const char* new_error_file_name, const int erasefile ) { FILE* newfile; assert( !GR_is_reporting() ); if (new_error_file_name == NULL) { /* Free up any allocated string */ if (error_file_name != NULL) { free(error_file_name); error_file_name = NULL; } return 1; } if (erasefile) { newfile = fopen(new_error_file_name, "w"); } else { newfile = fopen(new_error_file_name, "a"); } if (newfile != NULL) { time_t now; if (ftell(newfile) > 0) { /* Are we appending to an existing file? */ fprintf(newfile, "\n---------------------------------------\n\n"); } now = time(NULL); fprintf(newfile, "GRASP error file opened %s\n\n", ctime(&now) ); fclose(newfile); /* Free up any allocated string */ if (error_file_name != NULL) { free(error_file_name); error_file_name = NULL; } /* Allocate new string */ error_file_name = (char*) malloc( strlen(new_error_file_name)+1 ); assert(error_file_name != NULL); /* malloc better not fail */ (void) strcpy(error_file_name, new_error_file_name); return 1; } else { GR_start_error("GR_set_error_file_name()",rcsid,__FILE__,__LINE__); GR_report_error("Unable to report errors (write) to the file %s\n",new_error_file_name); GR_report_error("Using the previous error reporting method.\n"); GR_end_error(); return 0; } } const char* GR_get_error_file_name(void) { return error_file_name; }