GRASP error reports may be customized by modifying the behavior of the default handler functions or by replacing them entirely. By default, when GR_start_error() is called it checks to see if there is a default file name set; if so, that file is opened for appending. If not (that is, if the filename has been set to NULL), or if the file cannot be opened, it then checks to see if an error FILE* has been set. If so, that file is used. If not, then the handler fails since it has no way to report the error. The default GR_report_error() prints to whatever file GR_start_error() chose to use, and GR_end_error() prints the RCS ID. If the file was opened by name in GR_start_error() then GR_end_error() also closes it.
The default FILE* can be set and examined with GR_set_error_file() and GR_get_error_file(), and the default filename can be set and examined with GR_set_error_file_name() and GR_get_error_file_name(). The default behavior can be restored with GR_set_error_file(stderr) and/or GR_set_error_file_name(NULL). These functions may be called at any time except during an error report (between the calls to GR_start_error() and GR_end_error()), when an assert would fail. (This is a non-issue in practice because the file/filename could only be changed during a report if the file or filename is set in GRASP code itself, if the error reporting functions are called in user code as well as in GRASP, or if a custom handler changes the file/filename and then calls the default handler. The first possibility is strictly forbidden, and the others are discouraged because they can lead to confusion and subtle bugs.)
The reason for this somewhat complex system is safety. If a specified file name cannot be opened errors can still be reported to the FILE*, probably stderr. If an error file name has been set, the file is opened and closed for each report so that if the program crashes as much of the error log as possible is preserved on disk.
When using GR_set_error_file(), the calling program is responsible for opening the file for writing before the call and closing it (if necessary); GRASP will simply assume that the file is always available for output. A NULL file pointer is allowed, in which case calls to the default error handler will cause an assert to fail unless an error file name has been set.
When a file name is set with GR_set_error_file_name(), GRASP immediately attempts to open the file. If the erasefile parameter is TRUE (nonzero) the file will be opened for writing, if it is false (zero) it will be opened for appending. If the open succeeds, the name is copied and stored for future use (this means that the function can be safely called with locally allocated storage), an identifying start-up message and the time is written to the log file (even if error reporting is disabled), the file is closed, and the function returns true. When appending to a non-empty file it also writes a separator line so that reports from different runs are more easily distinguished. If the open fails, the filename is left unchanged (if a previous one existed), an error is reported in the usual way (unless error messages are suppressed), and the function returns false. Setting a NULL filename means that the FILE* should be used instead; the erasefile parameter is ignored in this case and the call always succeeds. As with GR_set_error_file(), the filename may not be changed during a report.
It is easy to see how the default behavior is obtained using these handlers. The default FILE* is stderr; conceptually a call to GR_set_error_file(stderr) occurs before the program begins. Similarly, a conceptual call to GR_set_error_file_name(filename, erase) occurs before program execution, with filename having the value of the environment variable GRASP_ERRORFILE if it exists and NULL otherwise, and erase true if the environment variable GRASP_ERRORFILE_OVERWRITE exists and false otherwise.