home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 11 Util
/
11-Util.zip
/
TIMEXSRC.ZIP
/
ERRHAND.C
< prev
next >
Wrap
Text File
|
1990-03-29
|
18KB
|
702 lines
/* errhand.c -- Error handling routines
1990 MIPS Magazine / M. Mallett
This is the code for the error handling routines described in the
column "Programming in the main()" in MIPS Magazine, January 1990.
Included are the following routines:
add_condition Add a condition to a condition list
any_condition Add or remove the "handle-any" condition
condition_list Make a new condition list
handle_error Establish an error handling context
intercept_error Specify routines that handle errors
note_error Indicate that an error has occured
remove_condition Remove a condition from a condition list
remove_error Remove a handler
return_error Abnormally return error.
receive_error Get return from error handling context
*/
#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <malloc.h>
#include "errhand.h"
/* Local definitions */
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif /* TRUE */
#define MSLSIZE 20 /* Number of messages retained */
typedef /* Error control structure */
struct ectl {
struct ectl *ec_prevP; /* Backward pointer */
ECTYPE ec_type; /* Type of control block */
jmp_buf *ec_ctxtP; /* Context (setjmp/longjmp) */
ERRLIST *ec_listP; /* Ptr to error list */
char **ec_msgPP; /* Ptr to message receiver var */
} ERRCTL;
/* External data referenced */
/* External routines used */
/* Local data publicly available */
/* Local routines and forward references */
static void add_control();
static void remove_control();
static char *save_msg();
/* Private data */
static ERRCTL *ClisttP = NULL; /* Ptr to list tail */
static char *Msglist[MSLSIZE]; /* Message list */
static int MsgX = 0; /* Next message index */
/*
*//* condition_list( condL )
Make a new condition list
Accepts :
condL Length of condition list (max conditions)
type Type of block to be constructed.
Returns :
<value> Ptr to a condition list struct
*/
ERRLIST *
condition_list( condL, type )
int condL; /* Length of list */
ECTYPE type; /* Type of list */
{
int size; /* Size required */
ERRLIST *condlP; /* Ptr to list */
/* Make new list */
size = sizeof(ERRLIST) +
((condL +1 - ELSIZE) *
( type == CTHANDLER ? sizeof(ERRCOND) : sizeof(EDISP) ) );
condlP = (ERRLIST *)malloc( size );
if ( condlP == NULL )
return_error( (ERRCOND)CCMEMORY, "Out of memory in condition_list()" );
/* Format the block */
condlP->c_condC = 0;
condlP->c_condL = condL;
condlP->c_flags = 0;
condlP->c_type = type;
/* Return it */
return( condlP );
}
/*
*//* add_condition( condlP, cond [, rtc ] )
Add a condition to a condition list
Accepts :
condlP Ptr to the condition list
cond The condition to be added
rtc For an interception list, the routine that
intercepts the error.
Returns :
<nothing>
*/
void
add_condition( condlP, cond, rtc )
ERRLIST *condlP; /* Ptr to condition list */
ERRCOND cond; /* Condition to add */
int (*rtc)(); /* Handler */
{
int condX; /* Condition index */
/* Search through the list */
for( condX = 0; condX < condlP->c_condC; ++condX )
if ( cond == el_cond_inx( condlP, condX ) )
break;
/* Only proceed if the condition isn't in the table. Don't care if
it already is... */
if ( condX == condlP->c_condC ) {
/* Make sure there's room for the new condition plus a spot
for an "any" return. */
if ( ( condX + 1 ) > condlP->c_condL )
return_error( (ERRCOND)CCLISTFULL,
"Condition list full in add_condition()" );
/* Add the condition */
++condlP->c_condC;
if ( condlP->c_type == CTHANDLER )
condlP->c_list.cl_cond[condX] = cond;
else {
condlP->c_list.cl_disp[condX].d_cond = cond;
condlP->c_list.cl_disp[condX].d_rtc = rtc;
}
}
}
/*
*//* remove_condition( condlP, cond )
Remove a condition from a condition list
Accepts :
condlP Ptr to the condition list struct
cond The condition to remove
Returns :
<nothing>
*/
void
remove_condition( condlP, cond )
ERRLIST *condlP; /* Ptr to condition list struct */
ERRCOND cond; /* Condition to remove */
{
int condX; /* Condition index */
/* Look for the condition */
for( condX = 0; condX < condlP->c_condC; ++condX )
if ( cond == el_cond_inx( condlP, condX ) )
break;
/* Remove it if found; don't care otherwise */
if ( condX != condlP->c_condC ) {
/* Shuffle down the remaining conditions */
for( --condlP->c_condC; condX < condlP->c_condC; ++condX ) {
if ( condlP->c_type == CTHANDLER )
condlP->c_list.cl_cond[condX] = condlP->c_list.cl_cond[condX+1];
else
condlP->c_list.cl_disp[condX] = condlP->c_list.cl_disp[condX+1];
}
}
}
/*
*//* any_condition( condlP, handleF [, rtc ] )
Set or remove the "handle-any" condition
Accepts :
condlP Ptr to the condition list
handleF Whether to handle
rtc For an interception list, the routine that
intercepts the error.
Returns :
<nothing>
*/
void
any_condition( condlP, handleF, rtc )
ERRLIST *condlP; /* Ptr to condition list */
int handleF; /* Whether to handle */
int (*rtc)(); /* Handler */
{
if ( !handleF ) /* If not handling.. */
condlP->c_flags &= ~CF_ANY; /* Remove it */
else {
condlP->c_flags |= CF_ANY; /* Set the flag */
if ( condlP->c_type == CTINTCEPT )
/* Store the intercept routine */
condlP->c_any.ca_disp.d_rtc = rtc;
}
}
/*
*//* receive_error( context, condlP, msgPP )
Receive an error at a control point
Accepts:
context setjmp buffer used to establish the context
condlP Ptr to condition list structure that contains
the conditions handled at this point
msgPP Address of a variable that will receive the address
of the error message string.
Returns :
<value> Index into condition list, or one of the special
condition indexes (CXxxxx)
*/
void
receive_error( context, condlP, msgPP )
jmp_buf *context; /* Setjmp buffer for context */
ERRLIST *condlP; /* Ptr to condition list struct */
char **msgPP; /* Where to store error message */
{
ERRCTL *ectlP; /* Error control block */
/* Get memory for error control block */
ectlP = (ERRCTL *)malloc( sizeof(ERRCTL) );
if ( ectlP == NULL )
return_error( (ERRCOND)CCMEMORY, "Out of memory in receive_error()" );
/* Format the error control block */
ectlP->ec_type = CTHANDLER; /* Type is a handler block */
ectlP->ec_ctxtP = context; /* Ptr to context */
ectlP->ec_listP = condlP; /* Ptr to condition list */
ectlP->ec_msgPP = msgPP; /* Ptr to message ptr variable */
/* Add control block to internal list, and return. */
add_control( ectlP );
}
/*
*//* remove_error( context )
Remove an error handler that is going out of scope
Accepts :
context Context associated with handler
Returns :
<nothing>
*/
void
remove_error( context )
jmp_buf *context; /* Ptr to context */
{
int doneF; /* Done... */
for( doneF = FALSE; ( ClisttP != NULL ) && !doneF; ) {
doneF = ( ClisttP->ec_ctxtP == context );
remove_control();
}
}
/*
*//* handle_error( contrtc, contarg, condlP, msgPP )
Establish an error context, and receive an error
Accepts:
contrtc Continuation routine to call.
contarg Ptr to anything to be given to *contrtc
condlP Ptr to condition list structure that contains
the conditions handled at this point
msgP Address of a variable that will receive the address
of the error message string.
Returns :
<value> Index into condition list, or one of the special
condition indexes (CXxxxx)
*/
int
handle_error( contrtc, contarg, condlP, msgPP )
int (*contrtc)(); /* Continuation routine to call */
void *contarg; /* Arg to continuation routine */
ERRLIST *condlP; /* Ptr to condition list struct */
char **msgPP; /* Where to store error message */
{
int result; /* Result from error return. */
jmp_buf context; /* Execution context */
/* Establish context and accept error returns */
if ( ( result = setjmp( context ) ) != 0 )
/* Caught an error code. Return it to the caller. */
return( result );
/* setjmp context established. Proceed to setup the error context
via receive_error(). Note that receive_error(), if it doesn't
return normally, returns to the setjmp() call!
*/
receive_error( context, condlP, msgPP );
/* Call the specified continuation routine. */
(*contrtc)( contarg );
/* Context is going out of scope; remove the handler. */
remove_error( context );
/* Normal execution return; return CXNONE (no error return) */
return( CXNONE );
}
/*
*//* intercept_error( contrtc, contarg, conddP )
Specify routines that intercept error notifications
Accepts:
contrtc Continuation routine to call.
contarg Ptr to anything to be given to *contrtc
conddP Ptr to condition list dispatch structure that
specifies the conditions to intercept and
the routines that will intercept them.
Returns :
< nothing >
*/
void
intercept_error( contrtc, contarg, conddP )
int (*contrtc)(); /* Continuation routine to call */
void *contarg; /* Arg to continuation routine */
ERRLIST *conddP; /* Ptr to condition list struct */
{
ERRCTL *ectlP; /* Error control block */
/* Get memory for error control block */
ectlP = (ERRCTL *)malloc( sizeof(ERRCTL) );
if ( ectlP == NULL )
return_error( (ERRCOND)CCMEMORY, "Out of memory in intercept_error()" );
/* Format the error control block */
ectlP->ec_type = CTINTCEPT; /* Type is intercept handler block */
ectlP->ec_listP = conddP; /* Ptr to dispatch list */
/* Add control block to internal list */
add_control( ectlP );
/* Pass control to continuation routine */
(*contrtc)( contarg );
/* Remove the control block and return. */
while( ClisttP != ectlP )
remove_control();
}
/*
*//* return_error( cond, msgP )
Return an error condition to the appropriate handler
Accepts:
cond The condition being returned
msgP Text to be used to report the error
Returns :
<never returns; gives control to the appropriate handler>
Notes :
If no handler exists for the condition, a default action will
be taken. In this implementation, the default action is to print a
message about the lack of a handler, print the error message, and
exit.
*/
void
return_error( cond, msgP )
ERRCOND cond; /* Condition being returned */
char *msgP; /* Text of error message */
{
int condX; /* Condition index */
ERRCTL *ectlP; /* Ptr to control block */
ERRLIST *elistP; /* Ptr to err list block */
jmp_buf *ctxtP; /* Ptr to context */
char **msgPP; /* Ptr to message receiver var */
/* Capture the message string */
msgP = save_msg( msgP );
/* Search through the handlers */
for( ; ; ) {
if ( ( ectlP = ClisttP ) == NULL )
break; /* Ran out of handlers */
/* Process if it's associated with a handler */
if ( ectlP->ec_type == CTHANDLER ) {
/* Check the conditions for a match */
elistP = ectlP->ec_listP;
for( condX = 0; condX < elistP->c_condC; ++condX ) {
/* Check matching condition */
if ( cond == el_cond_inx( elistP, condX ) )
break;
}
if ( condX < elistP->c_condC )
break; /* Found a match */
/* Check for handle-any */
if ( ( elistP->c_flags & CF_ANY ) != 0 ) {
elistP->c_any.ca_cond = cond; /* Remember the "any" */
break;
}
}
/* Bypass non-matching handler; take it off the top. */
remove_control();
}
if ( ectlP == NULL ) {
/* No handler takes responsibility... note the error and exit. */
note_error( (ERRCOND)CCHANDLER, "return_error() can not find a handler." );
note_error( cond, msgP );
exit( 1 );
}
/* Get stuff out of the control block before we delete it */
ctxtP = ectlP->ec_ctxtP;
msgPP = ectlP->ec_msgPP;
/* Control block is now inactive and used. Remove it. */
remove_control();
/* Return the message. */
*msgPP = msgP;
/* Return to the setjmp; return the appropriate code. */
longjmp( ctxtP, condX+1 );
}
/*
*//* note_error( cond, msgP )
Note an error condition.
Accepts:
cond The condition being noted
msgP Text to be used to report the error
Returns :
<no return value>
Notes :
This routine is used to note and report an error. It is
subject to interception via the intercept_error() mechanism; that
is, if the condition matches any conditions specified by an
enclosing call to intercept_error(), the specified interception
routine is called. That routine may elect to call handle_error,
which would prevent this routine from returning to its caller.
If no interception routine is found, the error message is
put out to stderr, and this routine returns. Note that when an
interception routine is used, no error message output is done --
that's up to the interceptor.
*/
void
note_error( cond, msgP )
ERRCOND cond; /* Condition being noted */
char *msgP; /* Text of error message */
{
int condX; /* Condition index */
int foundF; /* If any handlers found */
ERRCTL *ectlP; /* Ptr to control block */
ERRLIST *elistP; /* Ptr to err list block */
int (*rtc)(); /* Handler routine */
/* Search through the interception routines and call matching ones */
for( foundF = FALSE, ectlP = ClisttP;
ectlP != NULL;
ectlP = ectlP->ec_prevP ) {
/* Process if it's associated with an interception routine */
if ( ectlP->ec_type == CTINTCEPT ) {
/* Check the conditions for a match */
elistP = ectlP->ec_listP;
for( rtc = NULL, condX = 0; condX < elistP->c_condC; ++condX ) {
/* Check matching condition */
if ( cond == el_cond_inx( elistP, condX ) ) {
rtc = elistP->c_list.cl_disp[condX].d_rtc;
break;
}
}
if ( rtc == NULL )
/* Check for "handle-any" */
if ( ( elistP->c_flags & CF_ANY ) != 0 )
rtc = elistP->c_any.ca_disp.d_rtc;
/* Call the interceptor if any found */
if ( rtc != NULL ) {
foundF = TRUE;
if ( (*rtc)( cond, msgP ) == 0 )
break;
}
}
}
/* Perform default action if no handlers found */
if ( !foundF ) {
fprintf( stderr, "note_error: condition=%d; %s\n", cond, msgP );
}
}
/*
*//* add_control( ectlP )
Add a control block to the local list
Accepts :
ectlP Ptr to block to add to the tail of the list
Returns :
<nothing>
*/
static void
add_control( ectlP )
ERRCTL *ectlP;
{
/* Add to tail of list */
ectlP->ec_prevP = ClisttP;
ClisttP = ectlP;
}
/*
*//* remove_control()
Remove control block from the tail of the list
Accepts :
Returns :
*/
static void
remove_control()
{
ERRCTL *ectlP; /* Ptr to control struct */
/* Remove from tail */
ectlP = ClisttP;
if ( ectlP != NULL ) {
ClisttP = ectlP->ec_prevP;
free( ectlP );
}
else
ClisttP = NULL;
}
/*
*//* save_msg( msgP )
Make a safe copy of a message string.
Accepts :
msgP Ptr to existing string
Returns :
<value> Ptr to copy of the string
Notes :
Because a message string may be passed from a call frame back to
a higher, enclosing frame, that message string may be invalid (i.e.,
if it is stored on the stack). This routine makes a copy of the
string and remembers it in a ring of recently created message string.
Eventually, the ring pointer comes around and the string is deleted.
The ring is large enough for even unreasonable use of message strings.
But because message strings are copied and eventually garbaged,
an application shouldn't assume that it can hold on to a returned
error message string forever. If it needs a persistent copy of
the string, it should make one. This ring storage of message text
is designed to let an application keep hold of a string for a
reasonable length of time, and to relieve the application from the
burden of having to eventually release it.
*/
static char *
save_msg( msgP )
char *msgP; /* Source string */
{
char *newmsgP; /* Temp. ptr to new string */
int i; /* Scratch */
/* See if the string is already in the ring; for example, a
call to return_error with a msg string that was returned
from a previous handle_error. */
for( i = MsgX+1; ; ++i ) {
if ( i == MSLSIZE )
i = 0;
if ( i == MsgX )
break;
if ( Msglist[i] == msgP )
break;
}
if ( i != MsgX )
/* Found the same string; disremember it. */
Msglist[i] = NULL;
else {
/* String not in ring; make a new copy */
newmsgP = malloc( strlen( msgP ) +1 );
if ( newmsgP == NULL ) {
fprintf( stderr, "Fatal memory allocation error in error handling system.\n" );
exit(1);
}
strcpy( newmsgP, msgP );
msgP = newmsgP;
}
/* Remember the string in the ring buffer. */
if ( Msglist[MsgX] != NULL )
free( Msglist[MsgX] ); /* Garbage old remembered string. */
Msglist[MsgX] = msgP;
/* Adjust the ring ptr */
if ( ++MsgX == MSLSIZE )
MsgX = 0;
return( msgP );
}