/* signal.h
  L. Stewart June 2, 1982  9:25 AM
  From D. Swinehart's bcpl signaller package
 */

#define	UNWIND	1
#define	ANY	0
#define	ERROR	2

#define	minClientError	10
#define	maxClientError	037777

/*
   Clients may assign SIGNAL values via compiled-in constants
 or any other scheme, if they fall within [minClientError..maxClientError].
 If duplicate values result, caveat clientus.  code() returns a new SIGNAL
 value outside this interval.  To be sure each signal is distinct, make
 it a static and initialize it with sig = code().
 */

#define	REJECT	1
#define	RESUME	2
#define	RETRY	3
#define	CONTINUE	4

/*
   CatchProc: TYPE = PROC[sig: SIGNAL, arg: UNSPECIFIED, seal: SEAL]
  		RETURNS [control: ControlValue ]
   Catch phrases in this implementation are provided by actual
   procedures.  sig is the signal value that was raised, and arg is
   another value supplied in the SIGNAL call.  seal is described later.
   If the catch procedure returns RESUME, execution will continue after the
   SIGNAL call (there is no enforced analog of ERROR.)  Returning REJECT
   causes the next catch phrase to be sought and invoked.  Returning RETRY or
   CONTINUE causes the stack to be unwound, then a true (CONTINUE) or false
   (RETRY) return from the ENABLE for this seal (see below.)
   When the stack is unwound, catch procedures for UNWIND are
   called, as in Mesa.

   The control codes returned by UNWIND catch procedures are ignored.
   ANY will catch any signal, including an UNWIND.
   A catch phrase for ANY will not catch its own unwind -- same as Mesa.
   I don't know what happens when signals are raised within catch
   procedures, etc.
 */

struct Seal {
  int slauth;
  int slid;
  };
  
struct Seal1 {
	int slauth;
	int slid;
	int data[1];
	};
#define lenSeal1 (sizeof(struct Seal1)/2)

struct Seal2 {
	int slauth;
	int slid;
	int data[2];
	};
#define lenSeal2 (sizeof(struct Seal2)/2)

#define	lenseal	4

/*
  The client must supply a pointer to a two-word (or larger) block
  as a seal.  The signaller stores values in the seal that allows it
  to verify the seal's validity and identify the specific catch phrase
  instance later, when the signal is raised.  This is done so that
  the client does not have to explicitly disable each ENABLE when
  leaving the scope of the ENABLE.  Roughly speaking, the ENABLE
  is in effect until:
		The client calls DISABLE, specifying the same seal.
		The client calles ENABLE again, specifying the same seal.
		The client procedure containing the ENABLE returns.

  The seal is supplied to the catch procedure so that, if the client
  knows something about the seal that the signaller doesn't know
  (like what's in words 2..n, or where the seal is located within
  the local frame), the catch proc can gain some sort of access to
  the ENABLE's context.  Oh, for true nested scopes! Catch
  phrase procs are called, until accepted, in reverse temporal order
  of ENABLE within still-valid frames.  There can be several ENABLEs
  in effect for each local frame -- the frame orders must not be
  rearranged while the ENABLES are valid. When the client calls
  ENABLE, it returns false.  Later, when a catch procedure corresponding
  to that ENABLE's seal returns CONTINUE, it appears to the client
  as if the same ENABLE has just returned true.  If the catch
  procedure specifies RETRY, the client sees another false return
  from the ENABLE.  So a typical use might be:

  struct seal myseal;

  int catcher(sig, code, seal) 
    int sig, code;
    struct seal *sl;
    {
    if (code==unbound) return (CONTINUE);
    else return (REJECT);
    };

  main() {
    if (enable(callfailed, catcher, &myseal)) return;
    xxx;
    };

 Two situations to avoid:
 1) Scope lasts too long. Suppose that procedure P enabled signal
   S using seal L, then called Q, which returned normally.  Suppose
   that subsequently P is unprepared to handle S using that catch
   procedure.  P must explicitly DISABLE L.

 2) Invalid scope appears valid: Suppose that procedure P enabled
   signal S using seal L, did some work, then returned.   Suppose
   further that procedure P is then called again, and its local
   frame ends up in the same place (likely, if P is called again
   by its previous caller.)  Finally, suppose that P or one of its
   called procedures raises S before P reaches the enable of S
   sealed by L.  The seal will still appear valid, and the catch
   procedure will be called, possibly at a time that P cannot deal
   with. So if you enable a signal after some point in your procedure
   that could cause it to be raised, DISABLE the seal on entry to the
   procedure.  (Setting either word of the seal to zero will also
   disable it -- oh, for inlines!)
 */

/* procedures

  sigginit()
	Initialize the signaller package

  siginit(svec, sveclen, gvproc)
	svec is a block of storage of length sveclen bytes, it
	will be used for saving enable information.
	There should be a separate call on siginit from each
	process which uses signals.  gvproc should be tghe same in
	each case.  It is a procedure that the signaller will use
	to aquire the apropriate svec for the running process.

  int enable(s, p, sl)
	sl is a block of length lenseal for the signallers use.
	p is the procedure to be called when the signal is raised
	s is the signal value, aquired from code() perhaps.

	enable returns false on the initial call and on RETRY
	enable returns true on CONTINUE

  disable(sl)
	disable the signal corresponding to seal sl

  signal(s, c)
	raise signal s with argument c

  int code()
	acquire a unique signal value
*/