Internally, GR_error keeps three pointers of type GR_start_error_type, GR_report_error_type, and GR_end_error_type (defined in grasp.h) which point to the current start_error, report_error, and end_error handlers, respectively. When the three error handlers GR_start_error(), GR_report_error(), and GR_end_error() are called, they in turn check to see that they are called in the proper order and then call the function pointed to by the corresponding function pointer if (and only if) the function pointer is non-NULL (so that for convenience if a particular handler is not neccessary a dummy routine is not required) and if errors are currently enabled (so disabling errors works regardless of the handler). By default the handler pointers simply reference GR_default_start_error(), GR_default_report_error(), and GR_default_end_error(), which actually implement the default behavior described above. For convenience they may be restored with GR_restore_default_handlers() as well as GR_set_error_handlers().
The functions GR_set_error_handlers() and GR_get_error_handlers() set and examine the handler's current values, so that by writing the proper functions GRASP's error reports can be customized in any way desired. The GR_..._error_type typedef's in grasp.h illustrate the proper function prototypes. Note that GR_report_error_type functions take a va_list as their second argument rather than '...'; for convenience GR_report_error() creates the list and calls va_start() beforehand and calls va_end() afterwards so the handler need only deal with the list itself. In the common case where the message will simply be printed to a file, the va_list may be passed directly to one of the v...printf functions. The only restriction is that the handlers may not be changed between calls to GR_start_error() and GR_end_error(), but just as when changing the default error files this should not be a problem in practice.
The default handlers in src/utility/GR_error.c and their supporting routines are good examples of how GRASP error handlers are written. They are special only in that they are initialized and set automatically; otherwise, they use only features available to any handler. Most of their code provides the ability to switch files easily and safely; writing a custom handler that does not need this generality is quite straightforward. GR_is_reporting() returns true if a report is in progress and is sometimes useful when writing custom handlers.