/*** exception.c ***/
/* C exception handling system
 * (c) Paul Field
 */

#include "exception.h"

#include <assert.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "kernel.h"

static exception current;
_exception_block *_exception_list = NULL;

void throw(void)
{
	_exception_block* first_block;

	first_block = _exception_list;
	if (first_block == NULL)
	{
		exit(EXIT_FAILURE);
	}
	else
	{
		_exception_list = first_block->previous;
		longjmp(first_block->jmpbuf, 1);
	}
}

void __throw_user(unsigned id, char* file , int line)
{
	current.type          = exception_user;
	current.error.user_id = id;
	current.file          = file;
	current.line          = line;
	throw();
}

void __throw_os(const _kernel_oserror* err, char* file , int line)
{
	if (err == NULL) return;

	current.type     = exception_os;
	current.error.os = err;
	current.file          = file;
	current.line          = line;
	throw();
}

void __throw_last_os_error(char* file , int line)
{
	current.file          = file;
	current.line          = line;
	throw_os(_kernel_last_oserror());
}

void __throw_string(char* file , int line, const char* pformat, ...)
{
	va_list arg;
	static _kernel_oserror err;

	va_start(arg, pformat);
	vsprintf(err.errmess, pformat, arg);
	va_end(arg);

	err.errnum = 1;
	__throw_os(&err, file, line);
}

const exception* exception_current(void)
{
	return &current;
}

static void throw_signal(int sig)
{
	current.type            = exception_signal;
	current.error.signal_id = sig;
	signal(sig, throw_signal);  /* reinstate signal handler */
	throw();
}

static void throw_last_os(int sig)
{
	sig=sig;
	signal(SIGOSERROR, throw_last_os);  /* reinstate signal handler */
	throw_os(_kernel_last_oserror());
}

void exception_initialise(void)
{
	signal(SIGABRT, throw_signal);
	signal(SIGFPE,  throw_signal);
	signal(SIGILL,  throw_signal);
	signal(SIGINT,  throw_signal);
	signal(SIGSEGV, throw_signal);
	signal(SIGTERM, throw_signal);
	/*signal(SIGSTAK, throw_signal);*/
	signal(SIGOSERROR, throw_last_os);
}
