(* ----------------------------------------------------------------------------
 * $Id: MASERR.mi,v 1.9 1995/11/05 08:50:56 kredel Exp $
 * ----------------------------------------------------------------------------
 * This file is part of MAS.
 * ----------------------------------------------------------------------------
 * Copyright (c) 1989 - 1992 Universitaet Passau
 * ----------------------------------------------------------------------------
 * $Log: MASERR.mi,v $
 * Revision 1.9  1995/11/05 08:50:56  kredel
 * Added return value for runtime.
 *
 * Revision 1.8  1995/11/04  20:40:00  pesch
 * Renamed massignal.m? to massig.m? because of conflict with MASSIGNAL.m?
 * on certain OS.
 *
 * Revision 1.7  1995/09/12  17:22:49  pesch
 * Signal handling function are now declared in massignal.
 * Action has been changed according to ANSI C.
 * signal.* and massignal.* have been removed, the former because of
 * name clash with signal.h.
 *
 * Revision 1.6  1995/03/23  17:43:57  pesch
 * Added new options -E (exit on error) and -c (command).
 *
 * Revision 1.5  1993/03/22  10:22:33  kredel
 * Make CONTROL-C, SIGINT work
 *
 * Revision 1.4  1992/10/16  13:47:54  kredel
 * Errors found by Mocka
 *
 * Revision 1.3  1992/10/15  16:24:58  kredel
 * Changed rcsid variable
 *
 * Revision 1.2  1992/02/12  17:31:55  pesch
 * Moved CONST definition to the right place
 *
 * Revision 1.1  1992/01/22  15:10:38  kredel
 * Initial revision
 *
 * ----------------------------------------------------------------------------
 *)

IMPLEMENTATION MODULE MASERR;

(* MAS Error Implementation Module. *)


(* Import lists and Definitions *) 

FROM SYSTEM	IMPORT	ADDRESS, ADR, BYTE;

FROM MASmtc	IMPORT	getstck, gettoc;

FROM setjmp	IMPORT	jmp_buf, jmp_buf, longjmp, setjmp;

FROM massig	IMPORT	Action, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGSEGV,
			SIGTERM, SIG_ACK, SIG_ACK, SIG_ACK, SIG_ACK, SIG_DFL,
			SIG_DFL, SIG_DFL, SIG_DFL, SIG_ERR, SIG_ERR, SIG_ERR,
			SIG_ERR, SIG_IGN, SIG_IGN, SIG_IGN, SIG_IGN, raise,
			signal;  

FROM StdIO	IMPORT	ReadC, ReadI, WriteC, WriteFlush, WriteI, WriteN,
			WriteNl, WriteS;

FROM MASELEM	IMPORT	GAMMAINT, MASEXP, MASMAX, MASODD;

FROM MASCONF	IMPORT	ExitOnError;

(*
(* error indicators *)
CONST spotless  = 0;
      harmless  = 1;
      severe    = 2;
      fatal     = 3;
      confusion = 9;
TYPE P0 = PROCEDURE;
VAR DebugProcessor: P0; (* procedure called by ERROR if debug requested *)
    history: GAMMAINT; (* highest error level encountered so far. *)
*)

CONST rcsidi = "$Id: MASERR.mi,v 1.9 1995/11/05 08:50:56 kredel Exp $";
CONST copyrighti = "Copyright (c) 1989 - 1992 Universitaet Passau";

CONST maxerr   = 20;        (* maximal number of errors allowed *)

TYPE
     P1 = PROCEDURE(GAMMAINT);
     PS = PROCEDURE(ARRAY OF CHAR);

VAR elevel:  GAMMAINT;
    fehler:  GAMMAINT;
    schluss, errhup: BOOLEAN;

    errenv: jmp_buf;

    eh: GAMMAINT; 

    STACK, STACKEND: ADDRESS;

    lasterr: INTEGER;


PROCEDURE InitERR;
(*Initialize error handler. *)
BEGIN
(*1*) (*Set variables. *)
      elevel:=0; lasterr:=0; 
      STACKEND:=ADDRESS(0);
(*2*) (*procedures. *)
      schluss:=FALSE; errhup:=FALSE;
      DebugProcessor:=P0(NIL);
(*7*) END InitERR;


PROCEDURE DumpStack;
(*Dump Stack.  Dump objects on current stack. *)
VAR   I, E, a: GAMMAINT;
      c: CHAR;
BEGIN
(*1*) (*initialize. *) 
      I:=GAMMAINT(getstck()); E:=I+1000;
      WL; WS("Stack Dump: "); WL;
(*2*) (*dump objects. *) 
      WHILE I <= E DO a:=GETLONGINT(I);
	    WI(I); WS(" = "); WH(a); 
            WL; WS("(e)nd, <ENTER> ? ");
            c:=InChar();
            IF (c = 'e') OR (c = 'E') THEN RETURN END; 
            (* Must step by 1 because of CHARs and BYTes. *)
            I:=I+1; END;
(*4*) END DumpStack;


PROCEDURE GETLONGINT(A: LONGINT): LONGINT;
(*GET A LONG INTEGER FROM SPECIFIED ADDRESS. REGARDELESS
IF ADDRESS IS ON A WORD BOUNDARY. *) 
VAR   AL: LONGINT;
      XP, YP: POINTER TO BYTE;
      EINZ: ADDRESS;
BEGIN
(*1*) (*INITIALIZE*)  
      XP:=ADR(AL); YP:=ADDRESS(A); EINZ:=ADDRESS(1); 
(*2*) (*COPY BYTES*)
      (*OVERLAY WITH VARIABLE ALLIGNED ON CORRECT BOUNDARY*) 
      XP^:=YP^; (*1. BYTE*) 
      XP:=ADDRESS(XP)+EINZ; YP:=ADDRESS(YP)+EINZ;
      XP^:=YP^; (*2. BYTE*)
      XP:=ADDRESS(XP)+EINZ; YP:=ADDRESS(YP)+EINZ;
      XP^:=YP^; (*3. BYTE*)
      XP:=ADDRESS(XP)+EINZ; YP:=ADDRESS(YP)+EINZ;
      XP^:=YP^; (*4. BYTE*)
(*3*) (*FINISH.*) RETURN(AL);
END GETLONGINT;


PROCEDURE ERROR(a: GAMMAINT; s: ARRAY OF CHAR);
(*Error. An error of severity a and indication s is reported. *)
VAR   i: INTEGER;
      c: CHAR;
BEGIN
(*1*) (*initialize *) 
      IF a > history THEN history:=a END; 
      (*stack check
      IF getstck() < Tstack THEN a:=fatal;
	 WL; WS("** Stack underflow."); END; *)
      IF (a = spotless) AND (HIGH(s) < 5) THEN RETURN END;
      elevel:=elevel+1;
(*2*) (*report *) WL; WS("** "); 
      i:=INTEGER(a);
      CASE         i OF 
           spotless: |
           harmless: WS("error: ") |
           severe  : WS("severe error: ") |
           fatal   : WS("fatal error: ") 
                ELSE WS("("); WI(a); WS(") ") END; 
      WS(s); WS(" ");
      IF lasterr = SIGTERM THEN history:=confusion; longjmp(errenv,1) END; 
(*3*) (*interact *) 
      IF i > harmless THEN
     	 IF ExitOnError THEN a:=confusion;
	 ELSE
	     LOOP WL; WS("(a)bort, (b)reak, (c)ontinue, (d)ebug, <ENTER> ? ");
                  c:=InChar(); 
                     IF (c = 's') OR (c = 'S') THEN DumpStack
                  ELSIF (c = 'i') OR (c = 'I') THEN SysInfo
                  ELSIF (c = 'd') OR (c = 'D') THEN Debug
                   ELSE EXIT END;
                  END;
             CASE        c OF 
                  'a','A': WS("abort"); a:=confusion |
                  'b','B': WS("break"); a:=fatal |
                  'c','C': WS("continue"); a:=severe |
                      ELSE WS("system") (*use system decision*) END; 
             WS(". "); 
             END; 
      END;
      IF a > history THEN history:=a END; 
(*4*) (*history and error count *) 
      elevel:=elevel-1;
      (*avoid recursive errors*)
      IF elevel > 0 THEN history:=history+1 END;  
      IF history > spotless THEN fehler:=fehler+1 END;
      (*avoid to much errors*)
      IF fehler > maxerr THEN history:=history+1 END;
(*5*) (*continue ? *) 
      IF (spotless <= history) AND (history < fatal) THEN RETURN END;
      IF (spotless <= a) AND (a < fatal) THEN RETURN END;
      IF NOT errhup THEN HALT END;
      IF history > confusion THEN HALT END;
      longjmp(errenv,2);
(*6*) END ERROR;


PROCEDURE Debug;
(*call DebugProcessor if available. *)
BEGIN
(*1*) IF ADDRESS(DebugProcessor) = NIL THEN 
         WL; WS("No Debug Processor available."); WL;
         RETURN END;
(*2*) WL; WS("Entering Debug Processor ..."); WL;
      DebugProcessor;  
      WL; WS("... Debug Processor exited."); WL;
(*4*) END Debug;


PROCEDURE ErrorHandler(a: P0): GAMMAINT;
(*Error handler. Any error reported by the ERROR procedure is catched. *)
VAR   f, t: INTEGER;
      oldsig: Action;
BEGIN
(*1*) f:=0; 
      oldsig:=signal(SIGINT, runtime); 
      IF oldsig = SIG_ERR THEN 
         WL; WS("Cannot install runtime signal handler."); WL; 
         END;
   (* oldsig:=signal(SIGILL, runtime); 
      oldsig:=signal(SIGFPE, runtime); 
      oldsig:=signal(SIGBUS, runtime); 
      oldsig:=signal(SIGSEGV, runtime); 
      oldsig:=signal(SIGTERM, runtime); *)
(*2*) REPEAT f:=f+1;
             fehler:=0; history:=spotless;
	     t:=setjmp(errenv); errhup:=TRUE;
	     IF t = 0 THEN a(); schluss:=TRUE;
		      ELSE IF history >= confusion THEN schluss:=TRUE END 
                           END;
             IF NOT schluss AND (history > harmless) THEN 
                WL; WS("Trying to restart program ... "); WL;
                END;
             UNTIL schluss OR (f > maxerr);
(*3*) (*WL; WS("... done."); WL;*)
      oldsig:=signal(SIGINT, SIG_DFL); 
      errhup:=FALSE; 
      RETURN(history);
(*4*) END ErrorHandler;


PROCEDURE runtime(e: INTEGER): INTEGER;
(*Run time error handler for Unix. *)
VAR   s: ARRAY [0..100] OF CHAR;
      abort: BOOLEAN;
      err: GAMMAINT; 
      oldsig: Action; x:INTEGER;  
BEGIN
(*1*) abort:=TRUE; WL; lasterr:=e; err:=fatal;
      CASE e OF 
      SIGINT : s:=" Interrupt. "; abort:=FALSE; err:=severe |
(*unused*)
      SIGILL : s:=" Illegal Instruction. "; abort:=FALSE; err:=fatal |
      SIGFPE : s:=" Floating Point Error. "; abort:=FALSE; err:=fatal |
      SIGBUS : s:=" Bus Error. "; abort:=FALSE; err:=fatal |
     SIGSEGV : s:=" Segmentation Error. "; abort:=FALSE; err:=fatal |
     SIGTERM : s:=" Terminate. "; abort:=FALSE; err:=harmless |
          ELSE s:=" Unknown Error. "; abort:=TRUE; err:=fatal   
               END;
      WI(GAMMAINT(e)); WS(s); 
(*2*) IF abort THEN x:=raise(e); (*should not return*) HALT; END;
      oldsig:=signal(e, runtime); (*rs6000*)
      ERROR(err,"Signal received.");
      RETURN(0); 
(*4*) END runtime;


PROCEDURE WI(a: GAMMAINT);
(*Utility. *)
BEGIN
(*1*) WriteI(a,1); (*Dependes on MASELEM. *)
(*4*) END WI;


PROCEDURE WH(a: GAMMAINT);
(*Utility. *)
BEGIN
(*1*) WriteI(a,1); WriteS("  "); WriteN(a,1,16); (*Dependes on MASELEM. *)
(*4*) END WH;


PROCEDURE WS(s: ARRAY OF CHAR);
(*Utility. *)
BEGIN
(*1*) WriteS(s); WriteFlush;
(*9*) END WS;


PROCEDURE WL();
(*Utility. *)
BEGIN
(*1*) WriteNl; WriteFlush;
(*4*) END WL;


PROCEDURE InChar(): CHAR;
VAR   c: CHAR;
BEGIN
(*1*) c:=ReadC();
      IF ORD(c) <= 10 THEN c:=ReadC() END;
      RETURN(c); 
(*2*) END InChar;


PROCEDURE Escape;
VAR   c: CHAR;
BEGIN
(*1*) c:=ReadC();
      IF ORD(c) <= 10 THEN c:=ReadC(); END;
      IF ORD(c) = 27 THEN ERROR(severe,"ESC key pressed.") END;
(*2*) END Escape;


(* Test procedures *)

PROCEDURE SysInfo;
(*System Information. *)
VAR   a, b, bp, i, c: GAMMAINT;
BEGIN
(*4*) (*stack info. *)
      STACK:=getstck();
      WS("STACK END    = "); WH(GAMMAINT(STACKEND)); WL; 
      WS("STACK A1     = "); WH(GAMMAINT(STACK)); WL;
(*5*) (*register info. *)
      a:=GAMMAINT(gettoc());
      WS("      A2     = "); WH(a); WL;
(*7*) END SysInfo;


(*TEST  
PROCEDURE Hold;
VAR   c: CHAR;
BEGIN
(*1*) c:=ReadC();
      IF ORD(c) > 13 THEN Pause END;
(*2*) END Hold;


PROCEDURE Pause;
VAR   c: CHAR;
BEGIN
(*1*) REPEAT c:=ReadC();
       UNTIL ORD(c) > 0; 
(*2*) END Pause;


PROCEDURE TestERR;
(*Test error. *)
VAR   a,b: GAMMAINT;
BEGIN
(*1*) REPEAT WS("Eingabe 1: ");
	     b:=ReadI(); WL;
             IF b >= 0 THEN ERROR(b,"Fehler in testerr.") END;
             UNTIL b < 0;
(*2*) REPEAT WS("Eingabe 2: ");
	     b:=ReadI(); WL;
             IF b >= 0 THEN a:=1 DIV b END;
             UNTIL b < 0;
(*4*) END TestERR;
  TEST*)



(* MASSTOR module initialization.  *)

BEGIN
(*1*) STACKEND:=getstck(); (*remember stack address*)
(*2*) InitERR;
(*3*) (*TEST  
      eh:=ErrorHandler(TestERR);
      WS("nach error handler 1"); WL; 
      SysInfo;
      eh:=ErrorHandler(TestERR);
      WS("nach error handler 2"); WL; 
      SysInfo;
        TEST*)
END MASERR.

(* -EOF- *)