/* $Id: status.c,v 1.1 2001-10-17 18:00:20 eddy Exp $ */
#include "status.h"

/* A prior declaration and a tool: */
static status *Fenris(status *self);
/* Fenris special-codes its call to valid so that Fenris(NULL) == Ragnarok
   with one side-effect: it ensures Ragnarok contains its original value. */

status *valid(status *self) {
  if (self == NULL) return Fenris(NULL);	/* Waken the Kraken */
  else {
    status *up = self->up;

    if (up == self || up == NULL) {
      /* self was marked invalid already

	 You are always supposed to set a status' up to something sensible
	 before passing it to a judge.  This, in particular, means checking its
	 ->up after every time you pass your status down to any function call,
	 including a judge.

	 To respond unhappy, we need something other than self: and self->up
	 won't find us one !  This is such a bad error (contempt of court) that
	 we don't even try to be lenient (we don't know what values for
	 self->how are unsafe for us to call - they may be calling valid,
	 ultimately - so we can't even ask it for a result to use, even if it
	 does give us something other than self).

      */
      status *fatal = Fenris(NULL);
      /* but we can mend it's ->how, so we may as well */
      if (self->how == NULL) self->how = &valid;
      /* fatal->up isn't fatal, even if it was before we called Fenris. */
      if (self == fatal) return self->up;
      return self->up = fatal;
    }
    /* Those were the deadly sins.  The rest are forgivable.

       If anything else is wrong, fix it and mark self invalid,
       returning self->up.  If all's well, return self. */

    /* If up points down, mark it invalid, thereby fixing it. */
    if (up->up == self) up->up = up;
    else
      /* do no messing, return Happy */
      if (self->how && up->how) return self;

    /* Fix each->how, marking each invalid in the process. */

    if (up->how == NULL) {
      up->how = &valid;
      up->up = up;
    }

    /* The remaining piece of mopping up, then failure. */
    if (self->how == NULL) {
      self->how = &valid;
      self->up = self;
    }

    /* Unhappy, but marked as invalid and `mended' as best we can. */
    return up;
  }
}
/* The trunk of The One Tree */
static status _Ygdrasil = { &valid, NULL };
/* Prior declaration of its Nemesis */
static status *Ragnarok;

/* A contagiously lethal judge. */

static status *Fenris(status *self) {
  /* This makes sure you will die ! */
  status *run, *check = (self) ? valid(self) : Ragnarok;
  /* Fenris reminds Ragnarok what it's about, just in case it's been messed ! */
  Ragnarok->up = &_Ygdrasil;
  Ragnarok->how = &Fenris;
  /* That's all Fenris(NULL) is going to do, aside from returning */

  if (check == self && (check = self->up) == &_Ygdrasil) {
    /* This Is It. */
    extern void _nowireallydie(void);
    /* This won't return. */
    _nowireallydie();
  }
  /* if ->up isn't Ygdrasil, we return it, signaling `self is bad news'
     but we mess with its ->up chain first ... */

  if (check != Ragnarok) {
    /* Assume self is proper, so it and its ->up have normal judges:
       blat all the way up the chain everywhere you find them. */
    if (self->how != &Fenris) {
      for (run = self->up; run->up && run->up != run; run = run->up)
	if (run->how == self->how) run->how = &Fenris;
      self->how = &Fenris;
    }
    if (self->up->how != &Fenris) {
      for (run = self->up->up; run->up && run->up != run; run = run->up)
	if (run->how == self->up->how) run->how = &Fenris;
      self->up->how = &Fenris;
    }
    /* Flag deep doodoo */
    self->up = (self->up == Ragnarok) ? self : Ragnarok;
  }

  return check;
}
/* The eater of life looks up at the one tree and yearns to destroy it */
static status _Ragnarok = { &Fenris, &_Ygdrasil }, *Ragnarok = &_Ragnarok;

static status *croak(status *self) {
  /* This is the default ->how of the last thing before Ragnarok.

     If we're about to die, print a message saying we did come through the
     proper channel, just before doing it.

   */
  status *check = valid(self);
  if (check == self && (check = self->up)->how == &Fenris) {
    /* Do actual croaking: */
    extern void _ohshitimabouttodie(void);
    _ohshitimabouttodie();
    /* Do not press this button again. */
    self->how = &Fenris;
  }

  return check;
}
static status _OhShitImAboutToDie = { &croak, &_Ragnarok };

status *ready(status *self) {
  /* This will typically be used as a checker preamble by self->up->how. */
  return (*(_Ygdrasil.how))(self);
}

/* Not sure if this is useful. */
static status *prior(status *self) {
  /* Returns a status, in the ->up chain, whose ->how differs from self's. */
  status *check = ready(self);
  if (check != self) return check;
  else {
    status *run = self->up;
    while (run->how == self->how)
      if (run->up && run->up != run) run = run->up;
      else {
	/* mark self invalid, return run with same how */
	self->up = self;
	return run;
      }
    /* return run with a different how from self */
    return run;
  }
}
static status _Prior = { &prior, &_OhShitImAboutToDie };

static status *pull(status *self) {
  status *check = ready(self);
  if (check != self) return check;
  if (self->up->how == &pull) {
    if (self->how == &pull) return self;
    self->up->how = self->how;
    return self->up;
  }
  /* this one might change ? */
  return self->up;
}
static status _Winch = { &pull, &_Prior };

/* Throwing and catching.

   This is easy.  */

static status *uncaught(status *self) {
  /* This is what we raise if we fail to catch something ...
     it is, incidentally, a proper judge and a matcher for itself.
  */
  status *check = ready(self);
  if (check != self) return check;
  /* Unexpected: */
  if (self->how != &uncaught) return self->up;
  /* Second call: */
  if (self->up->how == &uncaught) return self;
  /* (it would be more usual for a self-catcher to frob self->up->up on catching
     itself, to tell the catcher that it did catch - but uncaught's catcher might
     be the static _unCaught below, so it doesn't) */

  /* First call: you shouldn't be throwing this ! */
  self->up->how = &uncaught;
  self->up->up = self->up;
  /* but we catch it anyway. */
  return self;
}
static status _unCaught = { &uncaught, &_Winch };

static status *_catch(status *self) {
  status *check = ready(self);
  if (check == self) {
      status *run = check = self->up;
      while (run->up && run->up != run)
	if (run->how == catch && run->up->how != self->up->how) {
	  status *matcher = run->up, *try = (*(matcher->how))(self);
	  if (try == self) {
	    /* matcher is interested in what we're throwing */
	    self->up = matcher;
	    try = (*(matcher->how))(self);

	    /* If it matched, return. */
	    if (self->up == matcher) return try;

	    /* matcher lost interest: reset and carry on */
	    self->up = check;
	  }
	  run = matcher->up;
	} else run = run->up;

      /* Flag failure ! */
      self->up = self;
      check->how = &uncaught;
    }
  return check;
}
judge catch = &_catch;
static status _Catch = { &_catch, &_unCaught };
/* Note that _Catch is effectively a catcher for uncaught ... */
static status _Pull = { &pull, &_Catch };
status *Pull = &_Pull;

/*
  $Log: status.c,v $
  Revision 1.1  2001-10-17 18:00:20  eddy
  added in all files previously missing from RCS

  Revision 1.11  1998/12/31 13:29:31  eddy
  Made it compile.

  Revision 1.10  1998/12/29 17:34:20  eddy
  Renamed to status, ditched Ready, made ready an export,
  implemented (throwing and) catching, added Pull.

  Revision 1.9  1998/12/22 15:42:58  eddy
  Looks good enough for the task in hand.

  Revision 1.8  1998/12/19 18:02:53  eddy
  Major revolution, peppered from Norse myth.

  Revision 1.7  1998/12/16 01:23:27  eddy
  Reinvented check, simplified Death and Ready.

  Revision 1.6  1998/12/16 00:19:42  eddy
  That's more like it.

  Revision 1.5  1998/12/15 21:45:15  eddy
  Tuesday dinner-break.  Revolution coming.

  Revision 1.4  1998/12/13 21:44:43  eddy
  Packing up for the night.

  Revision 1.3  1998/12/13 21:12:37  eddy
  Sunday evening.

  Revision 1.2  1998/12/13 09:46:02  eddy
  Start of major re-write around The Trinity.

  Initial Revision 1.1  1998/12/12 11:42:24  eddy
*/
