UMAFailure.h

/*[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n-]*/
/* UFailure.p */
/* Copyright © 1985-1990 by Apple Computer, Inc.  All rights reserved. */
 
/*
                T H E O R Y  O F     O P E R A T I O N
 
    This unit implements the MacApp failure mechanism.
 
    The failure mechanism is built around exception handlers.   An exception
    handler is a routine, generally local to some other routine, that is
    called when a failure occurs and takes action to handle the failure.
    An exception handler is of the form
 
    PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
 
    where error is the error that caused the failure, and message identifies
    the error message that may be displayed.  Consider a routine that opens
    a file, reads its contents, and closes the file.  If a failure occured
    while reading the file, an exception handler would be needed to close the
    file, as the rest of the routine will not be executed.  (See the example
    at the end of these comments.)
 
    References to expection handlers are defined by the FailInfo record.
    The exception handlers form a linked-list via the nextInfo field of
    FailInfo.   The linked list is a stack since new exception handlers
    are added to the front of the list.
 
    New exception handlers are added to the stack with the CatchFailures
    procedure, and removed from the stack with the Success procedure.   In
    general you call CatchFailures to post an exception handler when an
    error the application should handle might occur, and call Success to
    remove the handler from the stack, after the handler is no longer
    needed (i.e. the possibility of error no longer exists).  Any failure
    detected within the limits of the CatchFailures and subsequent Success
    call results in the execution of the exception handler.  (Failure does
    not have to occur in the same routine as your call to CatchFailures.
    The failure may occur in any routine called after CatchFailures but
    before Success.)
 
    When MacApp (or your code) determines that a failure has occured, it
    calls Failure.  As a convenience, several procedures are provided
    to check for standard kinds of failures and call Failure if needed.
    These procedures are:
 
    FailNIL         Calls Failure if its parameter is NIL.
    FailOSErr       Calls Failure if its parameter is not noErr.
    FailMemError    Calls Failure if MemError returns other than noErr.
    FailResError    Calls Failure if ResError returns other than noErr.
 
    When Failure is called, execution of the routine that called Failure is
    terminated and the exception handler at the top of the stack is popped.
    For each routine that was called after the handler was posted
    to the stack, execution is terminated as though from an EXIT statement.
    Then the exception handler is called.   It generally cleans up for the
    routine in which it is nested.  Upon completion the next exception handler
    is popped from the stack, repeating the process.
 
    The error causing the failure, and a message code is passed to Failure.
    For MacApp, the last exception handler on the stack is the one in
    TApplication.PollEvent.  It calls TApplication.ShowError, which calls
    ErrorAlert, which decodes the message and displays an alert.  You exception
    handlers may set the message code to one more specific to your application
    by calling FailNewMessage at the end of your exception handler.
    FailNewMessage changes the message only if the current one is non-zero.
    This has the effect of allowing those exception handlers closest to the
    source of the error to set the message.
 
    One last note about exception handlers:  It is possible for an exception
    handler to terminate exception processing by using a non-local GOTO to
    jump back into the routine in which the exception handler is nested.  This
    is how MacApp keeps the application running when a failure occurs.  The
    last exception handler on the stack, in TApplication.PollEvent, uses a
    GOTO to continue event processing.
 
    The following is an example showing the use of exception handlers.
    The ReadTheFile procedure reads the contents of a file, signalling
    failure if the open, read, or close returns an error.
 
    PROCEDURE ReadTheFile (fileName: Str255; volRefNum: INTEGER;
                            VAR contents: ContentsRecord);
    VAR
        fi:         FailInfo;
        count:      LONGINT;
 
        PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
        VAR
            err:    OSErr;
        BEGIN
            err := FSClose(fileRefNum);     { Make sure the file is closed }
            { Pass on my own error message... }
            FailNewMessage(error, message, kMyErrorMessage);
        END;
 
    BEGIN
        { If FSOpen fails we don't need to do any failure handling,
            though the guy that called us probably will. }
        FailOSErr(FSOpen(fileName, volRefNum, fileRefNum));
 
        { Now that the file is open, if the read fails we must make
            sure to close the file. }
        CatchFailures(fi, Exceptionhandler);
 
        count := SIZEOF(ContentsRecord);
        FailOSErr(FSRead(fileRefNum, count, @contents));
 
        { The file's been read so we don't need our exception handler
            anymore. }
        Success(fi);
 
        { Again, the guy that called us will have to deal with this. }
        FailOSErr(FSClose(fileRefNum));
    END;
*/
 
#ifndef  __UFailure__
#define __UFailure__  0
#endif
#if  ! __UFailure__
#define __UFailure__  1
 
        /* ¥ Auto-Include the requirements for this unit's interface. */
#ifndef  __TYPES__
#include "Types.h"
#endif
 
const short minErr              = - 32768;              /* Low error number */
const short maxErr              = 32767;                /* High error number */
            /* used for the exception handling mechanism */
            /* Preferred. The pointer type _MUST_ be declared first
               since the record is self referential */
typedef struct FailInfo *FailInfoPtr;
struct FailInfo {
    long regs[11];                          /* The saved registers as of the
                                               CatchFailures. */
    short error;                            /* The error condition passed to Failure. */
    long message;                           /* The message which accompanied the Failure. */
    long failA6;                            /* A6 of the caller to CatchFailures. */
    long failPC;                            /* Address of failure handler routine. */
    FailInfoPtr nextInfo;                   /* Next in stack. */
};
typedef FailInfoPtr PFailInfo;                          /* Left in for compatibility (2.0) */
 
extern pascal FailInfoPtr gTopHandler;
   /* Most recent link in linked list of failure
      handlers. Set to nil by a constant in
      UFailure.a so that Failure handling never
      needs to be initialized. */
 
extern pascal void Assertion(Boolean condition, StringPtr description);
        /* Asserts a condition.If condition is FALSE : IF debugging : prints description and
           stops in Debugger ELSE : Signals a general failure. */
 
pascal long BuildMessage(short lowWord, short highWord)
    =  0x2E9F;                                          /* MOVE.L (A7)+,(A7) */
        /* Takes the 2 integers and combines them into a LONGINT failure message.  Note that the
        low-order word is the first parameter. */
 
extern pascal void CatchFailures(FailInfo *fi, pascal void (*Handler)(short e, long m, void *
   Handler_StaticLink), void *Handler_StaticLink);
        /* Call this to set up an exception handler. This pushes your handler onto
        a stack of exception handlers. */
 
extern pascal void EachFailureHandlerDo(pascal void (*DoToHandler)(FailInfoPtr fiPtr, void *
   DoToHandler_StaticLink), void *DoToHandler_StaticLink);
        /* Calls DoToHandler for each failure handler in the stack from gTopHandler
        to the outermost handler. */
 
extern pascal void Failure(short error, long message);
        /* Call this to signal a failure.  Control will branch to the most recent
        exception handler, which will be popped off the handler stack. */
 
extern pascal void FailMemError(void);
        /* IF MemError <> noErr THEN Failure(MemError, 0); If you are using
        assembler, then you should just test the return code from the Memory
        Manager in DO by calling FailOSErr. (See the discussion of MemError in
        Inside Macintosh.) */
 
extern pascal void FailResError(void);
        /* IF ResError <> noErr THEN Failure(ResError, 0); (See Inside Macintosh.) */
 
extern pascal void FailNewMessage(short error, long oldMessage, long newMessage);
        /* This does:
        IF oldMessage = 0 THEN
            Failure(error, newMessage)
        ELSE
            Failure(error, oldMessage);
        */
 
extern pascal void FailNIL(void *p);
        /* Call this with a pointer/handle; this signals Failure(memFullErr, 0) if p is NIL. */
 
extern pascal void FailNILResource(void *r);
        /* Call this with a resource handle; this signals Failure if the handle is nil. The error is
        either that returned by ResError, or resNotFound if ResError returns no error. */
 
extern pascal void FailOSErr(short error);
        /* Call this with an OSError; signals Failure(error, 0) if error <> noErr. */
 
extern pascal Boolean HandlerExists(FailInfoPtr testFailInfoPtr);
        /* This test returns TRUE if the failure handler exists in the stack from gTopHandler
        to the outermost handler.  */
 
extern pascal void Success(FailInfo *fi);
        /* Call this when you want to pop your exception handler. We assume that the programmer
           passes
        in the most recent FailInfo record; ie. the one that is the top of the stack. If debugging
        is on, we check to be sure. */
#endif