#include <stdio.h>
#include <stdarg.h>
#include <ert.H>
#include <string.H>

struct call_frame {
   void* object;
   char* class_name;
   char* routine;
   struct call_args* args;
};

struct call_args* new_arg ( char*, char*, int);
struct call_frame* new_call_frame ( void*, char*, char*, struct call_args*);
void print_args_list ( FILE *fp, struct call_args*);
void free_args ( struct call_args*);

extern "C" {
char *_ert_traceback_map ( void *, int);
#if !defined ( _DJGPP_) && !defined ( _TCC_)
char *getenv(char *);
#endif

#ifdef _TCC_

#include <stdlib.h>
#include <setjmp.h>

int   strlen ( char *);
char *strcpy ( char *, char *);
int   strcmp ( char *, char *);
int   memcpy ( char *, char *, int);
int   memcmp ( char *, char *, int);
#endif
}

static struct call_frame* frameblk[_ERT_JMP_MAX];

static int trace_guard = 0;
static long TraceRetry = -1l;
static FILE *ExceptFp = NULL;
static char *_execname;


void
_ert_open_trace( char *execname)
{
	char *pt, *p;
#ifdef MSDOS
	register i;
	char *cptr;
	char namebuf[80];
#endif

	pt = execname + strlen ( execname);
	while ( pt > execname) {
		if ( *pt == '/' || *pt == PATH_SEP_CHAR) {
			execname = pt + 1;
			break;
		}

		pt--;
	}

	if ( !( p = getenv ( "EON_TMP")))
		p = TMP_DIR;


	pt = (char *) malloc ( strlen ( p) + 
				strlen ( execname) + strlen ( TRACE_EXT) + 2);

	strcpy ( pt, p);

#ifdef MSDOS
	for ( cptr = (char*)(execname+strlen(execname)-1);
			cptr >= execname && (*cptr != '\\' && *cptr != '/');
			cptr--)
		;
	if ( cptr > execname)
		strcpy ( namebuf, cptr+1);
	else
		strcpy ( namebuf, execname);
	for ( cptr = namebuf; *cptr && *cptr != '.'; cptr++)
		;
	if ( *cptr)
		*cptr = '\0';
	sprintf ( pt + strlen ( pt), "%c%s%s", PATH_SEP_CHAR, namebuf, TRACE_EXT);
#else
	sprintf ( pt + strlen ( pt), "%c%s%s", PATH_SEP_CHAR, execname, TRACE_EXT);
#endif

	_execname = (char *) malloc ( strlen ( pt) + 1);
	strcpy ( _execname, pt);

	free ( pt);

}

void
_ert_close_trace()
{
	if ( ExceptFp) {
		fprintf ( ExceptFp, 
			"\nProgram terminated (full exception listing in \"%s\").\n", _execname);
		fclose ( ExceptFp);
	}
}

void
_trace_write_log ( char *rec_class, char *rec_routine, void* addr, int type, 
   char *class_name, char *routine, char *tag, char *cond, int rcode, int depth)
{
	register i;

	if ( !ExceptFp) {
		if ( ( ExceptFp = fopen ( _execname, "w")) == NULL) {
			fprintf ( stderr, "Unable to open exception log \"%s\".\n", 
				_execname);
			exit ( 1);
		}

		fprintf ( ExceptFp, "\n");
		for ( i = 0; i < 79; i++) 
			fprintf ( ExceptFp, "-");
		fprintf ( ExceptFp, "\n");

/*
123456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789
*/

		fprintf ( ExceptFp, 
"Object   Class             Routine              Nature of exception     Effect\n");
		for ( i = 0; i < 79; i++) 
			fprintf ( ExceptFp, "=");
		fprintf ( ExceptFp, "\n\n");
	}

	fprintf ( ExceptFp, "%08lx %-17.17s %-20.20s %-25.25s %s\n", (long) addr,
			rec_class, rec_routine, 
			_ert_except_mesg ( type)->to_c (), 
			rcode == EXCEPT_RETRY ? "Retry" : " Fail");

	if ( class_name && (strcmp ( class_name, rec_class) || strcmp ( routine, rec_routine))) {

		fprintf ( ExceptFp, "         (from %.11s.%s)\n", 
			class_name || !depth || !frameblk[depth] 
						? class_name : frameblk[depth]->class_name,
			class_name || !depth || !frameblk[depth] 
						? routine : frameblk[depth]->routine);
	}

	if ( cond)
		fprintf ( ExceptFp, "%s: %s\n", 
				tag ? tag : "<no tag>", cond);

	if ( _ert_depth > 1 && frameblk[depth])
   		print_args_list ( ExceptFp, frameblk[depth]->args);
		
	if ( rcode == EXCEPT_FAIL) {
		for ( i = 0; i < 79; i++)
			fprintf ( ExceptFp, "-");
		fprintf ( ExceptFp, "\n");
	} else {
		for ( i = 0; i < 79; i++)
			fprintf ( ExceptFp, "=");
		fprintf ( ExceptFp, "\n");

		if ( rcode == EXCEPT_RETRY)
			TraceRetry = ftell ( ExceptFp);
	}

	fflush ( ExceptFp);
}

struct call_args*
_make_arg_list (char *begin, ...)
{
   	static struct call_args* result;
   	struct call_args *ptr = 0, *optr = 0;
   	va_list ap;
   	char *start, *name;
	void *val;
	int formal_id;

	if ( trace_guard)
		return 0;

   	va_start ( ap, begin);

	start = begin;
     if ( !start) {
		va_end ( ap);
     	return 0;
	}

	trace_guard++;

	result = 0;

   	while ( 1) {

      	val = va_arg ( ap, void*);
      	formal_id = va_arg ( ap, int);

    		ptr = new_arg ( start, (char *) val, formal_id);
      	if ( result == 0)
         		result = ptr;
      	else
         		optr->next = ptr;

      	optr = ptr;

      	start = va_arg ( ap, char*);
      	if ( start == 0)
         		break;
   	}

   	va_end ( ap);

	trace_guard--;

	if ( _ert_trace_mode > 2)
   		print_args_list ( stdout, result);

   	return result;
}

void
_enter_routine ( void *object, char* class_name, 
							char* rtn_name, struct call_args* args)
{
   	frameblk[_ert_depth] = 
				new_call_frame ( object, class_name, rtn_name, args);
}

void
free_call_frame ( struct call_frame* frame)
{
   if ( frame != 0) {
      if ( frame->class_name != 0)
         free ( frame->class_name);
      if ( frame->routine != 0)
         free ( frame->routine);
      if ( frame->args != 0)
         free_args ( frame->args);
      free ( frame);
	
   }
}

void
_leave_routine()
{
   	struct call_frame* ptr;
	
   	if ( _ert_depth) {
      	free_call_frame ( frameblk[_ert_depth]);
		frameblk[_ert_depth] = 0;
	}

}

struct call_frame*
new_call_frame ( void* object, char* class_name, 
								char* name, struct call_args* args)
{
   	static struct call_frame* result;

   	result = (struct call_frame*) malloc ( sizeof ( struct call_frame));
   	if ( result == 0)
      	_ert_memory_error ( "new_call_frame", sizeof ( struct call_frame));

   	if ( class_name && strlen ( class_name)) {
      	result->class_name = (char*) malloc ( strlen (  class_name) + 1);
      	if ( result == 0)
         		_ert_memory_error ( "new_call_frame", strlen ( class_name) + 1);
      	
      	strcpy ( result->class_name, class_name);
   	} else
      	result->class_name = 0;

   	if ( name && strlen ( name)) {
      	result->routine = (char*) malloc ( strlen (  name) + 1);
      	if ( result == 0)
         		_ert_memory_error ( "new_call_frame", strlen ( name) + 1);
      	
      	strcpy ( result->routine, name);
   	} else
      	result->routine = 0;

   	result->object = object;
   	result->args = args;

   	return result;
}

struct call_args*
new_arg ( char* name, char* data, int formal_id)
{
   	static struct call_args* result;
	char *val;

	if ( !data)
		val = "<not created>";
	else if ( !formal_id || !memcmp ( data, "?", 2))
		val = "?";
	else
		val = _ert_traceback_map ( (void *) data, formal_id);

	result = (struct call_args*) malloc ( sizeof ( struct call_args));
	if ( result == 0)
		_ert_memory_error ( "new_arg", sizeof ( struct call_args));

	if ( name && strlen(name)) {
		result->arg_name = (char*) malloc ( strlen (  name) + 1);
		if ( result == 0)
			_ert_memory_error ( "new_arg", strlen ( name) + 1);
		
		strcpy ( result->arg_name, name);
	} else
		result->arg_name = 0;

	if ( val && strlen(val)) {
		result->arg_value = (char*) malloc ( strlen (  val) + 1);
		if ( result == 0)
			_ert_memory_error ( "new_arg", strlen ( val) + 1);
		
		strcpy ( result->arg_value, val);
	} else
		result->arg_value = 0;

	result->next = 0;

	return result;
}

/*
void
print_routine ( struct call_frame* fp)
{
   fprintf ( stderr, "-------------------------------------------------------------------------------\n");
   fprintf ( stderr, "0x%08lx %-12s %-15s   ", fp->object, fp->class_name, fp->routine);
   print_args_list ( ExceptFp, fp->args);
   fprintf ( stderr, "\n");
}
*/

void
_trace_back ( int mode)
{
	register i;
	int ch;

	if ( _ert_show_traceback == false)
		return;

	if ( !mode) {
		fprintf ( stderr, "No traceback available\n");
		exit ( 1);
	}

	fflush ( stdout);
	if ( ( ExceptFp = fopen ( _execname, "r")) == NULL) {
		fprintf ( stderr, 
			"No traceback available - Can't open file \"%s\"\n", _execname);
		exit ( 1);
	}


	i = 0;
	while ( i < 4) {
		if ( (ch = fgetc ( ExceptFp)) == EOF)
			break;

		fputc ( ch, stderr);
		if ( ch == '\n')
			i++;
	}

	if ( TraceRetry != -1l)
		fseek ( ExceptFp, TraceRetry, 0);

	while ( (ch = fgetc ( ExceptFp)) != EOF)
		fputc ( ch, stderr);


	fclose ( ExceptFp);

   	exit ( 1);
}

void
free_args ( struct call_args* list)
{
   struct call_args* ptr;

   ptr = list;
   while ( ptr != 0) {
      struct call_args* optr = ptr->next;

      if ( ptr->arg_name != 0)
         free ( ptr->arg_name);
      if ( ptr->arg_value != 0)
         free ( ptr->arg_value);
      free ( ptr);

      ptr = optr;
   }
}

void
print_args_list ( FILE *fp, struct call_args* list)
{
   	struct call_args* ptr;
	register char *t;
	register i;
	char buf[80], *x = "Parameter list:";
	int l, l1;

	if ( !list)
		return;

	l = strlen ( x);

   	ptr = list;
   	while ( ptr != 0) {
      	l1 = strlen ( ptr->arg_name ? ptr->arg_name : "???") + 3;

      	if ( ptr->arg_value != 0) {
			t = ptr->arg_value;
			for ( i = 0; i < 79; i++, t++)
				buf[i] = *t == '\n' ? '^' : *t;
			buf[i] = '\0';
		
         		l1 += strlen ( buf) + 2;
      	} else
         		l1 += 6;

		if ( l1 > l)
			l = l1;

      	ptr = ptr->next;
   	}

	l = 79 - l;

	if ( l > 48)
		l = 48;

	for ( i = 0; i < l; i++)
		fprintf ( fp, " ");
	fprintf ( fp, "%s\n", x);

   	ptr = list;
   	while ( ptr != 0) {
		
		for ( i = 0; i < l; i++)
			fprintf ( fp, " ");

      	fprintf ( fp, "%s = ", ptr->arg_name ? ptr->arg_name : "???");
      	if ( ptr->arg_value != 0) {
			t = ptr->arg_value;
			for ( i = 0; i < 79; i++, t++)
				buf[i] = *t == '\n' ? '^' : *t;
			buf[i] = '\0';
		
         		fprintf ( fp, "\"%s\"", buf);
      	} else
         		fprintf ( fp, "<Void>");

     	fprintf ( fp, "\n");

      	ptr = ptr->next;
   	}
}
