/* $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 "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");
}

void
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();
}

void clear_players(dbref i)
{
}

void add_player(dbref i)
{
}