/* Copyright Per Bothner 1987. Read the file Q-INFO */
#pragma implementation
#include <iostream.h>
#include "types.h"
#include <parsefile.h>
#include <Qcompile.h>
#include <exceptions.h>
//#include <format.h>
#include <stdlib.h>
#include <string.h>
#include <machines.h>
#include <undobind.h>
#include "typetabs.h"
#include "debug.h"
#include <signal.h>

extern Name GetName();
extern char *ErrorString();
struct DataToken *GetMemTempToken();
extern "C" char* strerror(int);

#if 0
extern Var *GetLastVar();
struct ExceptionClass Any_exception[1] = 
   {{{"Any_exception", 0,0,0,0,0,0,0, ExceptionTypeKind },
    Any_exception, NULL, 0}};
#else
DefExceptionClass(Any_exception, Any_exception, 0);
#endif
DefExceptionClass(Fail, Any_exception, 1);
DefExceptionClass(End_of_file, Fail, 2);
DefExceptionClass(Compare_failed, Fail, 2);
DefExceptionClass(Condition_exclass, Fail, 2);
DefExceptionClass(Bad_arg_type, Fail, 2);
DefExceptionClass(Undefined, Fail, 2);
DefExceptionClass(No_match, Fail, 2);
DefExceptionClass(Illegal_request, Fail, 2);
DefExceptionClass(Error, Any_exception, 1);
DefExceptionClass(No_memory, Error, 2);
  
struct ExceptionClass *
DefException(struct Type *paramType, struct ExceptionClass *super)
{
    register ExceptionClass *except =
	(ExceptionClass*)malloc(sizeof(struct ExceptionClass));
    memset(except, 0, sizeof(struct ExceptionClass));
    except->parameter = paramType;
    if (super == NULL) super = Any_exception;
    except->super = super;
#if 0
    except->t.kind = ExceptionTypeKind;
    if (super->t.kind != ExceptionTypeKind) abort();
    except->class_name = NULL;
#endif
    except->level = super->level + 1;
    return except;
}

#if 0
void ExceptionPrint(struct Any ex, FILE *f)
{
    struct ExceptionClass *except = ex.addr;
    fprintf(f, "{Exception: ");
    if (except->exname == NULL) fprintf(f, "0x%X}", except);
    else fprintf(f, "%s}", except->exname);
}

ExceptionDumpPtr(struct Any ex, CFile *cf)
{
    struct ExceptionClass *except = ex.addr;
    if (except->exname == NULL) except->exname = GenString();
    cf->aux_stream() << "extern struct ExceptionClass "
	<< except->exname << "[1];\n";
    cf->asm_stream() except->exname;
    if (!(except->options & TypeIsCompiled)) {
	struct DataToken label[1];
	MakeLabelToken(label, except->exname, 0, 0);
	SavePendingValue(cf, label, ex);
	except->options |= TypeIsCompiled;
    }
}

ExceptionDumpData(struct Any ex, CFile *cf, struct Label *label)
{
    struct ExceptionClass *except = ex.addr;
    if (except->super == Any_exception)
	cf->asm_stream() << "extern struct ExceptionClass Any_exception[1];\n";
    cf->asm_stream() << "DefExceptionClass(" << except->exname << ", "
	<< except->super->exname << ", " << except->level << ")\n";
}

ExceptionDump(register ExceptionClass *except, CFile *cf, struct Label *label)
  { int id; char *superName; struct Label *nameLab = NULL;
    if (label != NULL && label->kind == StringLabel)
      {	char *name = label->label;
	if (except->name == NULL) except->name = name;
      }
    id = CompStdPrefix(cf, label, &ExceptionClass);
    Comp1Long(cf, 0, "(parameter type)");
    CompileLabelTo(cf, except->super);
    CompileLabelTo(cf, except->name);
    Comp1Short(cf, except->level, "(level)");
    return id;
  }
#endif

/* DummyFrame has a pseudo-RL that can be safely stored into
 * when various routines patch FRAME_RL(_Handler_->patched_FP) */
#ifndef MULTI_PROCESS
Continuation *_Handler_ = NullHandler;
#endif

#define HANDLER_STUB_ADDR(handler) ((char*)&(handler)->_call)
#define HANDLER_STUB_OFFSET HANDLER_STUB_ADDR((Continuation*)0)
/*#define FRAME_OLD_FP(frame) (((char**)(frame))[0]) * MACHINE_DEPENDENT */
/*#define FRAME_RL(frame) (((char**)(frame))[1]) * MACHINE_DEPENDENT */
extern void Patch_Return();

#if 1
void DestroyContinuation(Continuation *cont)
{
    Continuation *next = cont->next, **prev = cont->prev;
    next->prev = prev;
    *prev = next;
    cont->kind = 0; /*Invalid*/
}
extern "C" void UnlinkIfHandler(Continuation*cont)
{
    DestroyContinuation(cont);
}
#else
asm(" .globl UnlinkIfHandler");
asm("UnlinkIfHandler:");
asm(" .globl DestroyContinuation");
asm("DestroyContinuation:");
asm(" movl sp@(4),a0"); /* handler */
asm(" clrw a0@(72)"); /* mark handler as invalid */
asm(" movl a0@,a1"); /* next = handler->next */
asm(" movl a0@(60),a0"); /* prev = handler->prev */
asm(" movl a0,a1@(60)"); /* next->prev = prev */
asm(" movl a1,a0@"); /* *prev = next */
asm(" rts");
#endif

extern "C" void BadContinuation()
{
    fprintf(stderr, "!Invocation of deleted Continuation!\n");
    fflush(stderr);
    abort();
}
extern void InvokeContinuation(Continuation *, void *);
extern "C" volatile _InvokeContinuation(char*, Continuation*);

volatile void InvokeDestroyContinuation(Continuation *cont)
{
    char *new_SP = CONT_SAVE_SP(_Handler_);
    if (cont->kind == 0) BadContinuation();
    DestroyContinuation(cont);
    _InvokeContinuation(new_SP, cont);
}

extern "C" void UnlinkExceptionHandler(register Continuation *desc)
  { Continuation *next = desc->next, **prev = desc->prev;
    if (prev == NULL) return;
    next->prev = prev; *prev = next;
    desc->prev = NULL; /* since THEN is optional, there can be two calls */
  }

short PrintOnRaise = 0;
struct ExceptionClass *LastRaiseException = NULL;
Root* LastRaiseParameter = NULL;
Condition* LastRaiseCondition = NULL;


#if 0
CompileElseExpr(self, cf, dest)
    struct ElseExpr *self; register CFile *cf; struct DataToken *dest;
  {
    abort();
  }
#endif

void UndoBindings(UndoCommand *limit)
  {
#if 0
    UndoCommand *curUndo, *next;
    Var *first, *prev = NULL;
    for (curUndo = UndoTrail; curUndo != limit; curUndo = next) {
	switch (curUndo->kind) {
	  case UndoLinkFlag:
	    next = curUndo->link.prev;
#ifdef HAVE_PAGE_ALLOC
	    FreePages((int)curUndo & ~ (PageSize-1), PageSize);
#else
	  {
	    char *buffer = UndoBuffer;
	    UndoBuffer = curUndo->link.buffer;
	    free(buffer);
	  }
#endif
	    break;
	  case UndoEqualFlag: {
	    Var *var = curUndo->equal.var;
	    Var *last = GetLastVar(var); /* NOTE - avoid extra search */
	    Var *v = last->v.varlink.u;
	    last->v.varlink.u = MakeLastPtr(var->v.varlink.u);
	    var->v.varlink.u = v;
	    next = (UndoCommand*)(&curUndo->equal + 1);
	    break;
	  }
	/* When a variable is instantiated, a UndoLastFlag command
	 * is pushed, followed by zero or more UndoSetFlag (one for
	 * each non-IsLastVar variable bound equal).
	 * When undoing, we go into opposite order, trying to re-create
	 * the list of linked variables.
	 * The first to undo is recognized by prev==NULL.
	 */
	  case UndoSetFlag: {
	    Var *var = curUndo->set.var;
	    if (prev == NULL) first = var;
	    var->v.varlink.u = prev;
	    prev = var;
#if 0
	    var->v.flags = curUndo->set.flags; var->v.misc = curUndo->set.misc;
#endif
	    next = (UndoCommand*)(&curUndo->set + 1);
	    break;
	  }
	  case UndoSeekFlag: {
#if 1
abort();
#else
	    struct BFile *file = curUndo->seek.file;
	    file->handlerAtLastSeek = curUndo->seek.savedHandlerAtLastSeek;
	    file->flags &= ~FileUndoSeeks;
	    BFileSeek(file, curUndo->seek.savedIndex, 0);
	    file->flags |= FileUndoSeeks;
	    next = (UndoCommand*)(&curUndo->seek + 1);
#endif
	    break;
	  }
	  case UndoLastFlag: {
	    Var *var = curUndo->set.var;
	    if (prev == NULL) prev = var;
	    else first->v.varlink.u = var;
	    var->v.flags = curUndo->set.flags; var->v.misc = curUndo->set.misc;
	    var->v.varlink.u = MakeLastPtr(prev); 
	    prev = NULL;
	    next = (UndoCommand*)(&curUndo->set + 1);
	    break;
	  }
	  case UndoChangeFlag:
	    *(void**)curUndo->change.ptr = curUndo->change.old;
	    next = (UndoCommand*)(&curUndo->change + 1);
	    break;
	  case UndoJoinFlag:
	  {
	    void *tmp;
	    tmp = *(void**)curUndo->join.ptr1;
	    *(void**)curUndo->join.ptr1 = *(void**)curUndo->join.ptr2;
	    *(void**)curUndo->join.ptr2 = tmp;
	    next = (UndoCommand*)(&curUndo->join + 1);
	    break;
	  }
	  default:
	    fprintf(stderr, "Illegal kind of UndoCommand: %d\n",
		curUndo->kind);
	    fflush(stderr);
	    abort();
	}
    };
    UndoTrail = curUndo;
#endif
  }

#ifndef USE_EXCEPTIONS
static void PrintError()
{
    register ExceptionClass *ex = LastRaiseException;
    if (ex != Condition_exclass)
	LastRaiseCondition = NULL;
    if (ex == Condition_exclass)
	; // cerr << "Exception: ";
    else if (ex->exname != NULL)
	cerr << "Exception: " << ex->exname;
    else cerr.form("Exception: #%x", ex);
    if (LastRaiseParameter == NULL) { }
    else cerr << *(Root*)LastRaiseParameter;
    cerr << '\n';
}
#endif

volatile Object _Raise_(struct ExceptionClass *except, void *parameter)
{
#ifdef USE_EXCEPTIONS
    Signal(new GenericCondition(except->exname));
#else
    extern void UndoBinding();
    register Continuation *handler = _Handler_; /* a5 */
    extern int _Raise_jmp_();
    LastRaiseException = except;
    LastRaiseParameter = (Root*)parameter;
#ifndef USE_LONGJMP
    while (RealHandler(handler)) {
    /* for each handler-frame... */
	register struct ExceptionClass **ptr =
	    (struct ExceptionClass **)(handler->handler_PC - 2);
	Continuation *next = handler->next;
	int i;
	if (handler->kind == 2) {
	    for (i = *(short*)ptr; --ptr, --i >= 0; ) {
		struct ExceptionClass *except = except;
		while (except->level > ptr[0]->level) except = except->super;
		if (except == ptr[0]) goto found;
	    }
	    DestroyContinuation(handler);
	}
	handler = next;
    }
#endif
    if (handler == InteractiveHandler) {
	PrintError();
	UndoBindings(NULL);
	longjmp(main_jmp_buf, 1);
    }
#ifndef USE_LONGJMP
  found:
#endif
    if (PrintOnRaise || handler == NullHandler)
      {
	PrintError();
	fflush(stderr);
      }
    if (handler == NullHandler)
      {
	fprintf(stderr, "(No handler)\n");
	exit(-1);
      }

    UndoBindings(handler->undo_trail);
    InvokeDestroyContinuation(handler);
#endif
}

struct ExceptionClass *
MatchExceptions(register ExceptionClass *ex1, register ExceptionClass *ex2)
  {
    while (ex1 != ex2)
      {
	if (ex1->level > ex2->level) ex1 = ex1->super;
	else if (ex2->level > ex1->level) ex2 = ex2->super;
	else RunError(0, "MatchExceptions failed to converge");
      }
    return ex1;
  }

#if 0
CompRaise(cf, exception, param)
    CFile *cf; struct ExceptionClass *exception;
 /* param should be NULL for now */
  { struct DataToken tmp_token[1];
    AddressLabelToken(tmp_token, cf, "NoValue", NoValue);
    CompileMove(cf, tmp_token, PushStackToken);
    AddressLabelToken(tmp_token, cf, exception->exname, exception);
    CompileMove(cf, tmp_token, PushStackToken);
    CallExternC(cf, "_Raise_", _Raise_, 2, NULL);
  }
#endif

void Signal(Condition *cond)
{
    LastRaiseCondition = cond;
#ifdef USE_EXCEPTIONS
    throw cond;
#else
    RAISE(Condition_exclass, cond);
#endif
}

void ReRaiseUnlessMissing()
{
#ifdef USE_EXCEPTIONS
    if (LastRaiseCondition != &MissingElement)
	Signal(LastRaiseCondition);;
#else
    if (LastRaiseException != Condition_exclass
	|| LastRaiseCondition != &MissingElement)
	_Raise_(LastRaiseException, LastRaiseParameter);
#endif
}

void Condition::printon(ostream& outs) const
{
    outs << "Some error or condition was signalled";
}

void GenericCondition::printon(ostream& outs) const
{
    outs << message;
}

void UnboundVariable::printon(ostream& outs) const
{
    if (cell)
	outs << "Unbound variable cell: " << *cell;
    else
	outs << "Unbound variable cell";
}

void MissingCondition::printon(ostream& outs) const
{
    outs << "NONE";
}
MissingCondition MissingElement;
void CompareFail::printon(ostream& outs) const
{
    outs << "Failed comparison (" << op << ")";
    if (left && right)
	outs << " between " << *left << " and " << *right << ".";
    else
	outs << ".";
}

void CoercionFail::printon(ostream& outs) const
{
    outs << "Failed to coerce " << *value << " to type " << *type << ".";
}

void ParameterFail::printon(ostream& outs) const
{
    outs << "Actual parameter (" << *param_value()
	<< ") has wrong type for formal " << *param_name() << "~"
	    << *param_type() << ".";
}

void UnimplementedOp::printon(ostream& outs) const
{
    outs << "Unimplemented operation: " << operator_name;
    if (operand_type)
	outs << " for operand type " << *operand_type;
    outs << ".";
}

void BadAssignment::printon(ostream& outs) const
{
    outs << "Illegal assignment to: " << *var << "\n";
}

void BadSyscall::printon(ostream& outs) const
{
    outs.form(format, name1, name2);
    if (err_number != 0)
	outs << ": " << strerror(err_number);
    outs << '.';
}

void ProgramExitFailure::printon(ostream& outs) const
{
    outs << "Program ";
    if (program_name) outs << program_name << " ";
    outs << "(pid: " << pid <<") failed with return code " << return_code<<".";
}

#if 0

#if !defined (NSIG)
#  if defined (_NSIG)
#    define NSIG _NSIG
#  else
#    define NSIG 64
#  endif /* !_NSIG */
#endif /* !NSIG */

/* A translation list so we can be polite to our users. */
char *signal_names[NSIG];

static int signal_names_initialized = 0;

void initialize_signal_names ()
{
  register int i;

  if (!signal_names_initialized)
    {
      for (i = 1; i < NSIG; i++)
        {
	  signal_names[i] = (char *)NULL;
        }

      /* `signal' 0 is what we do on exit. */
      signal_names[0] = "EXIT";

#if defined (SIGHUP)		/* hangup */
      signal_names[SIGHUP] = "SIGHUP";
#endif

#if defined (SIGINT)		/* interrupt */
      signal_names[SIGINT] = "SIGINT";
#endif

#if defined (SIGQUIT)		/* quit */
      signal_names[SIGQUIT] = "SIGQUIT";
#endif

#if defined (SIGILL)		/* illegal instruction (not reset when caught) */
      signal_names[SIGILL] = "SIGILL";
#endif

#if defined (SIGTRAP)		/* trace trap (not reset when caught) */
      signal_names[SIGTRAP] = "SIGTRAP";
#endif

#if defined (SIGABRT)		/*  */
      signal_names[SIGABRT] = "SIGABRT";
#endif

#if defined (SIGIOT)		/* IOT instruction */
      signal_names[SIGIOT] = "SIGIOT";
#endif

#if defined (SIGEMT)		/* EMT instruction */
      signal_names[SIGEMT] = "SIGEMT";
#endif

#if defined (SIGFPE)		/* floating point exception */
      signal_names[SIGFPE] = "SIGFPE";
#endif

#if defined (SIGKILL)		/* kill (cannot be caught or ignored) */
      signal_names[SIGKILL] = "SIGKILL";
#endif

#if defined (SIGBUS)		/* bus error */
      signal_names[SIGBUS] = "SIGBUS";
#endif

#if defined (SIGSEGV)		/* segmentation violation */
      signal_names[SIGSEGV] = "SIGSEGV";
#endif

#if defined (SIGSYS)		/* bad argument to system call */
      signal_names[SIGSYS] = "SIGSYS";
#endif

#if defined (SIGPIPE)		/* write on a pipe with no one to read it */
      signal_names[SIGPIPE] = "SIGPIPE";
#endif

#if defined (SIGALRM)		/* alarm clock */
      signal_names[SIGALRM] = "SIGALRM";
#endif

#if defined (SIGTERM)		/* software termination signal from kill */
      signal_names[SIGTERM] = "SIGTERM";
#endif

#if defined (SIGCLD)		/* Like SIGCHLD.  */
      signal_names[SIGCLD] = "SIGCLD";
#endif

#if defined (SIGPWR)		/* Magic thing for some machines. */
      signal_names[SIGPWR] = "SIGPWR";
#endif

#if defined (SIGPOLL)		/* For keyboard input?  */
      signal_names[SIGPOLL] = "SIGPOLL";
#endif

#if defined (SIGURG)		/* urgent condition on IO channel */
      signal_names[SIGURG] = "SIGURG";
#endif

#if defined (SIGSTOP)		/* sendable stop signal not from tty */
      signal_names[SIGSTOP] = "SIGSTOP";
#endif

#if defined (SIGTSTP)		/* stop signal from tty */
      signal_names[SIGTSTP] = "SIGTSTP";
#endif

#if defined (SIGCONT)		/* continue a stopped process */
      signal_names[SIGCONT] = "SIGCONT";
#endif

#if defined (SIGCHLD)		/* to parent on child stop or exit */
      signal_names[SIGCHLD] = "SIGCHLD";
#endif

#if defined (SIGTTIN)		/* to readers pgrp upon background tty read */
      signal_names[SIGTTIN] = "SIGTTIN";
#endif

#if defined (SIGTTOU)		/* like TTIN for output if (tp->t_local&LTOSTOP) */
      signal_names[SIGTTOU] = "SIGTTOU";
#endif

#if defined (SIGIO)		/* input/output possible signal */
      signal_names[SIGIO] = "SIGIO";
#endif

#if defined (SIGXCPU)		/* exceeded CPU time limit */
      signal_names[SIGXCPU] = "SIGXCPU";
#endif

#if defined (SIGXFSZ)		/* exceeded file size limit */
      signal_names[SIGXFSZ] = "SIGXFSZ";
#endif

#if defined (SIGVTALRM)		/* virtual time alarm */
      signal_names[SIGVTALRM] = "SIGVTALRM";
#endif

#if defined (SIGPROF)		/* profiling time alarm */
      signal_names[SIGPROF] = "SIGPROF";
#endif

#if defined (SIGWINCH)		/* window changed */
      signal_names[SIGWINCH] = "SIGWINCH";
#endif

#if defined (SIGLOST)		/* resource lost (eg, record-lock lost) */
      signal_names[SIGLOST] = "SIGLOST";
#endif

#if defined (SIGUSR1)		/* user defined signal 1 */
      signal_names[SIGUSR1] = "SIGUSR1";
#endif

#if defined (SIGUSR2)		/* user defined signal 2 */
      signal_names[SIGUSR2] = "SIGUSR2";
#endif

#if defined (SIGMSG)	/* HFT input data pending */
      signal_names[SIGMSG] = "SIGMSG";
#endif

#if defined (SIGPWR)	/* power failure imminent (save your data) */
      signal_names[SIGPWR] = "SIGPWR";
#endif

#if defined (SIGDANGER)	/* system crash imminent */
      signal_names[SIGDANGER] = "SIGDANGER";
#endif

#if defined (SIGMIGRATE)	/* migrate process to another CPU */
      signal_names[SIGMIGRATE] = "SIGMIGRATE";
#endif

#if defined (SIGPRE)	/* programming error */
      signal_names[SIGPRE] = "SIGPRE";
#endif

#if defined (SIGGRANT)	/* HFT monitor mode granted */
      signal_names[SIGGRANT] = "SIGGRANT";
#endif

#if defined (SIGRETRACT)	/* HFT monitor mode retracted */
      signal_names[SIGRETRACT] = "SIGRETRACT";
#endif

#if defined (SIGSOUND)	/* HFT sound sequence has completed */
      signal_names[SIGSOUND] = "SIGSOUND";
#endif

      for (i = 0; i < NSIG; i++)
	if (signal_names[i] == (char *)NULL)
	  {
	    signal_names[i] = (char *)xmalloc (10 + strlen ("SIGNAL"));
	    sprintf (signal_names[i], "SIGNAL(%d)", i);
	  }
    }
}

/* Return the print name of this signal. */
char *
signal_name (int signal)
{
    if (!signal_names_initialized)
	initialize_signal_names ();
    if (signal > NSIG || signal < 0)
	return ("bad signal number");
    else return (signal_names[signal]);
}
#endif

extern "C" char* strsignal(int); // Imported from in libiberty into libg++.a.

void SysSignal::printon(ostream& outs) const
{
    outs << "Caught " << strsignal(sig) << " signal.";
}
