/* 
 * ObjectError
 *
 * You may freely copy, distribute and reuse the code in this example.
 * This code is provided AS IS without warranty of any kind, expressed 
 * or implied, as to its fitness for any particular use.
 *
 * Copyright 1995 Ralph Zazula (rzazula@next.com).  All Rights Reserved.
 *
 * Based on the original ObjectError by Julie Zelenski
 *
 */

#import "ObjectError.h"
#import <appkit/nextstd.h>
#import <sys/signal.h>
#import <strings.h>
#import <stdio.h>

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

typedef struct _type {
	char encoding;
	char *format;
	char *name;
} ObjcType;

extern SignalItem signals[];
extern ObjcType encodings[];

@implementation ObjectError

static void handle_signal (int signal);
static BOOL ignoreCrashes;

+ setup
{
	[self poseAs:[Object class]];
	[self setSignalHandler:handle_signal];
	ignoreCrashes = NO;
	return self;
}

+ setSignalHandler:(void(*)())handler
{
	SignalItem *cur;
	
	for(cur = signals; cur->number; cur++) {
		if(cur->isOn) {
			signal(cur->number, handler);
		}
	}
	return self;
}

+ resumeHandlingCrashes
{
	[self setSignalHandler:handle_signal];
	ignoreCrashes = NO;
	return self;
}

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

#define MAX_FUNCTION_ARGS 4

+ printFunctionFromFP:(void *)framePointer
{
	char buffer[256];
	char line[1024];
	void *argStart;
	long argNum;
	
	sprintf(line, "function (");
	argStart = framePointer + 8;
	for(argNum = 0; argNum < MAX_FUNCTION_ARGS; argNum++) {
		sprintf(buffer, "%s0x%06lx", (argNum != 0 ? ", " : ""),
			*(((long *)argStart) + argNum));
		strcat(line, buffer);
	}
	strcat(line, ")");
	NXLogError("%s\n",line);
	return self;
}

#define IS_CLASS(object) ([object class] == object)

+ printMethodFromFP:(void *)framePointer
{
	char buffer[256];
	char line[1024];
	SEL selector;
	id object;
	Method m;
	BOOL isClassMethod;
		
	object = *(id *)(framePointer + 8);
	selector = *(SEL *)(framePointer + 12);
	isClassMethod = IS_CLASS(object);
	
	sprintf(line, "%c[%s %s",(isClassMethod ? '+' : '-'),
		object_getClassName(object), sel_getName(selector));
		
	m = (isClassMethod ? class_getClassMethod([object class], selector) :
		class_getInstanceMethod([object class], selector));
		
	if(m) {
		void *argStart = (framePointer + 8);
		int argNum, numArgs, offset;
		char *type;
		
		numArgs = method_getNumberOfArguments(m);
		argNum = 2;
		while(argNum < numArgs) {
			ObjcType *cur;
			
			method_getArgumentInfo(m, argNum, &type, &offset);
			for(cur = encodings; cur->encoding; cur++) {
				if(cur->encoding == type[0]) {
					sprintf(buffer, " :(%s)", cur->name);
					strcat(line,buffer);
					sprintf(buffer,cur->format,*(long*)(argStart+offset));
					strcat(line,buffer);
					break;
				}
			}
			argNum++;
		}
	} else {
		strcat(line, "  Unknown Method");
	}
	strcat(line, "]");
	NXLogError("%s\n",line); 
	return self;
}

#define MAX_FRAMES 50

+ printBacktrace
{
	void *framePointer;
	unsigned int frameCount;
	
	[self stopHandlingCrashes];
	
#if DEBUG
	framePointer = ((void *) &self) - 8;
	frameCount = 0;
	
	while(frameCount < MAX_FRAMES && framePointer) {
		if(sel_isMapped(*(SEL *)(framePointer + 12))) {
			[self printMethodFromFP:framePointer];
		} else {
			[self printFunctionFromFP:framePointer];
		}
		
		framePointer = (void *)*(long *)framePointer;
	}
#endif
		
	return self;
}

+ dumpBacktrace:(const char *)message
{
	NXLogError("%s: %s\n", [self name], message);
	[self printBacktrace];
	return self;
}

static void handle_signal(int signal)
{
	const char *msg = NULL;
	char buf[256];
	SignalItem *cur;
	
	msg = "Unrecognized signal";
	for(cur = signals; cur->number; cur++) {
		if(cur->number == signal) {
			msg = cur->message;
			break;
		}
	}
	sprintf(buf, "Caught signal #%d: \"%s\"",signal, msg);
	[ObjectError dumpBacktrace:buf];
}

- error:(const char *)aString, ...;
{
	va_list ap;
	char buffer[1024];
	
	va_start(ap, aString);
	vsprintf(buffer, aString, ap);
	va_end(ap);
	if(!ignoreCrashes) {
		[[self class] dumpBacktrace:buffer];
		[[self class] resumeHandlingCrashes];
	} else {
		return [super error:buffer];
	}
	return self;
}

+ error:(const char *)aString, ...;
{
	va_list ap;
	char buffer[1024];
	
	va_start(ap, aString);
	vsprintf(buffer, aString, ap);
	va_end(ap);
	if(!ignoreCrashes) {
		[[self class] dumpBacktrace:buffer];
		[[self class] resumeHandlingCrashes];
	} else {
		return [super error:buffer];
	}
	return self;
}

#define ON 1
#define OFF 0

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 pipe with no reader"},
	{ 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 has changed"},
	{ SIGTTIN	, OFF,	"background read attempted from control terminal"},
	{ SIGTTOU	, OFF,	"background write attempted to control terminal"},
	{ SIGIO		, OFF,	"i/o is impossible on descriptor"},
	{ SIGXCPU	, OFF, 	"cpu time limit exceeded"},
	{ SIGXFSZ	, OFF,	"file size limit exceeded"},
	{ SIGVTALRM	, OFF,	"virtual time alarm"},
	{ SIGPROF	, OFF,	"profiling timer alarm"},
	{ SIGWINCH	, OFF,	"window size changed"},
	{ SIGUSR1	, OFF,	"user defined #1"},
	{ SIGUSR2	, OFF,	"user defined #2"},
	{0},
};

ObjcType encodings[] = {
	{  _C_ID,		"0x%06lx", 	"id"},
	{  _C_CLASS,	"0x%06lx",	"Class"},
	{  _C_SEL,		"%s",			"SEL"},
	{  _C_CHR,		"%c",			"char"},
	{  _C_UCHR,		"%c",			"unsigned char"},
	{  _C_SHT,		"%s",			"short"},
	{  _C_USHT,		"%s",			"unsigned short"},
	{  _C_INT,		"%d",			"int"},
	{  _C_UINT,		"%d",			"unsigned int"},
	{  _C_LNG,		"%l",			"long"},
	{  _C_ULNG,		"%l",			"unsigned long"},
	{  _C_FLT,		"%f",			"float"},
	{  _C_DBL,		"%f",			"double"},
	{  _C_VOID,		"0x%06lx",	"void"},
	{  _C_PTR,		"0x%06lx",	"pointer"},
	{  _C_CHARPTR,	"*s",			"char *"},
	{  _C_STRUCT_B,"%x",			"struct"},
	{  0, 			"0x%06lx",	"Unknown"},
};

@end

	