/* $Header: sanity.c,v 1.1 90/05/05 12:48:53 lachesis Exp $
 * $Log:    sanity.c,v $
 * Revision 1.1  90/05/05  12:48:53  lachesis
 * Initial revision
 *
 */
#include "os.h"
#include "copyright.h"
#include "db.h"

extern dbref recyclable;

void violate (dbref i, const char *s, const char *why)
{
  printf ("Object #%d violates %s rules: %s\n", i, s, why);
  db_write_object (stdout, i);
}

int recursive_location (dbref what, dbref loc)
{
  if (loc == NOTHING)
    return 0;
  if (what == loc)
    return 1;
  if (recursive_location (what, db[loc].location))
    return 1;
  return 0;
}

int recursive_list (dbref what, dbref next)
{
  if (next == NOTHING)
    return 0;
  if (what == next)
    return 1;
  if (recursive_list (what, db[next].next))
    return 1;
  return 0;
}

void check_common (dbref obj)
{
  int i;

  /* check location */
  if (db[obj].location >= db_top)
    violate (obj, "location", "out of range");
  if (recursive_location (obj, db[obj].location))
    violate (obj, "location", "recursive");
  /* check contents */
  if (recursive_list (db[obj].contents, HOME))
    violate (obj, "contents", "recursive");
  for (i = db[obj].contents; i < db_top && i != NOTHING; i = db[i].next);
  if (i != NOTHING)
    violate (obj, "contents", "garbage at end");
}

void check_room (dbref obj)
{
  dbref i;

  if (db[obj].sp.room.dropto >= db_top
    || (((db[db[obj].sp.room.dropto].flags & TYPE_MASK) != TYPE_ROOM)
      && db[obj].sp.room.dropto != NOTHING && db[obj].sp.room.dropto != HOME))
    violate (obj, "dropto", "garbage or not a room");

  if (recursive_list (db[obj].contents, HOME))
    violate (obj, "contents", "recursive");
  if (recursive_list (db[obj].sp.room.exits, HOME))
    violate (obj, "exits", "recursive");
  for (i = db[obj].sp.room.exits; i != NOTHING && i < db_top; i = db[i].next)
    if ((db[i].flags & TYPE_MASK) != TYPE_EXIT)
      violate (i, "exits", "non-exit in exit list");
  if (i != NOTHING)
    violate (obj, "exits", "garbage at end");

  if (db[obj].sp.room.owner >= db_top
    || ((db[db[obj].sp.room.owner].flags & TYPE_MASK) != TYPE_PLAYER))
    violate (obj, "owner", "garbage or not a player");
}

void check_thing (dbref obj)
{
  dbref i;

  if (db[obj].sp.thing.home >= db_top
    || ((db[db[obj].sp.thing.home].flags & TYPE_MASK) != TYPE_ROOM))
    violate (obj, "home", "garbage or not a room");
  if (recursive_list (db[obj].contents, HOME))
    violate (obj, "contents", "recursive");
  for (i = db[obj].sp.thing.actions;
    i < db_top && i != NOTHING; i = db[i].next)
    if ((db[i].flags & TYPE_MASK) != TYPE_EXIT)
      violate (i, "actions", "non-action in action list");

  if (i != NOTHING)
    violate (obj, "actions", "garbage at end");

  if (db[obj].sp.thing.owner >= db_top
    || ((db[db[obj].sp.thing.owner].flags & TYPE_MASK) != TYPE_PLAYER))
    violate (obj, "owner", "garbage or not a player");
}

void check_exit (dbref obj)
{
  int i;

  for (i = 0; i < db[obj].sp.exit.ndest; i++)
    if (db[obj].sp.exit.dest[i] >= db_top)
      violate (obj, "destination", "garbage");

  if (db[obj].sp.exit.owner >= db_top
    || ((db[db[obj].sp.exit.owner].flags & TYPE_MASK) != TYPE_PLAYER))
    violate (obj, "owner", "garbage or not a player");
}

void check_player (dbref obj)
{
  dbref i;

  if (db[obj].sp.player.home >= db_top
    || ((db[db[obj].sp.player.home].flags & TYPE_MASK) != TYPE_ROOM))
    violate (obj, "home", "garbage or not a room");

  if (recursive_list (db[obj].sp.player.actions, HOME))
    violate (obj, "actions", "recursive");
  for (i = db[obj].sp.player.actions;
    i < db_top && i != NOTHING; i = db[i].next)
    if ((db[i].flags & TYPE_MASK) != TYPE_EXIT)
      violate (obj, "actions", "non-action in action list");
  if (i != NOTHING)
    violate (obj, "actions", "garbage at end");
}

void check_garbage (void)
{
  dbref i;

  if (recursive_list (recyclable, HOME))
    fprintf (stderr, "Garbage free list is recursive!\n");
  for (i = recyclable; i != NOTHING && i < db_top; i = db[i].next)
    if (Typeof (i) != TYPE_GARBAGE)
      violate (i, "garbage", "not TYPE_GARBAGE");
  if (i != NOTHING)
    fprintf (stderr,
      "Garbage at end of garbage free list!  (no puns please)\n");
}

int main (int argc, char **argv)
{
  int i;

  db_read (stdin);

  printf ("dbtop = %d\n", db_top);

  for (i = 0; i < db_top; i++) {
#ifdef DEBUG
    fprintf (stderr, "Checking object %d..\n", i);
#endif
    check_common (i);
    switch (db[i].flags & TYPE_MASK) {
    case TYPE_ROOM:
      check_room (i);
      break;
    case TYPE_THING:
      check_thing (i);
      break;
    case TYPE_EXIT:
      check_exit (i);
      break;
    case TYPE_PLAYER:
      check_player (i);
      break;
    case TYPE_GARBAGE:
      break;
    default:
      violate (i, "type", "bad type");
      break;
    }
  }
  check_garbage ();
  return 0;
}

void clear_players (dbref i)
{
}

void add_player (dbref i)
{
}