home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / TIMEXSRC.ZIP / ERRHAND.C < prev    next >
Text File  |  1990-03-29  |  18KB  |  702 lines

  1. /* errhand.c -- Error handling routines
  2.  
  3.     1990    MIPS Magazine / M. Mallett
  4.  
  5. This is the code for the error handling routines described in the
  6. column "Programming in the main()" in MIPS Magazine, January 1990.
  7.  
  8. Included are the following routines:
  9.  
  10.     add_condition    Add a condition to a condition list
  11.     any_condition    Add or remove the "handle-any" condition
  12.     condition_list    Make a new condition list
  13.     handle_error    Establish an error handling context
  14.     intercept_error    Specify routines that handle errors
  15.     note_error    Indicate that an error has occured
  16.     remove_condition Remove a condition from a condition list
  17.     remove_error    Remove a handler
  18.     return_error    Abnormally return error.
  19.     receive_error    Get return from error handling context
  20.  
  21. */
  22.  
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <setjmp.h>
  26. #include <malloc.h>
  27. #include "errhand.h"
  28.  
  29. /* Local definitions */
  30.  
  31. #ifndef    TRUE
  32. #define    TRUE    1
  33. #define    FALSE    0
  34. #endif    /* TRUE */
  35.  
  36. #define    MSLSIZE    20            /* Number of messages retained */
  37.  
  38. typedef                    /* Error control structure */
  39.   struct ectl {
  40.     struct ectl    *ec_prevP;        /* Backward pointer */
  41.     ECTYPE    ec_type;        /* Type of control block */
  42.     jmp_buf    *ec_ctxtP;        /* Context (setjmp/longjmp) */
  43.     ERRLIST    *ec_listP;        /* Ptr to error list */
  44.     char    **ec_msgPP;        /* Ptr to message receiver var */
  45.   } ERRCTL;
  46.  
  47.  
  48. /* External data referenced */
  49.  
  50.  
  51. /* External routines used */
  52.  
  53.  
  54. /* Local data publicly available */
  55.  
  56.  
  57. /* Local routines and forward references */
  58.  
  59. static    void    add_control();
  60. static    void    remove_control();
  61. static    char    *save_msg();
  62.  
  63. /* Private data */
  64.  
  65. static    ERRCTL    *ClisttP = NULL;    /* Ptr to list tail */
  66. static    char    *Msglist[MSLSIZE];    /* Message list */
  67. static    int    MsgX = 0;        /* Next message index */
  68. /*
  69.  
  70. *//* condition_list( condL )
  71.  
  72.     Make a new condition list
  73.  
  74. Accepts :
  75.  
  76.     condL        Length of condition list (max conditions)
  77.     type        Type of block to be constructed.
  78.  
  79. Returns :
  80.  
  81.     <value>        Ptr to a condition list struct
  82.  
  83. */
  84.  
  85. ERRLIST *
  86. condition_list( condL, type )
  87.     int        condL;        /* Length of list */
  88.     ECTYPE        type;        /* Type of list */
  89. {
  90.     int        size;        /* Size required */
  91.     ERRLIST        *condlP;    /* Ptr to list */
  92.  
  93.     /* Make new list */
  94.     size = sizeof(ERRLIST) +
  95.             ((condL +1 - ELSIZE) *
  96.             ( type == CTHANDLER ? sizeof(ERRCOND) : sizeof(EDISP) ) );
  97.  
  98.     condlP = (ERRLIST *)malloc( size );
  99.  
  100.     if ( condlP == NULL )
  101.     return_error( (ERRCOND)CCMEMORY, "Out of memory in condition_list()" );
  102.  
  103.     /* Format the block */
  104.     condlP->c_condC = 0;
  105.     condlP->c_condL = condL;
  106.     condlP->c_flags = 0;
  107.     condlP->c_type = type;
  108.  
  109.     /* Return it */
  110.     return( condlP );
  111. }
  112. /*
  113.  
  114. *//* add_condition( condlP, cond [, rtc ] )
  115.  
  116.     Add a condition to a condition list
  117.  
  118. Accepts :
  119.  
  120.     condlP        Ptr to the condition list
  121.     cond        The condition to be added
  122.     rtc        For an interception list, the routine that
  123.               intercepts the error.
  124.  
  125. Returns :
  126.  
  127.     <nothing>
  128.  
  129. */
  130.  
  131. void
  132. add_condition( condlP, cond, rtc )
  133.     ERRLIST        *condlP;    /* Ptr to condition list */
  134.     ERRCOND        cond;        /* Condition to add */
  135.     int        (*rtc)();    /* Handler */
  136. {
  137.     int        condX;        /* Condition index */
  138.  
  139.     /* Search through the list */
  140.     for( condX = 0; condX < condlP->c_condC; ++condX )
  141.     if ( cond == el_cond_inx( condlP, condX ) )
  142.         break;
  143.  
  144.     /* Only proceed if the condition isn't in the table.  Don't care if
  145.        it already is... */
  146.     if ( condX == condlP->c_condC ) {
  147.     /* Make sure there's room for the new condition plus a spot
  148.        for an "any" return. */
  149.     if ( ( condX + 1 ) > condlP->c_condL )
  150.         return_error( (ERRCOND)CCLISTFULL,
  151.              "Condition list full in add_condition()" );
  152.  
  153.     /* Add the condition */
  154.     ++condlP->c_condC;
  155.     if ( condlP->c_type == CTHANDLER )
  156.         condlP->c_list.cl_cond[condX] = cond;
  157.     else {
  158.         condlP->c_list.cl_disp[condX].d_cond = cond;
  159.         condlP->c_list.cl_disp[condX].d_rtc = rtc;
  160.     }
  161.     }
  162. }
  163. /*
  164.  
  165. *//* remove_condition( condlP, cond )
  166.  
  167.     Remove a condition from a condition list
  168.  
  169. Accepts :
  170.  
  171.     condlP        Ptr to the condition list struct
  172.     cond        The condition to remove
  173.  
  174. Returns :
  175.  
  176.     <nothing>
  177.  
  178. */
  179.  
  180. void
  181. remove_condition( condlP, cond )
  182.     ERRLIST        *condlP;    /* Ptr to condition list struct */
  183.     ERRCOND        cond;        /* Condition to remove */
  184. {
  185.     int        condX;        /* Condition index */
  186.  
  187.     /* Look for the condition */
  188.     for( condX = 0; condX < condlP->c_condC; ++condX )
  189.     if ( cond == el_cond_inx( condlP, condX ) )
  190.         break;
  191.  
  192.     /* Remove it if found; don't care otherwise */
  193.     if ( condX != condlP->c_condC ) {
  194.     /* Shuffle down the remaining conditions */
  195.     for( --condlP->c_condC; condX < condlP->c_condC; ++condX ) {
  196.         if ( condlP->c_type == CTHANDLER )
  197.         condlP->c_list.cl_cond[condX] = condlP->c_list.cl_cond[condX+1];
  198.         else
  199.         condlP->c_list.cl_disp[condX] = condlP->c_list.cl_disp[condX+1];
  200.     }
  201.     }
  202. }
  203. /*
  204.  
  205. *//* any_condition( condlP, handleF [, rtc ] )
  206.  
  207.     Set or remove the "handle-any" condition
  208.  
  209. Accepts :
  210.  
  211.     condlP        Ptr to the condition list
  212.     handleF        Whether to handle
  213.     rtc        For an interception list, the routine that
  214.               intercepts the error.
  215.  
  216. Returns :
  217.  
  218.     <nothing>
  219.  
  220. */
  221.  
  222. void
  223. any_condition( condlP, handleF, rtc )
  224.     ERRLIST        *condlP;    /* Ptr to condition list */
  225.     int        handleF;    /* Whether to handle */
  226.     int        (*rtc)();    /* Handler */
  227. {
  228.     if ( !handleF )            /* If not handling.. */
  229.     condlP->c_flags &= ~CF_ANY;    /* Remove it */
  230.     else {
  231.     condlP->c_flags |= CF_ANY;    /* Set the flag */
  232.  
  233.     if ( condlP->c_type == CTINTCEPT )
  234.         /* Store the intercept routine */
  235.         condlP->c_any.ca_disp.d_rtc = rtc;
  236.     }
  237. }
  238. /*
  239.  
  240. *//* receive_error( context, condlP, msgPP )
  241.  
  242.     Receive an error at a control point
  243.  
  244. Accepts:
  245.  
  246.     context        setjmp buffer used to establish the context
  247.     condlP        Ptr to condition list structure that contains
  248.               the conditions handled at this point
  249.     msgPP        Address of a variable that will receive the address
  250.               of the error message string.
  251.  
  252. Returns :
  253.  
  254.     <value>        Index into condition list, or one of the special
  255.               condition indexes (CXxxxx)
  256.  
  257. */
  258.  
  259. void
  260. receive_error( context, condlP, msgPP )
  261.     jmp_buf        *context;    /* Setjmp buffer for context */
  262.     ERRLIST        *condlP;    /* Ptr to condition list struct */
  263.     char        **msgPP;    /* Where to store error message */
  264. {
  265.     ERRCTL        *ectlP;        /* Error control block */
  266.  
  267.     /* Get memory for error control block */
  268.     ectlP = (ERRCTL *)malloc( sizeof(ERRCTL) );
  269.     if ( ectlP == NULL )
  270.     return_error( (ERRCOND)CCMEMORY, "Out of memory in receive_error()" );
  271.  
  272.     /* Format the error control block */
  273.     ectlP->ec_type = CTHANDLER;        /* Type is a handler block */
  274.     ectlP->ec_ctxtP = context;        /* Ptr to context */
  275.     ectlP->ec_listP = condlP;        /* Ptr to condition list */
  276.     ectlP->ec_msgPP = msgPP;        /* Ptr to message ptr variable */
  277.  
  278.     /* Add control block to internal list, and return. */
  279.     add_control( ectlP );
  280. }
  281. /*
  282.  
  283. *//* remove_error( context )
  284.  
  285.     Remove an error handler that is going out of scope
  286.  
  287. Accepts :
  288.  
  289.     context        Context associated with handler
  290.  
  291. Returns :
  292.  
  293.     <nothing>
  294.  
  295. */
  296.  
  297. void
  298. remove_error( context )
  299.     jmp_buf        *context;    /* Ptr to context */
  300. {
  301.     int        doneF;        /* Done... */
  302.  
  303.     for( doneF = FALSE; ( ClisttP != NULL ) && !doneF; ) {
  304.     doneF = ( ClisttP->ec_ctxtP == context );
  305.     remove_control();
  306.     }
  307. }
  308. /*
  309.  
  310. *//* handle_error( contrtc, contarg, condlP, msgPP )
  311.  
  312.     Establish an error context, and receive an error
  313.  
  314. Accepts:
  315.  
  316.     contrtc        Continuation routine to call.
  317.     contarg        Ptr to anything to be given to *contrtc
  318.     condlP        Ptr to condition list structure that contains
  319.               the conditions handled at this point
  320.     msgP        Address of a variable that will receive the address
  321.               of the error message string.
  322.  
  323. Returns :
  324.  
  325.     <value>        Index into condition list, or one of the special
  326.               condition indexes (CXxxxx)
  327.  
  328. */
  329.  
  330. int
  331. handle_error( contrtc, contarg, condlP, msgPP )
  332.     int        (*contrtc)();    /* Continuation routine to call */
  333.     void        *contarg;    /* Arg to continuation routine */
  334.     ERRLIST        *condlP;    /* Ptr to condition list struct */
  335.     char        **msgPP;    /* Where to store error message */
  336. {
  337.     int        result;        /* Result from error return. */
  338.     jmp_buf        context;    /* Execution context */
  339.  
  340.     /* Establish context and accept error returns */
  341.     if ( ( result = setjmp( context ) ) != 0 )
  342.     /* Caught an error code.  Return it to the caller. */
  343.     return( result );
  344.  
  345.     /* setjmp context established.  Proceed to setup the error context
  346.        via receive_error().  Note that receive_error(), if it doesn't
  347.        return normally, returns to the setjmp() call!
  348.     */
  349.     receive_error( context, condlP, msgPP );
  350.  
  351.     /* Call the specified continuation routine. */
  352.     (*contrtc)( contarg );
  353.  
  354.     /* Context is going out of scope; remove the handler. */
  355.     remove_error( context );
  356.  
  357.     /* Normal execution return; return CXNONE (no error return) */
  358.     return( CXNONE );
  359. }
  360. /*
  361.  
  362. *//* intercept_error( contrtc, contarg, conddP )
  363.  
  364.     Specify routines that intercept error notifications
  365.  
  366. Accepts:
  367.  
  368.     contrtc        Continuation routine to call.
  369.     contarg        Ptr to anything to be given to *contrtc
  370.     conddP        Ptr to condition list dispatch structure that
  371.               specifies the conditions to intercept and
  372.               the routines that will intercept them.
  373.  
  374. Returns :
  375.  
  376.     < nothing >
  377.  
  378. */
  379.  
  380. void
  381. intercept_error( contrtc, contarg, conddP )
  382.     int        (*contrtc)();    /* Continuation routine to call */
  383.     void        *contarg;    /* Arg to continuation routine */
  384.     ERRLIST        *conddP;    /* Ptr to condition list struct */
  385. {
  386.     ERRCTL        *ectlP;        /* Error control block */
  387.  
  388.     /* Get memory for error control block */
  389.     ectlP = (ERRCTL *)malloc( sizeof(ERRCTL) );
  390.     if ( ectlP == NULL )
  391.     return_error( (ERRCOND)CCMEMORY, "Out of memory in intercept_error()" );
  392.  
  393.     /* Format the error control block */
  394.     ectlP->ec_type = CTINTCEPT;        /* Type is intercept handler block */
  395.     ectlP->ec_listP = conddP;        /* Ptr to dispatch list */
  396.  
  397.     /* Add control block to internal list */
  398.     add_control( ectlP );
  399.  
  400.     /* Pass control to continuation routine */
  401.     (*contrtc)( contarg );
  402.  
  403.     /* Remove the control block and return. */
  404.     while( ClisttP != ectlP )
  405.     remove_control();
  406. }
  407. /*
  408.  
  409. *//* return_error( cond, msgP )
  410.  
  411.     Return an error condition to the appropriate handler
  412.  
  413. Accepts:
  414.  
  415.     cond        The condition being returned
  416.     msgP        Text to be used to report the error
  417.  
  418. Returns :
  419.  
  420.     <never returns; gives control to the appropriate handler>
  421.  
  422. Notes :
  423.  
  424.     If no handler exists for the condition, a default action will
  425. be taken.  In this implementation, the default action is to print a
  426. message about the lack of a handler, print the error message, and
  427. exit.
  428.  
  429. */
  430.  
  431. void
  432. return_error( cond, msgP )
  433.     ERRCOND        cond;        /* Condition being returned */
  434.     char        *msgP;        /* Text of error message */
  435. {
  436.     int        condX;        /* Condition index */
  437.     ERRCTL        *ectlP;        /* Ptr to control block */
  438.     ERRLIST        *elistP;    /* Ptr to err list block */
  439.     jmp_buf        *ctxtP;        /* Ptr to context */
  440.     char        **msgPP;    /* Ptr to message receiver var */
  441.  
  442.     /* Capture the message string */
  443.     msgP = save_msg( msgP );
  444.  
  445.     /* Search through the handlers */
  446.     for( ; ; ) {
  447.     if ( ( ectlP = ClisttP ) == NULL )
  448.         break;            /* Ran out of handlers */
  449.  
  450.     /* Process if it's associated with a handler */
  451.     if ( ectlP->ec_type == CTHANDLER ) {
  452.         /* Check the conditions for a match */
  453.         elistP = ectlP->ec_listP;
  454.         for( condX = 0; condX < elistP->c_condC; ++condX ) {
  455.         /* Check matching condition */
  456.         if ( cond == el_cond_inx( elistP, condX ) )
  457.             break;
  458.         }
  459.  
  460.         if ( condX < elistP->c_condC )
  461.         break;            /* Found a match */
  462.  
  463.         /* Check for handle-any */
  464.         if ( ( elistP->c_flags & CF_ANY ) != 0 ) {
  465.             elistP->c_any.ca_cond = cond;  /* Remember the "any" */
  466.         break;
  467.         }
  468.     }
  469.  
  470.     /* Bypass non-matching handler; take it off the top. */
  471.     remove_control();
  472.     }
  473.  
  474.     if ( ectlP == NULL ) {
  475.     /* No handler takes responsibility... note the error and exit. */
  476.     note_error( (ERRCOND)CCHANDLER, "return_error() can not find a handler." );
  477.     note_error( cond, msgP );
  478.     exit( 1 );
  479.     }
  480.  
  481.     /* Get stuff out of the control block before we delete it */
  482.     ctxtP = ectlP->ec_ctxtP;
  483.     msgPP = ectlP->ec_msgPP;
  484.  
  485.     /* Control block is now inactive and used.  Remove it. */
  486.     remove_control();
  487.  
  488.     /* Return the message. */
  489.     *msgPP = msgP;
  490.  
  491.     /* Return to the setjmp; return the appropriate code. */
  492.     longjmp( ctxtP, condX+1 );
  493. }
  494. /*
  495.  
  496. *//* note_error( cond, msgP )
  497.  
  498.     Note an error condition.
  499.  
  500. Accepts:
  501.  
  502.     cond        The condition being noted
  503.     msgP        Text to be used to report the error
  504.  
  505. Returns :
  506.  
  507.     <no return value>
  508.  
  509. Notes :
  510.  
  511.     This routine is used to note and report an error.  It is
  512. subject to interception via the intercept_error() mechanism; that
  513. is, if the condition matches any conditions specified by an
  514. enclosing call to intercept_error(), the specified interception
  515. routine is called.  That routine may elect to call handle_error,
  516. which would prevent this routine from returning to its caller.
  517.  
  518.     If no interception routine is found, the error message is
  519. put out to stderr, and this routine returns.  Note that when an
  520. interception routine is used, no error message output is done --
  521. that's up to the interceptor.
  522.  
  523. */
  524.  
  525. void
  526. note_error( cond, msgP )
  527.     ERRCOND        cond;        /* Condition being noted */
  528.     char        *msgP;        /* Text of error message */
  529. {
  530.     int        condX;        /* Condition index */
  531.     int        foundF;        /* If any handlers found */
  532.     ERRCTL        *ectlP;        /* Ptr to control block */
  533.     ERRLIST        *elistP;    /* Ptr to err list block */
  534.     int        (*rtc)();    /* Handler routine */
  535.  
  536.     /* Search through the interception routines and call matching ones */
  537.     for( foundF = FALSE, ectlP = ClisttP;
  538.             ectlP != NULL;
  539.             ectlP = ectlP->ec_prevP ) {
  540.  
  541.     /* Process if it's associated with an interception routine */
  542.     if ( ectlP->ec_type == CTINTCEPT ) {
  543.         /* Check the conditions for a match */
  544.         elistP = ectlP->ec_listP;
  545.         for( rtc = NULL, condX = 0; condX < elistP->c_condC; ++condX ) {
  546.         /* Check matching condition */
  547.         if ( cond == el_cond_inx( elistP, condX ) ) {
  548.             rtc = elistP->c_list.cl_disp[condX].d_rtc;
  549.             break;
  550.         }
  551.         }
  552.  
  553.         if ( rtc == NULL )
  554.         /* Check for "handle-any" */
  555.         if ( ( elistP->c_flags & CF_ANY ) != 0 )
  556.             rtc = elistP->c_any.ca_disp.d_rtc;
  557.  
  558.         /* Call the interceptor if any found */
  559.         if ( rtc != NULL ) {
  560.         foundF = TRUE;
  561.         if ( (*rtc)( cond, msgP ) == 0 )
  562.             break;
  563.         }
  564.     }
  565.     }
  566.  
  567.     /* Perform default action if no handlers found */
  568.     if ( !foundF ) {
  569.     fprintf( stderr, "note_error: condition=%d; %s\n", cond, msgP );
  570.     }
  571. }
  572. /*
  573.  
  574. *//* add_control( ectlP )
  575.  
  576.     Add a control block to the local list
  577.  
  578. Accepts :
  579.  
  580.     ectlP        Ptr to block to add to the tail of the list
  581.  
  582. Returns :
  583.  
  584.     <nothing>
  585.  
  586. */
  587.  
  588. static void
  589. add_control( ectlP )
  590.     ERRCTL        *ectlP;
  591. {
  592.     /* Add to tail of list */
  593.     ectlP->ec_prevP = ClisttP;
  594.     ClisttP = ectlP;
  595. }
  596. /*
  597.  
  598. *//* remove_control()
  599.  
  600.     Remove control block from the tail of the list
  601.  
  602. Accepts :
  603.  
  604.  
  605. Returns :
  606.  
  607.  
  608. */
  609.  
  610. static void
  611. remove_control()
  612. {
  613.     ERRCTL        *ectlP;        /* Ptr to control struct */
  614.  
  615.     /* Remove from tail */
  616.     ectlP = ClisttP;
  617.     if ( ectlP != NULL ) {
  618.     ClisttP = ectlP->ec_prevP;
  619.     free( ectlP );
  620.     }
  621.     else
  622.     ClisttP = NULL;
  623. }
  624. /*
  625.  
  626. *//* save_msg( msgP )
  627.  
  628.     Make a safe copy of a message string.
  629.  
  630. Accepts :
  631.  
  632.     msgP        Ptr to existing string
  633.  
  634. Returns :
  635.  
  636.     <value>        Ptr to copy of the string
  637.  
  638.  
  639. Notes :
  640.  
  641.     Because a message string may be passed from a call frame back to
  642.     a higher, enclosing frame, that message string may be invalid (i.e.,
  643.     if it is stored on the stack).  This routine makes a copy of the
  644.     string and remembers it in a ring of recently created message string.
  645.     Eventually, the ring pointer comes around and the string is deleted.
  646.  
  647.     The ring is large enough for even unreasonable use of message strings.
  648.     But because message strings are copied and eventually garbaged,
  649.     an application shouldn't assume that it can hold on to a returned
  650.     error message string forever.  If it needs a persistent copy of
  651.     the string, it should make one.  This ring storage of message text
  652.     is designed to let an application keep hold of a string for a
  653.     reasonable length of time, and to relieve the application from the
  654.     burden of having to eventually release it.
  655.  
  656. */
  657.  
  658. static char *
  659. save_msg( msgP )
  660.     char        *msgP;        /* Source string */
  661. {
  662.     char        *newmsgP;    /* Temp. ptr to new string */
  663.     int        i;        /* Scratch */
  664.  
  665.     /* See if the string is already in the ring; for example, a
  666.        call to return_error with a msg string that was returned
  667.        from a previous handle_error. */
  668.     for( i = MsgX+1; ; ++i ) {
  669.     if ( i == MSLSIZE )
  670.         i = 0;
  671.     if ( i == MsgX )
  672.         break;
  673.     if ( Msglist[i] == msgP )
  674.         break;
  675.     }
  676.  
  677.     if ( i != MsgX )
  678.     /* Found the same string; disremember it. */
  679.     Msglist[i] = NULL;
  680.     else {
  681.     /* String not in ring; make a new copy */
  682.     newmsgP = malloc( strlen( msgP ) +1 );
  683.     if ( newmsgP == NULL ) {
  684.         fprintf( stderr, "Fatal memory allocation error in error handling system.\n" );
  685.         exit(1);
  686.     }
  687.     strcpy( newmsgP, msgP );
  688.     msgP = newmsgP;
  689.     }
  690.  
  691.     /* Remember the string in the ring buffer. */
  692.     if ( Msglist[MsgX] != NULL )
  693.     free( Msglist[MsgX] );        /* Garbage old remembered string. */
  694.     Msglist[MsgX] = msgP;
  695.  
  696.     /* Adjust the ring ptr */
  697.     if ( ++MsgX == MSLSIZE )
  698.     MsgX = 0;
  699.  
  700.     return( msgP );
  701. }
  702.