ftp.nice.ch/pub/next/connectivity/mail/apps/MailCrashTrap.1.0.NIHS.bs.tar.gz#/MailCrashTrap-1.0/Source/TRHCrashTrap.subproj/TRHCrashTrap.m

This is TRHCrashTrap.m in view mode; [Download] [Up]

/*+++
 *  title:      TRHCrashTrap.m
 *  abstract:   Implementation of of TRHCrashTrap class.
 *  author:     various
 *  created:    Dec 1998, by Tom Hageman <tom@basil.icce.rug.nl>
 *  modified:   Jan 1999
 *  copyleft:   (see below)
 *  description:
 *
 *    [adapted for use in Mail.app by means of MailCrashTrap plugin]
 * 
 *    XXX TODO:
 *	-	make thread-safe;
 *	v	add delegate to handle user-interaction;
 *	v	gracefully handle nested crash;
 *	v	throw & catch dedicated exception instead of abusing abortModal;
 *	-	watch out for character buffer overflows;
 *	-	port stack backtrace to hppa, sparc;
 *	-	autodoc.
 *
 *    Based on:
//
// HKCrashTrap   version 1.0
//
// Based on ObjectError
// Original class by:
// Bill Bumgarner, Andrew Stone, Mike Morton, and Julie Zelenski
//
// Modifications by:
// Ivo Rothschild (ivo@hasc.ca)
//
// A class that [poses as object and] does crash-reporting.
// *** This version does _not_ pose as Object anymore, it turns out not
// *** to be necessary.
// Catches terminating signals (ie seg faults, bus errors)
// and fatal Objective-C runtime errors and writes a backtrace 
// out to the console using some shady hacks.  This could be modified
// to write backtrace to a file, mail message, etc... if desired.
//
//
// You may freely copy, distribute, and reuse the code in this class.
//
// 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.
//
 *---*/

static const char * const RCSid = ((void)&RCSid,
	"@(#)TRHCrashTrap.m,v 1.15 1999/01/10 17:05:20 tom Exp");
#define RCS_TRHCrashTrap_ID

#import "TRHCrashTrap.h"
#import "objcTypeDecode.h"
#import <appkit/appkit.h>
#import <string.h>
#import <stdio.h>
#import <signal.h>
#import <ctype.h>
#import <objc/objc-runtime.h>
#import <defaults/defaults.h>


#ifdef POSE_AS_OBJECT
/* According to the docs, -[Object error] calls _error,
   which we already handle so this poser should not be necessary.
   And indeed the docs are right. */
@interface _TRHCrashTrapObjectPoser : Object
+ (void)_crashTrapSetup;
- error:(const char *)aString, ...;
+ error:(const char *)aString, ...;
@end
#endif


static const char *signal_descriptions[NSIG];
static BOOL should_handle_signals[NSIG];

static void init_signal_tables(void);
	/* fills in the tables above. */

static void (*org_signal_handlers[NSIG])();

static BOOL ignoreCrashes;
static BOOL continueAfterError;
static int selfOffset, _cmdOffset;

static Class crashTrapHandlerClass;
static id delegate;


#define FREED_OBJECT_NAME  "<FreedObject>"


// Factory default values.

#define MAX_FRAMES	50			// Print at most this number of stack frames.
#define MAX_FUNCTION_ARGS	4	// Print at most this number of args to a function.
#define MAX_ARG_VAL_LENGTH	256	// limit length of printed argument values.

// User default names.

#define MAX_FRAMES_DEFAULT_NAME			"CrashTrap_MaxFrameCount"
#define MAX_FUNCTION_ARGS_DEFAULT_NAME	"CrashTrap_FunctionArgCount"
#define MAX_ARG_VAL_LENGTH_DEFAULT_NAME	"CrashTrap_MaxArgDescriptionLength"
#define DUMP_BACKTRACE_DEFAULT_NAME		"CrashTrap_DumpBacktrace"

static unsigned maxFrames = MAX_FRAMES;
static unsigned maxFunctionArgs = MAX_FUNCTION_ARGS;
static unsigned maxArgValLength = MAX_ARG_VAL_LENGTH;
static BOOL dumpBacktrace = YES;



// XXX Should define this elsewhere -- indirection to force macro expansion.
#define STRINGIZE(X)	_STRINGIZE(X)
#define _STRINGIZE(X)	#X


//		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?
//
// XXX These assumptions are only valid for m68k and i386;
// XXX hppa, sparc and ppc are _not_ yet supported.

#if (__m68k__ || __i386__)
# define FRAME_ARG_OFFSET	8
#endif

#if 0 // NOTYET
#if (__hppa__)
# define FRAME_ARG_OFFSET	0 // really?
# define INITIAL_FRAME(first_arg_address) __asm__("%%r4") XXX Does not work with 3.3 cc (gcc 2.5.8)
# define ARG_DIRECTION	(-1)
#endif

#if (__sparc__)
# define FRAME_ARG_OFFSET	8 // really?
# define INITIAL_FRAME(first_arg_address) __asm__("%%fp") XXX see above
#endif
#endif // NOTYET

#ifndef FRAME_ARG_OFFSET
# warning XXX stack backtrace not yet implemented for this architecture.
# define FRAME_ARG_OFFSET	0
# define INITIAL_FRAME(first_arg_addr)	= NULL
#endif

#ifndef FRAME_NEXT_OFFSET
# define FRAME_NEXT_OFFSET	0
#endif

#ifndef FRAME_RETURN_OFFSET
# define FRAME_RETURN_OFFSET	4
#endif

#ifndef ARG_DIRECTION
# define ARG_DIRECTION	(+1)
#endif

#ifndef INITIAL_FRAME
# define INITIAL_FRAME(first_arg_address) \
	= ((void*)((char*)(first_arg_address) - FRAME_ARG_OFFSET))
#endif

#ifndef NEXT_FRAME
# define NEXT_FRAME(fp) \
	(*(void**)((char*)(fp) + FRAME_NEXT_OFFSET))
#endif

#ifndef FRAME_ARG_START
# define FRAME_ARG_START(fp) \
	((void *)((char *)(fp) + FRAME_ARG_OFFSET))
#endif

#ifndef FRAME_RETURN_ADDRESS
# define FRAME_RETURN_ADDRESS(fp) \
	(*(void**)((char*)(fp) + FRAME_RETURN_OFFSET))
#endif


// in OPENSTEP 4.1 and later we can use NSFrameAddress() instead.
static void *FramePointer(unsigned frame)
{
	register void *fp INITIAL_FRAME(&frame);

	// Unwind fp the requested number of frames
	// (+ 1 additional to compensate for self)
	do
	{
		if (fp == NULL) break;
		fp = NEXT_FRAME(fp);
	}
	while (frame-- > 0);

	return fp;
}


typedef void (*ObjcErrorHandler)(Object *, const char *, va_list);

static ObjcErrorHandler _originalError;

static ObjcErrorHandler SetObjcErrorHandler(ObjcErrorHandler newHandler)
{
	ObjcErrorHandler oldHandler = _error;
	_error = newHandler;
	return oldHandler;
}


/* error/signal handler callback functions. */

static void handle_objc_error(Object *anObject, const char *format, va_list ap)
{
	[crashTrapHandlerClass handleObjcError:anObject format:format args:ap];
}

static void handle_signal(int signal)
{
	[crashTrapHandlerClass handleSignal:signal];
}


static void error_reporter(NXHandler *errorState)
{
	switch (errorState->code)
	{
	 case CrashTrap_Crash:
		// Already reported the error, so nothing to do here...
		break;

	 case CrashTrap_Continue:
#if DEBUG
		NXLogError("CrashTrap: ...continuing.");
#endif
		break;

	 case CrashTrap_RecursiveCrash:
		NXLogError("CrashTrap: INTERNAL ERROR: uncaught recursive crash.");
		break;

	 default:
		NXLogError("CrashTrap: unknown error code %ld\n", errorState->code);
	}
}


// User defaults helper functions.

static unsigned getUnsignedDefaultValue(const char *owner, const char *name, unsigned defaultValue)
{
	const char *stringValue;
	unsigned result = defaultValue;

	NXUpdateDefault(owner, name);	// Make sure value is up-to-date.
	stringValue = NXGetDefaultValue(owner, name);
	if (stringValue && isdigit(*stringValue))
	{
		sscanf(stringValue, "%u", &result);
	}
	return result;
}

static BOOL getBoolDefaultValue(const char *owner, const char *name, BOOL defaultValue)
{
	const char *stringValue;
	BOOL result = defaultValue;

	NXUpdateDefault(owner, name);	// Make sure value is up-to-date.
	stringValue = NXGetDefaultValue(owner, name);
	if (stringValue)
	{
		if (strcasecmp("YES", stringValue) == 0) result = YES;
		else if (strcasecmp("NO", stringValue) == 0) result = NO;
	}
	return result;
}

static const char *appName()
{
	const char *result = NULL;

	if (NXApp) result = [NXApp appName];

	if (result == NULL)
	{
		/* shady hack in case NXApp is not yet initialized. */
		result = strrchr(NXArgv[0], '/');
		if (result) ++result;
	}
	return result;
}

@implementation TRHCrashTrap

+ initialize
{
	if (crashTrapHandlerClass == Nil)
	{
		// Determine offsets of hidden self, _cmd arguments.
		Method m = class_getClassMethod(self, _cmd);
		char *typeDummy;

		method_getArgumentInfo(m, 0, &typeDummy, &selfOffset);
		method_getArgumentInfo(m, 1, &typeDummy, &_cmdOffset);

		init_signal_tables();

		NXRegisterErrorReporter(CRASHTRAP_ERROR_BASE,
								CRASHTRAP_ERROR_BASE + CRASHTRAP_ERROR_RANGE -1,
								error_reporter);

		[self setHandlerClass:[TRHCrashTrap class]];
	}
	return self;
}

+ (void)setup
{
	[self setHandlerClass:[self class]];

	_originalError = SetObjcErrorHandler(handle_objc_error);

	[self setContinueAfterError:NO];
	[self setDefaultValues];
	[self resumeHandlingCrashes];

#ifdef POSE_AS_OBJECT
	[_TRHCrashTrapObjectPoser _crashTrapSetup];
#endif
}

+ (void)setDefaultValues
{
	static const NXDefaultsVector defaults = {
		{ MAX_FRAMES_DEFAULT_NAME,         STRINGIZE(MAX_FRAMES) },
		{ MAX_FUNCTION_ARGS_DEFAULT_NAME,  STRINGIZE(MAX_FUNCTION_ARGS) },
		{ MAX_ARG_VAL_LENGTH_DEFAULT_NAME, STRINGIZE(MAX_ARG_VAL_LENGTH) },
		{ DUMP_BACKTRACE_DEFAULT_NAME,     "YES" },
		{ NULL }
	};
	static BOOL initialized;
	const char *owner = appName();

	if (!initialized)
	{
		initialized = YES;
		NXRegisterDefaults(owner, defaults);
	}

	// Obtain default values.
	maxFrames = getUnsignedDefaultValue(owner, MAX_FRAMES_DEFAULT_NAME, MAX_FRAMES);
	maxFunctionArgs = getUnsignedDefaultValue(owner, MAX_FUNCTION_ARGS_DEFAULT_NAME, MAX_FUNCTION_ARGS);
	maxArgValLength = getUnsignedDefaultValue(owner, MAX_ARG_VAL_LENGTH_DEFAULT_NAME, MAX_ARG_VAL_LENGTH);
	dumpBacktrace = getBoolDefaultValue(owner, DUMP_BACKTRACE_DEFAULT_NAME, YES);
}

+ (void)setContinueAfterError:(BOOL)flag;
{
	continueAfterError = flag;
}

+ (BOOL)continueAfterError;
{
	return continueAfterError;
}

+ (void)setDelegate:aDelegate;
{
	delegate = aDelegate;

	if (delegate && [delegate respondsTo:@selector(crashTrap:shouldContinueAfterError:)])
	{
		[self setContinueAfterError:YES];
	}
}

+ delegate
{
	return delegate;
}

+ (Class)handlerClass
{
	return crashTrapHandlerClass;
}

+ (void)setHandlerClass:(Class)aClass
{
	crashTrapHandlerClass = aClass;
	// XXX should only allow self or subclasses of self?
}


+ (void)_setHandler:(void (*)())aHandler forSignal:(int)aSignal
{
	// XXX probably should use sigvec instead to preserve signal flags as well.
	void (*org_handler)() = signal(aSignal, ((aHandler == SIG_DFL) ?
											 org_signal_handlers[aSignal] :
											 aHandler));

	if (aHandler != SIG_DFL && org_handler != aHandler)
	{
		org_signal_handlers[aSignal] = org_handler;
	}
}


+ (BOOL)isHandlingSignal:(int)aSignal
{
	if (aSignal <= 0 || aSignal >= NSIG) return NO;

	return should_handle_signals[aSignal];
}

+ (void)setSignal:(int)aSignal handle:(BOOL)aValue
{
	if (aSignal <= 0 || aSignal >= NSIG) return;

	if (aValue) aValue = YES;	// normalize value.

	if (should_handle_signals[aSignal] != aValue)
	{
		should_handle_signals[aSignal] = aValue;
		if (!ignoreCrashes)
		{
			[self _setHandler:(aValue ? handle_signal : SIG_DFL) forSignal:aSignal];
		}
	}
}

+ (void)setSignalHandler:(void (*)())handler
{
	int sig;

	/* [TRH] semantics of setSignalHandler:SIG_DFL has changed slightly:
	   now it restores the situation that was in effect just before the
	   CrashTrap handler was installed. */
	for (sig = 1;  sig < NSIG;  sig++)
	{
		if (should_handle_signals[sig])
		{
			[self _setHandler:handler forSignal:sig];
		}
	}
}

+ (void)resumeHandlingCrashes
{
	[self setSignalHandler:handle_signal];
	ignoreCrashes = NO;
}

+ (void)stopHandlingCrashes
{
	[self setSignalHandler:SIG_DFL];
	ignoreCrashes = YES;
}

#define BACKTRACE_PREFIX_FORMAT	"#%-2d 0x%08lx in "

+ (void)printFunctionFromFP:(void *)framePointer at:(void *)address index:(int)index
{
	char line[1024];
	char *buffer = line;
	unsigned long *argStart;
	long argNum;	// Index into arguments;

	sprintf(buffer, BACKTRACE_PREFIX_FORMAT, index, (long)address);
	buffer += strlen(buffer);
	sprintf(buffer, "function (");
	buffer += strlen(buffer);

	argStart = FRAME_ARG_START(framePointer);

	for (argNum = 0;  argNum < maxFunctionArgs;  argNum++)
	{
		sprintf(buffer, "%s0x%08lx", ((argNum != 0) ? ", " : ""), 
				marg_getValue(argStart, selfOffset + argNum * sizeof(long) * ARG_DIRECTION, long));
		buffer += strlen(buffer);
	}
	strcpy(buffer, ")");
	[self logMessage:line];
}

+ (void)printMethodFromFP:(void *)framePointer at:(void *)address index:(int)index
{
	char line[BUFSIZ];
	char *buffer = line;
	SEL selector;
	const char *selName;
	id object, class, classIsa;
	char *argStart;
	Method m;
	BOOL isClassMethod;

  NX_DURING

	sprintf(buffer, BACKTRACE_PREFIX_FORMAT, index, (long)address);
	buffer += strlen(buffer);

	argStart = FRAME_ARG_START(framePointer);

	object = marg_getValue(argStart, selfOffset, id);
	selector = marg_getValue(argStart, _cmdOffset, SEL);

	selName = sel_getName(selector);
	if (selName == NULL) selName = "<invalid selector>";

	class = nil;
	classIsa = nil;
	object_getInstanceVariable(object, "isa", (void **)&class);
	if (class)
	{
		object_getInstanceVariable(class, "isa", (void **)&classIsa);
	}
	if (!classIsa)
	{
		// Freed object.
		// (in practice classes are never freed, so assume instance method.)
		sprintf(buffer, "%c[%s0x%06lx %s]", '-', FREED_OBJECT_NAME, (long)object, selName);
	}
	else
	{
		isClassMethod = IS_CLASS(object);

		m = (isClassMethod	? class_getClassMethod(class, selector)
						: class_getInstanceMethod(class, selector));
		if (m)
		{
			int argNum, numArgs, offset;
			char *type;

			*buffer++ = (isClassMethod?'+':'-');
			/* Get return type -- XXX assumption: Method types description
			   starts with return type. */
			*buffer++ = '(';
			decode_objc_encoded_type(buffer, sizeof(line) - (buffer - line),
									 m->method_types, 0);
			buffer += strlen(buffer);
			*buffer++ = ')';
			*buffer++ = '[';
			decode_objc_encoded_value_for_type(buffer, sizeof(line) - (buffer - line),
											   @encode(id), &object, DECODE_OBJC_VERBOSE);
			buffer += strlen(buffer);

			numArgs = method_getNumberOfArguments(m);
			argNum = 2; // Skip the first two args which are self and _cmd

			if (numArgs <= argNum)
			{
				sprintf(buffer, " %s", selName);
			}
			else
			{
				while (argNum < numArgs)
				{
					int argVerbosity = DECODE_OBJC_VERBOSE;
					const char *se = strchr(selName, ':');

					// intermix selector name parts and arguments.
					if (se == NULL)
					{
						sprintf(buffer, (*selName) ? " %s " : ", ", selName);
						selName += strlen(selName);
					}
					else
					{
						++se;
						sprintf(buffer, " %.*s", (int)(se - selName), selName);
						selName = se;
					}
					buffer += strlen(buffer);

					// argument type.
					method_getArgumentInfo(m, argNum, &type, &offset);
					*buffer++ = '(';
					decode_objc_encoded_type(buffer, sizeof(line) - (buffer - line), type, 0);
					buffer += strlen(buffer);
					*buffer++ = ')';

					// argument value.
					while (argVerbosity >= 0)
					{
						// Retry with less verbosity if recursive crash.
					  NX_DURING
						decode_objc_encoded_value_for_type(buffer, maxArgValLength, type,
														   marg_getRef(argStart, offset, void),
														   argVerbosity);
						argVerbosity = -1;
					  NX_HANDLER
						// If recursive crash, retry with less verbosity.
						if (argVerbosity & DECODE_OBJC_VERBOSE)
						{
							argVerbosity &= ~DECODE_OBJC_VERBOSE;
						}
						else
						{
							strcpy(buffer, "???");
							argVerbosity = -1;
						}
					  NX_ENDHANDLER
					}
					buffer += strlen(buffer);

					argNum++;
				}
			}
			strcat(buffer, "]");
		}
		else
		{
			*buffer++ = (isClassMethod?'+':'-');
			*buffer++ = '[';
			decode_objc_encoded_value_for_type(buffer, sizeof(line) - (buffer - line),
											   @encode(id), &object, DECODE_OBJC_VERBOSE);
			buffer += strlen(buffer);
			sprintf(buffer, " %s] <unknown method>", selName);
		}
	}
  NX_HANDLER
	static const char recursiveCrashMessage[] = " *** recursive crash ***";

	if (strlen(line) <= sizeof(line) - sizeof(recursiveCrashMessage))
	{
		strcat(line, recursiveCrashMessage);
	}
  NX_ENDHANDLER
	[self logMessage:line];
}

+ (void)dumpBacktrace
{
	return [self dumpBacktraceAtFrame:1];
}

+ (void)dumpBacktraceAtFrame:(unsigned)startFrameNumber
{
	unsigned int frameCount = 0;	// counter for number of frames printed
	void *returnAddress;
	void *framePointer;

	if (maxFrames <= 0) return;

	framePointer = FramePointer(startFrameNumber);

	if (framePointer == NULL)
	{
		if (FramePointer(0) == NULL)
		{
			[self logMessage:"*** stack frame Backtrace not yet supported for architecture "__ARCHITECTURE__", sorry..."];
		}
		else
		{
			[self logMessage:"*** stack frame Backtrace -- sorry, I'm lost!"];
		}
		return;
	}
	[self logMessage:"*** stack frame Backtrace follows:"];

	// Get initial return address.
	returnAddress = FRAME_RETURN_ADDRESS(framePointer);

	// 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 have
	// probably got enough info to see a whole cycle.

	// initial NEXT_FRAME to skip self.
	while (frameCount < maxFrames && (framePointer = NEXT_FRAME(framePointer)))
	{
		void *argStart = FRAME_ARG_START(framePointer);
		SEL sel = marg_getValue(argStart, _cmdOffset, SEL); 

		// If this frame is a method call we will have a valid 
		// selector as second argument.
		if (sel && sel_isMapped(sel))
		{
			[self printMethodFromFP:framePointer at:returnAddress index:frameCount];
		}
		else
		{
			[self printFunctionFromFP:framePointer at:returnAddress index:frameCount];
		}
		++frameCount;
		returnAddress = FRAME_RETURN_ADDRESS(framePointer);
	}
	if (framePointer) [self logMessage:"... (more frames follow, not listed)"];
}

+ (BOOL)handleError:(const char *)message
{
	return [self handleError:message atFrame:1];
}

+ (BOOL)handleError:(const char *)message atFrame:(unsigned)startFrameNumber
{
	if (!ignoreCrashes)
	{
		static BOOL crashing;

		if (!crashing)
		{
			crashing = YES;

			[self logStart];
			[self logMessage:message];
			if (dumpBacktrace) [self dumpBacktraceAtFrame:(startFrameNumber + 1)];
			[self logStop];

			if (continueAfterError && [self shouldContinue:message])
			{
#if DEBUG
				NXLogError("CrashTrap: trying to continue...");
#endif
				crashing = NO;

				NX_RAISE(CrashTrap_Continue, NULL, NULL);
				return YES;
			}
			[self stopHandlingCrashes];

			crashing = NO;
		}
		else
		{
#if DEBUG
			fprintf(stderr, "CrashTrap: recursive crash \"%s\"\n", message);
#endif
			NX_RAISE(CrashTrap_RecursiveCrash, NULL, NULL);
			return YES;
		}
	}
	return NO;
}

+ (BOOL)shouldContinue:(const char *)message
{
	/* XXX this introduces AppKit dependencies.  A better solution might be
	   to exlusively let the delegate handle it. */
	if (delegate && [delegate respondsTo:@selector(crashTrap:shouldContinueAfterError:)])
	{
		return [delegate crashTrap:self shouldContinueAfterError:message];
	}
	// XXX Localize?
	// XXX should preallocate this alert panel to be more crashproof?
	if (NXRunAlertPanel("Error Alert!",
						"%s\n\nAn internal error has occurred."
						"  Try to save work,"
						" then quit and restart the %s application.\n"
						"(see the console for details)",
						"Continue", "CRASH!", NULL, message, appName()) != NX_ALERTDEFAULT)
	{
		return NO;
	}
	return YES;
}

+ (void)handleObjcError:object format:(const char *)format args:(va_list)args
{
	char buf[BUFSIZ];

	sprintf(buf, "objc: <%s> ", object_getClassName(object));

	vsprintf(buf + strlen(buf), format, args);

	if ([self handleError:buf atFrame:3]) return;
	// skip self, invoking error handler function, and -error:.

	/* invoke original error handler. */
	_originalError(object, format, args);
}

+ (void)handleSignal:(int)sig
{
	const char *desc = NULL;
	char buf[256];

	// unblock this signal.
	unsigned long mask = sigblock(0) & ~sigmask(sig);
	sigsetmask(mask);

	if (0 < sig && sig < NSIG) desc = signal_descriptions[sig];
	if (desc == NULL) desc = "Unrecognized signal";

	sprintf(buf, "Caught signal #%d (%s)", sig, desc);

	if ([self handleError:buf atFrame:2]) return;
	// skip self, and signal handler.

	/* Here the original signal handler is in effect (or should be, at least.)
	   Re-raise signal to get original intended handling (or lack thereof). */
	kill(getpid(), sig);
}


// Logging.

+ (void)logStart {}

+ (void)logMessage:(const char *)message
{
	NXLogError("%s\n", message);
}

+ (void)logStop {}

@end // TRHCrashTrap


#ifdef POSE_AS_CLASS

@implementation _TRHCrashTrapObjectPoser

+ (void)_crashTrapSetup
{
	[self poseAs:[self superclass]];
}

- error:(const char *)aFormat, ...
{
	va_list ap;
	char buffer[BUFSIZ];
	
	va_start(ap, aFormat);
	vsprintf(buffer, aFormat, ap);
	va_end(ap);
	
	if ([[TRHCrashTrap handlerClass] handleError:buffer]) return self;

	return [super error:"%s", buffer];
}

+ error:(const char *)aFormat, ...
{
	va_list ap;
	char buffer[BUFSIZ];
	
	va_start(ap, aFormat);
	vsprintf(buffer, aFormat, ap);
	va_end(ap);
	
	if ([[TRHCrashTrap handlerClass] handleError:buffer]) return self;

	return [super error:"%s", buffer];
}

@end // _TRHCrashTrapObjectPoser

#endif


typedef struct _sig  {
	short number;
	BOOL isOn;
	const char *message;
} SignalItem;

#define ON		1
#define OFF		0

static const SignalItem signals[] = {
	{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"},
	{0},
};

static void init_signal_tables()
{
	const SignalItem *cur;

	for (cur = signals; cur->number; cur++)
	{
		signal_descriptions[cur->number] = cur->message;
		should_handle_signals[cur->number] = cur->isOn;
	}
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.