static const char *rcsid = "$Id: OEObject.m,v 1.3 1997/10/23 02:06:09 art Exp $ cc: "__DATE__" "__TIME__;

// Suppress compiler warning about rcsid being unused, yet prevent assembler
// code for this function from being produced.
inline extern const char *suppressCompilerWarning(void)
{
    return rcsid;
}

//
// --------------------- OEObject Class Implementation ------------------------
//
//  (see .h for information)
//
// NO WARRANTY:
// ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
// IS HEREBY DISCLAIMED.  IN NO EVENT WILL THE AFOREMENTIONED PARTIES BE LIABLE
// FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES ARISING OUT OF THE USE OF OR INABILITY TO USE THIS CODE.
//
// Things to fix:
//  1.  Handle functions better
//	a.  Perhaps use frame length to figure it out?
//	b.  Compiler can probably help
//  2.  Find the return value and use that information on Windows
//  3.  Test more on hppa and sparc.  Problem here is that the information
//  is not actually on the stack, it's in registers which get blown away by
//  the next call.
//  4.  Localization
// ----------------------------------------------------------------------------
//

// ----------------------------- Header Files ---------------------------------

#import <Foundation/Foundation.h>
#import <Foundation/NSDebug.h>
#import <objc/Object.h>
#import <objc/objc-class.h>
#import <objc/objc-runtime.h>

#ifndef WIN32
#import <sys/signal.h>
#else WIN32
#import <signal.h>
#import <windows.h>
#endif WIN32

#import "OEObject.h"

// ---------------- Typedef, Struct, and Union Declarations -------------------

#ifndef WIN32
typedef struct _signalItem
{
    int number;
    BOOL isOn;
    NSString *message;
} OESignalItem_t;
#endif WIN32

typedef struct _objcTypeDesc
{
    NSString *format;
    NSString *name;
} OEObjcTypeDesc_t;

typedef struct _objcType
{
    char encoding;
    OEObjcTypeDesc_t description;
} OEObjcType_t;

typedef struct _returnInfo
{
    int type;
    int size;
} OEReturnInfo_t;

// --------------------- Constant and Enum Definitions ------------------------

// Default number of function arguments printed.
static unsigned functionArgCountG = 4;

// Default number of stack frames printed.
static unsigned frameCountG = 50;

// Default maximum argument description length.
static unsigned maxArgDescriptionLengthG = 255;

// Name of exception raised to continue execution.
static NSString *crashExceptionG = @"Crash";

// Name of defaults variables that can be used to set the function argument and
// stack frame counts and maximum argument description length.
static NSString *functionArgCountDefaultsNameG = @"OEFunctionArgCount";
static NSString *frameCountDefaultsNameG = @"OEFrameCount";
static NSString *maxArgDescriptionLengthDefaultsNameG =
    @"OEMaxArgDescriptionLength";

// Telltale string that occurs in error message when a freed object is
// messaged.
static NSString *freedObjectMessageG = @"freed object";

// Class name printed in backtrace for a freed object.
static NSString *freedObjectNameG = @"<FreedObject>";

#ifndef WIN32
static NSString *endOfLineG = @"\n";
#else WIN32

// Make stack backtrace display nicely in NT's EventViewer.
static NSString *endOfLineG = @"\r\n";
#endif WIN32

// ASSUMPTION:  The layout of a stack frame for a method invocation is:
//	fp+0 bytes:	calling frame
//	fp+4 bytes:	calling pc
//	fp+8 bytes:	self
//	fp+12 bytes:	selector for method invoked
//	fp+16 bytes:	first argument
//
// ASSUMPTION: The layout of a stack frame for a function invocation is:
//	fp+0 bytes:	calling frame
//	fp+4 bytes:	calling pc
//	fp+8 bytes:	first argument
//
// Clearly these are shady assumptions, however we're already in the process of
// crashing, so what harm can be done?

// WIN32 assumptions

// Stack layout is the same, except in cases where the return value from the
// current frame is a structure, when all arguments get shifted away.

// Stack unwind definitions.  These are offsets from the frame pointer where
// the previous frame pointer is stored, and where the return pointer is
// stored.  (These offsets don't seem to be used, so why are they defined??)

#if defined(i386) || defined(m68k)
#define FP_WORD_OFFSET 0
#define RP_WORD_OFFSET 1
#elif defined(hppa)
#define FP_WORD_OFFSET 0
#define RP_WORD_OFFSET -5
#define STACK_GROWS_UP 1
#elif defined(sparc)
#define FP_WORD_OFFSET 14
#define RP_WORD_OFFSET 15
#else
#error Stack unwind not defined for this architecture
#endif

// Mach m68k/i486 assumptions

// Given the frame pointer, where are the arguments?
static int argStartPositionG = 8;

// Given the location of the arguments, where are self and the selector?
#if defined(STACK_GROWS_UP)
static int selfIndexG = -1;
static int sELIndexG = -2;
#else
static int selfIndexG = 0;
static int sELIndexG = 1;
#endif

#ifndef WIN32
#define ON 1
#define OFF 0

static OESignalItem_t OESignals[] =
{
    {0, 	OFF, 	@"Exit from the shell"},
    {SIGHUP, 	OFF, 	@"Hangup"},
    {SIGINT, 	OFF, 	@"Interrupt"},
    {SIGQUIT, 	ON, 	@"Quit"},
    {SIGILL, 	ON, 	@"Illegal instruction"},
    {SIGTRAP, 	ON, 	@"Trace trap"},
    {SIGIOT, 	ON, 	@"IOT instruction"},
    {SIGEMT, 	ON, 	@"EMT instruction"},
    {SIGFPE, 	ON, 	@"Floating point exception"},
    {SIGKILL, 	OFF, 	@"Kill"},
    {SIGBUS, 	ON, 	@"Bus error"},
    {SIGSEGV, 	ON, 	@"Segmentation violation"},
    {SIGSYS, 	ON, 	@"Bad argument to system call"},
    {SIGPIPE, 	OFF, 	@"Write on a pipe with no one to read it"},
    {SIGALRM, 	OFF, 	@"Alarm clock"},
    {SIGTERM, 	OFF, 	@"Software termination"},
    {SIGURG, 	OFF, 	@"Urgent condition present on socket"},
    {SIGSTOP, 	OFF, 	@"Stop"},
    {SIGTSTP, 	OFF, 	@"Stop signal generated from keyboard"},
    {SIGCONT, 	OFF, 	@"Continue after stop"},
    {SIGCHLD, 	OFF, 	@"Child status changed"},
    {SIGTTIN, 	OFF, 	@"Background read attempted from control terminal"},
    {SIGTTOU, 	OFF, 	@"Background write attempted to control terminal"},
    {SIGIO, 	OFF, 	@"I/O is possible on a descriptor"},
    {SIGXCPU, 	OFF, 	@"CPU time limit is exceeded"},
    {SIGXFSZ, 	OFF, 	@"File size limit exceeded"},
    {SIGVTALRM, OFF, 	@"Virtual timer alarm"},
    {SIGPROF, 	OFF, 	@"Profiling timer alarm"},
    {SIGWINCH, 	OFF, 	@"Window size change"},
    {SIGUSR1, 	OFF, 	@"User defined signal 1"},
    {SIGUSR2, 	OFF, 	@"User defined signal 2"}
};
#endif WIN32

static OEObjcType_t OEEncodings[] =
{
    {_C_ID,		{@"%@",		@"id"}},
    {_C_CLASS,		{@"%@",		@"Class"}},
    {_C_SEL,		{@"%s",		@"SEL"}},
    {_C_CHR,		{@"%c",		@"char"}},
    {_C_UCHR,		{@"%c",		@"unsigned char"}},
    {_C_SHT,		{@"%hd",	@"short"}},
    {_C_USHT,		{@"%hu",	@"unsigned short"}},
    {_C_INT,		{@"%d",		@"int"}},
    {_C_UINT,		{@"%u",		@"unsigned"}},
    {_C_LNG,		{@"%ld",	@"long"}},
    {_C_ULNG,		{@"%lu",	@"unsigned long"}},
    {_C_FLT,		{@"%f",		@"float"}},
    {_C_DBL,		{@"%f",		@"double"}},
    {_C_VOID,		{@"%#08x",	@"void"}},
    {_C_PTR,		{@"%#08x",	@"ptr"}},
    {_C_CHARPTR,	{@"%.*s",	@"char *"}},
    {_C_STRUCT_B,	{@"%x",		@"struct"}}
};

// ------------------------- Function Declarations ----------------------------

#ifndef WIN32
static void handle_signal(int aSignal);
#else WIN32
static int filter(LPEXCEPTION_POINTERS theInfo);
#endif WIN32

// The _error() function prototype has an Object rather than NSObject first
// argument type.
static void (*_originalError)(Object *anObject, const char *aFormat,
                              va_list theArguments);
static void _objectError(Object *anObject, const char *aFormat,
                         va_list theArguments);

@interface OEObject(Private)
// ---------------------- Private Method Declarations -------------------------
#ifndef WIN32
+ (void)_setSignalHandler:(void (*)())aHandler;
#endif WIN32
+ (NSString *)_prepareToPrintBacktrace:(NSString *)aMessage;
+ (NSString *)_printFrame:(void *)aFramePointer
              withMessage:(NSString *)aMessage;
+ (NSString *)_functionMessageFromFP:(void *)aFramePointer;
+ (NSString *)_methodMessageWithFP:(void *)aFramePointer
                          selector:(SEL)aSelector;
+ (BOOL)_isReturnStructInRegistersWithSize:(int)aSize;

// returnValue argument is always NULL - future enhancement??
+ (id)_selfWithFrame:(void *)anArgumentFrame
         returnValue:(OEReturnInfo_t *)aReturnValue;
+ (SEL)_selectorWithFrame:(void *)anArgumentFrame
              returnValue:(OEReturnInfo_t *)aReturnValue;
+ (NSString *)_argumentStringWithObjcType:(const char *)anObjcType
                                   offset:(void *)anOffset;
+ (void)_logMessage:(NSString *)aMessage;

@end

@implementation OEObject
// --------------------- Class Variable Declarations --------------------------

static BOOL continueAfterErrorC;
static BOOL ignoreCrashesC;
static BOOL isSetUpC;
static unsigned frameCountC;
static unsigned freedAddressC;
static unsigned functionArgCountC;
static unsigned maxArgDescriptionLengthC;
static NSMutableDictionary *objcTypeEncodingsC;
#ifndef WIN32
static NSMutableDictionary *signalMessagesC;
#endif WIN32

// ----------------------- Class Method Definitions ---------------------------

+ (void)initialize
{
    if (self == [OEObject class])
    {
        continueAfterErrorC = NO;
        ignoreCrashesC = NO;
        isSetUpC = NO;
        freedAddressC = 0;
        objcTypeEncodingsC = nil;
    #ifndef WIN32
        signalMessagesC = nil;
    #endif WIN32
    }
}

// Pose as the NSObject class.
+ (void)setup
{
    if (!isSetUpC)
    {
        NSUserDefaults *userDefaultsL = [NSUserDefaults standardUserDefaults];
        NSNumber *functionArgCountL = [userDefaultsL
            objectForKey:functionArgCountDefaultsNameG];
        NSNumber *frameCountL = [userDefaultsL
            objectForKey:frameCountDefaultsNameG];
        NSNumber *maxArgDescriptionLengthL = [userDefaultsL
            objectForKey:maxArgDescriptionLengthDefaultsNameG];

        if (functionArgCountL == nil)
        {
            functionArgCountC = functionArgCountG;
        }
        else
        {
            functionArgCountC = [functionArgCountL unsignedIntValue];
        }
        if (frameCountL == nil)
        {
            frameCountC = frameCountG;
        }
        else
        {
            frameCountC = [frameCountL unsignedIntValue];
        }
        if (maxArgDescriptionLengthL == nil)
        {
            maxArgDescriptionLengthC = maxArgDescriptionLengthG;
        }
        else
        {
            maxArgDescriptionLengthC = [maxArgDescriptionLengthL
                unsignedIntValue];
        }
        [self poseAsClass:[NSObject class]];
        _originalError = _error;
        _error = _objectError;
    #ifndef WIN32
        signalMessagesC = [[NSMutableDictionary alloc]
            initWithCapacity:sizeof(OESignals) / sizeof(OESignalItem_t)];
        [self _setSignalHandler:handle_signal];
    #else WIN32
        SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)filter);
    #endif WIN32
        isSetUpC = YES;
    }
}

+ (void)setContinueAfterError:(BOOL)aFlag
{
    continueAfterErrorC = aFlag;
}

+ (void)resumeHandlingCrashes
{
#ifndef WIN32
    [self _setSignalHandler:handle_signal];
#endif WIN32
    ignoreCrashesC = NO;
}

+ (void)stopHandlingCrashes
{
#ifndef WIN32
    [self _setSignalHandler:(void (*)())SIG_DFL];
#endif WIN32
    ignoreCrashesC = YES;
}

+ (NSString *)printBacktrace:(NSString *)aMessage
           skippedFrameCount:(unsigned)aCount
{
    // Counter for number of frames printed
    unsigned frameCountL;
    unsigned maxFrameCountL;

    // Pointer to current frame
    void *framePointerL;

    aMessage = [self _prepareToPrintBacktrace:aMessage];
    maxFrameCountL = frameCountC + aCount;

    // Skip aCount OEObject frames.  Assume that a whole lotta frames means
    // either (a) we're trashed or (b) we're in a recursive deathtrap.  In the
    // latter case, we've probably got enough info to see a whole cycle.
    for (frameCountL = aCount; frameCountL < maxFrameCountL; frameCountL++)
    {
        // Set framePointer to current frame.
        framePointerL = NSFrameAddress(frameCountL);

        if (framePointerL == NULL) break;

        aMessage = [self _printFrame:framePointerL withMessage:aMessage];
    }
    freedAddressC = 0;
    return aMessage;
}

#ifdef WIN32
+ (NSString *)printBacktrace:(NSString *)aMessage address:(void *)aFramePointer
{
    // Counter for number of frames printed
    unsigned frameCountL;

    aMessage = [self _prepareToPrintBacktrace:aMessage];

    for (frameCountL = 0; frameCountL < frameCountC; frameCountL++)
    {
        if (aFramePointer == NULL) break;

        aMessage = [self _printFrame:aFramePointer withMessage:aMessage];

        // Increment void * aFramePointer!  Don't try this at home, kids.
        aFramePointer = *(void **)aFramePointer;
    }
    freedAddressC = 0;
    return aMessage;
}
#endif WIN32

+ (NSString *)crashException
{
    return crashExceptionG;
}

+ (NSString *)functionArgCountDefaultsName
{
    return functionArgCountDefaultsNameG;
}

+ (NSString *)frameCountDefaultsName
{
    return frameCountDefaultsNameG;
}

+ (NSString *)maxArgDescriptionLengthDefaultsName
{
    return maxArgDescriptionLengthDefaultsNameG;
}

// ---------------- Overridden Instance Method Definitions --------------------

- (void)doesNotRecognizeSelector:(SEL)aSelector
{
    NS_DURING
        [super doesNotRecognizeSelector:aSelector];
    NS_HANDLER
    #ifdef WIN32
        unsigned skippedFrameCountL = 3;
    #else WIN32
        unsigned skippedFrameCountL = 4;
    #endif WIN32
        Class selfClassL = [self class];

        [selfClassL _logMessage:[selfClassL printBacktrace:endOfLineG
            skippedFrameCount:skippedFrameCountL]];
    NS_ENDHANDLER
}

// -------------------- New Instance Method Definitions -----------------------

// ----------------- Delegate Instance Method Definitions ---------------------

@end

@implementation OEObject(Private)
// ---------------------- Private Method Definitions --------------------------

#ifndef WIN32
+ (void)_setSignalHandler:(void (*)())aHandler
{
    OESignalItem_t *signalItemL;

    for (signalItemL = OESignals + (sizeof(OESignals) / sizeof(OESignalItem_t))
         - 1; 0 < signalItemL->number; signalItemL--)
    {
        if (signalItemL->isOn)
        {
            [signalMessagesC setObject:signalItemL->message
                                forKey:[NSNumber
                numberWithInt:signalItemL->number]];
            signal(signalItemL->number, aHandler);
        }
    }
}
#endif WIN32

+ (NSString *)_prepareToPrintBacktrace:(NSString *)aMessage
{
    NSScanner *scannerL = [NSScanner scannerWithString:aMessage];

    // Identify message to freed object error message and extract the address
    // of the freed object so that it can be used to identify the object in the
    // stack frame report.
    if ([scannerL scanUpToString:freedObjectMessageG intoString:NULL] &&
        [scannerL scanString:freedObjectMessageG intoString:NULL])
    {
        [scannerL setScanLocation:[scannerL scanLocation] + 1];
        [scannerL scanHexInt:&freedAddressC];
    }
    aMessage = [aMessage
        stringByAppendingFormat:@"Attempting stack frame backtrace:%@",
        endOfLineG];

    // Try to avoid re-entry problems.
    [self stopHandlingCrashes];
    return aMessage;
}

+ (NSString *)_printFrame:(void *)aFramePointer
              withMessage:(NSString *)aMessage
{
    // This cast may be bogus, but it shuts up the compiler.
    SEL possibleSelectorL = [self
        _selectorWithFrame:(void *)((unsigned)aFramePointer +
                                    argStartPositionG)
               returnValue:NULL];

    if (sel_isMapped(possibleSelectorL))
    {
        aMessage = [aMessage stringByAppendingString:[self
           _methodMessageWithFP:aFramePointer selector:possibleSelectorL]];
    }
    else
    {
        aMessage = [aMessage stringByAppendingString:[self
            _functionMessageFromFP:aFramePointer]];
    }
    return aMessage;
}

+ (NSString *)_functionMessageFromFP:(void *)aFramePointer
{
    void *argStartL;
    unsigned argNumL;	// Index into arguments;
    NSString *messageL;

    // This cast may be bogus, but it shuts up the compiler.
    argStartL = (void *)((unsigned)aFramePointer + argStartPositionG);

    messageL = @"function(";
    for (argNumL = 0; argNumL < functionArgCountC; argNumL++)
    {
        NSString *punctuationL;

        if (argNumL != 0)
        {
            punctuationL = @", ";
        }
        else
        {
            punctuationL = @"";
        }
        messageL = [messageL stringByAppendingFormat:@"%@%#08x", punctuationL,
            *((unsigned*)argStartL + argNumL)];
    }
    return [messageL stringByAppendingFormat:@")%@", endOfLineG];
}

+ (NSString *)_methodMessageWithFP:(void *)aFramePointer
                          selector:(SEL)aSelector
{
    NSString *messageL;

    // This cast may be bogus, but it shuts up the compiler.
    id objectL = [self _selfWithFrame:(void *)((unsigned)aFramePointer +
                                               argStartPositionG)
                          returnValue:NULL];

    if (freedAddressC == (int)objectL)
    {
        // Cause of crash is freed object, so no need to print method info.
        messageL = [NSString stringWithFormat:@"? [%@ %@]", freedObjectNameG,
            NSStringFromSelector(aSelector)];
    }
    else
    {
        char methodCharL;
        Method methodL;

        // Using runtime functions rather than Objective-C messages to return
        // the name and class is a little safer than depending on the class
        // method in case of the object being freed (this code should never be
        // executed in that case) or a custom root object not responding to the
        // class method.
        const char *classNameL = object_getClassName(objectL);
        Class classL = objc_lookUpClass(classNameL);

        if (objectL == classL)
        {
            methodCharL = '+';
            methodL = class_getClassMethod(classL, aSelector);
        }
        else
        {
            methodCharL = '-';
            methodL = class_getInstanceMethod(classL, aSelector);
        }
        messageL = [NSString stringWithFormat:@"%c [%s ", methodCharL,
            classNameL];

        if (methodL != NULL)
        {
            NSString *selectorWithArgsL;
            int argCountL = method_getNumberOfArguments(methodL);
            NSString *selectorStringL = NSStringFromSelector(aSelector);

            if (2 < argCountL)
            {
                unsigned argNumL;
                NSString *selectorComponentL;
                void *argStartL = (void *)((unsigned)aFramePointer +
                                           argStartPositionG);
                NSScanner *argScannerL = [NSScanner
                    scannerWithString:selectorStringL];

                selectorWithArgsL = nil;

                // Skip the first two args which are self and _cmd.
                for (argNumL = 2; argNumL < argCountL; argNumL++)
                {
                    const char *typeL;
                    int offsetL;

                    // Deal with case in which argument includes no
                    // description.
                    if (![argScannerL scanUpToString:@":"
                                          intoString:&selectorComponentL])
                    {
                        selectorComponentL = @"";
                    }
                    if (selectorWithArgsL != nil)
                    {
                        selectorWithArgsL = [selectorWithArgsL
                            stringByAppendingFormat:@" %@",
                            selectorComponentL];
                    }
                    else
                    {
                        selectorWithArgsL = selectorComponentL;
                    }
                    method_getArgumentInfo(methodL, argNumL, &typeL, &offsetL);
                    selectorWithArgsL = [selectorWithArgsL
                        stringByAppendingString:[self
                        _argumentStringWithObjcType:typeL
                        offset:(void *)((unsigned)argStartL + offsetL)]];
                    [argScannerL scanString:@":" intoString:NULL];
                }
            }
            else
            {
                selectorWithArgsL = selectorStringL;
            }
            messageL = [messageL stringByAppendingFormat:@"%@]",
                selectorWithArgsL];
        }
        else
        {
            messageL = [messageL
                stringByAppendingString:@" <unknown method>]"];
        }
    }
    return [messageL stringByAppendingString:endOfLineG];
}

// The following encapsulates how to find self and selectors on the various
// architectures as best I know how.
+ (BOOL)_isReturnStructInRegistersWithSize:(int)aSize
{
#if defined(sparc) || defined(__alpha__)
    // Sparc, PDO (alpha, sparc) never put struct in reg on return
    return NO;
#else
    // these are heuristics (i.e. wrong in many cases)
    // nextstep hppa
    #if defined(hppa) && !defined(hpux)
        return (BOOL)(aSize == 1 || aSize == 2 || aSize == 4);
    #elif defined(m68k) || defined(i386) || defined(hppa)
        return (BOOL)(aSize == 1 || aSize == 2 || aSize == 4 || aSize == 8);
    #else
        #error dont know how to compute structure-in-registers decision
    #endif
#endif
}

+ (id)_selfWithFrame:(void *)anArgumentFrame
         returnValue:(OEReturnInfo_t *)aReturnValue
{
    if (aReturnValue == NULL)
    {
        // Take our best guess.
        return ((id *)anArgumentFrame)[selfIndexG];
    }
#if defined(__alpha__)
    {
        int offsetL;

        if (aReturnValue->type == NSObjCStructType)
        {
            offsetL = 1;
        }
        else
        {
            offsetL = 0;
        }
        return ((id *)anArgumentFrame)[selfIndexG + offsetL];
    }
#elif defined(WIN32)
    {
        // If the return type is a struct, and the struct is NOT passed in
        // registers, then the address of the struct return is on the stack
        // in the 0th slot.  Therefore, all other normal parameters are shifted
        // by one position.
        if ((aReturnValue->type == NSObjCStructType)
            && ![self _isReturnStructInRegistersWithSize:aReturnValue->size])
        {
            return ((id *)anArgumentFrame)[selfIndexG + 1];
        }
        // if it's not a struct return OR if the struct is in registers
        // (i.e., it's less than 8 bytes) then just return the selfIndex.
        return ((id *)anArgumentFrame)[selfIndexG];
    }
#else // ! __alpha__
    return ((id *)anArgumentFrame)[selfIndexG];
#endif
}

+ (SEL)_selectorWithFrame:(void *)anArgumentFrame
              returnValue:(OEReturnInfo_t *)aReturnValue
{
    if (aReturnValue == NULL)
    {
        // Take our best guess.
        return ((SEL *)anArgumentFrame)[sELIndexG];
    }
#if defined(__alpha__)
    {
        int offsetL;

        if (aReturnValue->type == NSObjCStructType)
        {
            offsetL = 1;
        }
        else
        {
            offsetL = 0;
        }
        return ((SEL *)anArgumentFrame)[sELIndexG + offsetL];
    }
#elif defined(WIN32)
    {
        // If the return type is a struct, and the struct is NOT passed in
        // registers, then the address of the struct return is on the stack
        // in the 0th slot.  Therefore, all other normal parameters are shifted
        // by one position.
        if ((aReturnValue->type == NSObjCStructType)
            && ![self _isReturnStructInRegistersWithSize:aReturnValue->size])
        {
            return ((SEL *)anArgumentFrame)[sELIndexG + 1];
        }
        // if it's not a struct return OR if the struct is in registers
        // (i.e., it's less than 8 bytes) then just return the sELIndex.
        return ((SEL *)anArgumentFrame)[sELIndexG];
    }
#else // ! __alpha__
    return ((SEL *)anArgumentFrame)[sELIndexG];
#endif
}

+ (NSString *)_argumentStringWithObjcType:(const char *)anObjcType
                                   offset:(void *)anOffset
{
    NSData *descriptionL;

    if (objcTypeEncodingsC == nil)
    {
        int indexL;
        int sizeL = sizeof(OEEncodings) / sizeof(OEObjcType_t);

        objcTypeEncodingsC = [[NSMutableDictionary alloc]
            initWithCapacity:sizeL];

        for (indexL = sizeL - 1; 0 <= indexL; indexL--)
        {
            OEObjcType_t encodingL = OEEncodings[indexL];
            NSNumber *charEncodingL = [NSNumber
                numberWithChar:encodingL.encoding];
            OEObjcTypeDesc_t objcTypeDescL = encodingL.description;

            descriptionL = [NSData dataWithBytes:(void *)&objcTypeDescL
                                          length:sizeof(OEObjcTypeDesc_t)];
            [objcTypeEncodingsC setObject:descriptionL
                                   forKey:charEncodingL];
        }
    }
    descriptionL = [objcTypeEncodingsC objectForKey:[NSNumber
        numberWithChar:anObjcType[0]]];

    if (descriptionL != nil)
    {
        OEObjcTypeDesc_t objcTypeDescL;
        NSString *formatL;

        [descriptionL getBytes:(void *)&objcTypeDescL
                        length:sizeof(OEObjcTypeDesc_t)];
        formatL = [@":(%@)" stringByAppendingString:objcTypeDescL.format];

        switch (anObjcType[0])
        {
            case _C_ID:
            {
                id objectL = *(id *)anOffset;

                if ([objectL respondsToSelector:@selector(description)])
                {
                    NSString *objectDescriptionL = [objectL description];

                    if (maxArgDescriptionLengthC < [objectDescriptionL length])
                    {
                        // Limit length of description to
                        // maxArgDescriptionLength.
                        objectDescriptionL = [objectDescriptionL
                            substringToIndex:maxArgDescriptionLengthC];
                    }
                    return [NSString stringWithFormat:formatL,
                        objcTypeDescL.name, objectDescriptionL];
                }
                else
                {
                    return [NSString stringWithFormat:@":(%@)%#08x",
                        objcTypeDescL.name, objectL];
                }
            }
            case _C_CHARPTR:
                // Limit length of description to maxArgDescriptionLength.
                return [NSString stringWithFormat:formatL, objcTypeDescL.name,
                    maxArgDescriptionLengthC, *(char **)anOffset];

            case _C_CHR:
            case _C_UCHR:
                return [NSString stringWithFormat:formatL, objcTypeDescL.name,
                    *(char *)anOffset];

            case _C_SHT:
            case _C_USHT:
                return [NSString stringWithFormat:formatL, objcTypeDescL.name,
                    *(short *)anOffset];

            case _C_LNG:
            case _C_ULNG:
                return [NSString stringWithFormat:formatL, objcTypeDescL.name,
                    *(long *)anOffset];

            case _C_FLT:
                return [NSString stringWithFormat:formatL, objcTypeDescL.name,
                    *(float *)anOffset];

            case _C_DBL:
                return [NSString stringWithFormat:formatL, objcTypeDescL.name,
                    *(double *)anOffset];

            default:
                return [NSString stringWithFormat:formatL, objcTypeDescL.name,
                    *(int *)anOffset];
        }
    }
    else
    {
        // This cast may be bogus, but it shuts up the compiler.
        return [NSString stringWithFormat:@" :(<unknown type>)%#08x",
            *(int *)anOffset];
    }
}

+ (void)_logMessage:(NSString *)aMessage
{
    // If continuing after the error, leave the method of reporting the stack
    // backtrace to a process-specific exception handler.
    if (continueAfterErrorC)
    {
        [self resumeHandlingCrashes];

        // Don't add an AppKit dependency here by opening Alert panel to warn
        // the user.
        [NSException raise:crashExceptionG format:aMessage];
    }
    // Don't continue, so log the stack backtrace.
    else
    {
        NSLog(aMessage);
        exit(1);
    }
}

@end

// ------------------------- Function Definitions -----------------------------

#ifndef WIN32
static void handle_signal(int aSignal)
{
    NSString *messageL;
    NSString *signalMessageL = [signalMessagesC objectForKey:[NSNumber
        numberWithInt:aSignal]];

    if (signalMessageL != nil)
    {
        messageL = [NSString stringWithFormat:@"Caught signal #%d: \"%@\"%@",
            aSignal, signalMessageL, endOfLineG];
    }
    else
    {
        messageL = [NSString
            stringWithFormat:@"Caught unrecognized signal #%d%@", aSignal,
            endOfLineG];
    }
    [OEObject _logMessage:[OEObject printBacktrace:messageL
                                 skippedFrameCount:2]];
}
#endif WIN32

static void _objectError(Object *anObject, const char *aFormat,
                         va_list theArguments)
{
    char bufferL[BUFSIZ];
    NSString *messageL;

    vsprintf(bufferL, aFormat, theArguments);
    messageL = [NSString stringWithFormat:@"%s%@", bufferL, endOfLineG]; 
    _error = _originalError;
    messageL = [OEObject printBacktrace:messageL skippedFrameCount:3];
    _error = _objectError;
    [OEObject _logMessage:messageL];
}

#ifdef WIN32
static int filter(LPEXCEPTION_POINTERS theInfo)
{
    NSString *messageL;
    NSString *exceptionMessageL;
    int codeL = theInfo->ExceptionRecord->ExceptionCode;

    switch (codeL)
    {
	case STATUS_ABANDONED_WAIT_0:
            exceptionMessageL = @"STATUS_ABANDONED_WAIT_0";
            break;

        case STATUS_USER_APC:
            exceptionMessageL = @"STATUS_USER_APC";
            break;

        case STATUS_TIMEOUT:
            exceptionMessageL = @"STATUS_TIMEOUT";
            break;

        case STATUS_PENDING:
            exceptionMessageL = @"STATUS_PENDING";
            break;

        case STATUS_SEGMENT_NOTIFICATION:
            exceptionMessageL = @"STATUS_SEGMENT_NOTIFICATION";
            break;

        case STATUS_GUARD_PAGE_VIOLATION:
            exceptionMessageL = @"STATUS_GUARD_PAGE_VIOLATION";
            break;

        case STATUS_DATATYPE_MISALIGNMENT:
            exceptionMessageL = @"STATUS_DATATYPE_MISALIGNMENT";
            break;

        case STATUS_BREAKPOINT:
            exceptionMessageL = @"STATUS_BREAKPOINT";
            break;

        case STATUS_SINGLE_STEP:
            exceptionMessageL = @"STATUS_SINGLE_STEP";
            break;

        case STATUS_ACCESS_VIOLATION:
            exceptionMessageL = [NSString stringWithFormat:@"Access Violation on %@ at address %#08x",
                theInfo->ExceptionRecord->ExceptionInformation[0] ? @"write" :
                @"read", theInfo->ExceptionRecord->ExceptionInformation[1]];
            break;

        case STATUS_IN_PAGE_ERROR:
            exceptionMessageL = @"STATUS_IN_PAGE_ERROR";
            break;

        case STATUS_INVALID_HANDLE:
            exceptionMessageL = @"STATUS_INVALID_HANDLE";
            break;

        case STATUS_NO_MEMORY:
            exceptionMessageL = @"STATUS_NO_MEMORY";
            break;

        case STATUS_ILLEGAL_INSTRUCTION:
            exceptionMessageL = @"STATUS_ILLEGAL_INSTRUCTION";
            break;

        case STATUS_NONCONTINUABLE_EXCEPTION:
            exceptionMessageL = @"STATUS_NONCONTINUABLE_EXCEPTION";
            break;

        case STATUS_INVALID_DISPOSITION:
            exceptionMessageL = @"STATUS_INVALID_DISPOSITION";
            break;

        case STATUS_ARRAY_BOUNDS_EXCEEDED:
            exceptionMessageL = @"STATUS_ARRAY_BOUNDS_EXCEEDED";
            break;

        case STATUS_FLOAT_DENORMAL_OPERAND:
            exceptionMessageL = @"STATUS_FLOAT_DENORMAL_OPERAND";
            break;

        case STATUS_FLOAT_DIVIDE_BY_ZERO:
            exceptionMessageL = @"STATUS_FLOAT_DIVIDE_BY_ZERO";
            break;

        case STATUS_FLOAT_INEXACT_RESULT:
            exceptionMessageL = @"STATUS_FLOAT_INEXACT_RESULT";
            break;

        case STATUS_FLOAT_INVALID_OPERATION:
            exceptionMessageL = @"STATUS_FLOAT_INVALID_OPERATION";
            break;

        case STATUS_FLOAT_OVERFLOW:
            exceptionMessageL = @"STATUS_FLOAT_OVERFLOW";
            break;

        case STATUS_FLOAT_STACK_CHECK:
            exceptionMessageL = @"STATUS_FLOAT_STACK_CHECK";
            break;

        case STATUS_FLOAT_UNDERFLOW:
            exceptionMessageL = @"STATUS_FLOAT_UNDERFLOW";
            break;

        case STATUS_INTEGER_DIVIDE_BY_ZERO:
            exceptionMessageL = @"STATUS_INTEGER_DIVIDE_BY_ZERO";
            break;

        case STATUS_INTEGER_OVERFLOW:
            exceptionMessageL = @"STATUS_INTEGER_OVERFLOW";
            break;

        case STATUS_PRIVILEGED_INSTRUCTION:
            exceptionMessageL = @"STATUS_PRIVILEGED_INSTRUCTION";
            break;

        case STATUS_STACK_OVERFLOW:
            exceptionMessageL = @"STATUS_STACK_OVERFLOW";
            break;

        case STATUS_CONTROL_C_EXIT:
            exceptionMessageL = @"STATUS_CONTROL_C_EXIT";
            break;

        default:
            exceptionMessageL = nil;
    }
    if (exceptionMessageL != nil)
    {
        messageL = [NSString stringWithFormat:@"Caught WIN32 exception %#08x: \"%@\"%@",
            codeL, exceptionMessageL, endOfLineG];
    }
    else
    {
        messageL = [NSString
            stringWithFormat:@"Caught unrecognized WIN32 exception %#08x",
            codeL, endOfLineG];
    }
    if (theInfo->ContextRecord->ContextFlags & CONTEXT_CONTROL)
    {
        [OEObject _logMessage:[OEObject printBacktrace:messageL
           address:(void *)theInfo->ContextRecord->Ebp]];
    }
    else
    {
        // Don't know how many frames to skip, so don't skip any.
        [OEObject _logMessage:[OEObject printBacktrace:messageL
                                     skippedFrameCount:0]];
    }
    return EXCEPTION_CONTINUE_SEARCH;
}
#endif WIN32
