/* signaller.c
  L. Stewart August 24, 1982  12:05 PM
  L. Stewart November 8, 1982  3:25 PM, osstatics
 */

#include	<Signal.h>
#include	<Env.h>
#include	<Ec.h>

extern MyFrame();
extern CallSwat();
extern CallersFrame();
extern ReturnLoc();
extern Call0();
extern Apply();
extern Ugt();
extern ByteBlt();

struct enab {
  int ensignal;
  int enproc;
  int enframe;
  int enenframe;
  int enid;
  int encontinue;
  struct Seal *enseal;
  };

#define	lenenab	14

struct sigvec {
  int svused;
  int svmaxEnabled;
  struct enab sven[1];
  };

#define	lensigvec	4

#define	sealseal	0132576

extern int	getsv;
extern int	sigid;
extern int	signame;

SigInit(svec, sveclen, gvproc)
  struct sigvec *svec;
  int sveclen;
  int gvproc;
  {
  sigid = 0;
  signame = 037777;
  getsv = gvproc;
  svec->svused = 0;
  svec->svmaxEnabled = (sveclen-lensigvec)/lenenab;
  };

int Enable(s, p, sl)
  int s, p;
  struct Seal *sl;
  {
  return(EnableWithFrame(s, p, sl, MyFrame()));
  };

int EnableWithFrame(s, p, sl, f)
  int s;		/* signal */
  int p;		/* proc to call */
  struct Seal *sl;
  int f;
  {
  struct sigvec *sv;
  struct enab *en;
  sv = Call0(getsv);
  if (sv->svused == sv->svmaxEnabled) {
    if (purgesv() >= sv->svmaxEnabled) CallSwat(ecSignal+1);
    };
  en = &sv->sven[sv->svused];
  sv->svused += 1;
  en->ensignal = s;
  en->enproc = p;
  en->enenframe = f;
  en->enframe = CallersFrame(f);
  en->enseal = sl;
  en->encontinue = ReturnLoc(f);
  sigid += 1;
  en->enid = sigid;
  sl->slauth = sealseal;
  sl->slid = sigid;
  return(0);
  };

Disable(sl)
  struct Seal *sl;
  {
  sl->slauth = 0;
  };

Signal(s, c)
  int s;		/* Signal */
  int c;		/* unspecified */
  {
  struct enab *en;
  struct sigvec *sv;
  int enindex;
  int enabres;
  int ent;
  int result;
  int argv[3];
  sv = Call0(getsv);
  enindex = purgesv();
  for (;;) {
    enabres = true;
    en = finden(s, &enindex);
    if (en==0) CallSwat(ecSignal+2);
    argv[0] = s;
    argv[1] = c;
    argv[2] = (int) en->enseal;
    result = Apply(argv, en->enproc, 3);
    switch (result) {
      case REJECT: continue;	/* look for next catch phrase */
      case RESUME: return;
      case RETRY: enabres = false;
      case CONTINUE:
        unwind(enindex, enabres);
        break;
      default: CallSwat(ecSignal+3);
      };
    };
  };

int Code()
  {
  signame += 1;
  return(signame);
  };

static int purgesv()
  {
  int f, prevf, invalid;
  int enf, dest, i;
  struct sigvec *sv;
  struct enab *en;
  struct Seal *sl;
  sv = Call0(getsv);
  f = CallersFrame(MyFrame());
  prevf = 0;
  invalid = false;
  for (i = (sv->svused) - 1; i >= 0; i -= 1) {
    en = &sv->sven[i];
    sl = en->enseal;
    if ((sl->slauth != sealseal) || (sl->slid != en->enid)) {
      en->enid = 0;
      invalid = true;
      continue;
      };
    enf = en->enframe;
    while (Ugt(enf, f)) f = CallersFrame(f);
    if (Ugt(prevf, enf) || Ugt(f, enf)) {
      en->enid = 0;
      invalid = true;
      };
    else prevf = f;
    };
  dest = 0;
  if (invalid) {
    for (i=0; i < sv->svused; i+= 1) {
      en = &sv->sven[i];
      if (en->enid == 0) continue;  /* invalid */
      if (dest != i) {
        ByteBlt(&sv->sven[dest], &sv->sven[i], lenenab);
        };
      dest += 1;
      };
    sv->svused = dest;
    };
  return (sv->svused);
  };

static struct enab *finden(s, lvindex)
  int s, *lvindex;
  {
  struct sigvec *sv;
  struct enab *en;
  int i;
  sv = Call0(getsv);
  for (i = (*lvindex) - 1; i >= 0; i -= 1) {
    en = &sv->sven[i];
    if ((en->ensignal == s) || (en->ensignal == ANY)) {
      *lvindex = i;
      return (en);
      };
    };
  *lvindex = (-1);
  return (0);
  };

static unwind(toenindex, result)
  int toenindex, result;
  {
  struct sigvec *sv;
  struct enab *toen, *en;
  int id, index, toframe, toenframe, prevframe, nextframe;
  int argv[3];

  sv = Call0(getsv);
  toen = &sv->sven[toenindex];
  id = toen->enid;
  index = purgesv();
  /* purgesv should not invalidate toen */
  if ((index<=toenindex) || (toen->enid != id)) CallSwat(ecSignal+4);
  toframe = toen->enframe;
  toenframe = toen->enenframe;
  prevframe = MyFrame();
  en = finden(UNWIND, &index);
  for (;;) {
    nextframe = CallersFrame(prevframe);
    while ((index>toenindex) && (en->enframe == nextframe)) {
      argv[0] = UNWIND;
      argv[1] = 0;
      argv[2] = (int) en->enseal;
      Apply(argv, en->enproc, 3);
      en = finden(UNWIND, &index);
      };
    if (nextframe==toframe) break;
    prevframe = nextframe;
    };
  returnto(toenframe, toframe, toen->encontinue, result);
  };