dmuck0.15-beta/docs/muf/
dmuck0.15-beta/game/
dmuck0.15-beta/game/logs/
dmuck0.15-beta/game/muf/
dmuck0.15-beta/game/muf/text/
#include "copyright.h"
#include "config.h"

#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "db.h"
#include "params.h"
#include "interface.h"
#include "externs.h"

static char garbagebuf[BUFFER_LEN];

object *db = 0;
dbref db_top = 0;
dbref compost_heap = NOTHING;
static char buf[BUFSIZ];

#ifndef DB_INITIAL_SIZE
#define DB_INITIAL_SIZE 1000
#endif /* DB_INITIAL_SIZE */

#define COUNTER_MAX 10000
#define VALIDATE_LISTS

#ifdef DB_DOUBLING
dbref db_size = DB_INITIAL_SIZE;
#endif /* DB_DOUBLING */

macrotable *macrotop;

extern char *dup_string(char *);
int number(char *s);
extern char *crypt(char *key, char *salt);

/* returns 0 if password matches */
int check_password(char *plaintext, dbref player)
{
   char salt[3];

   salt[0] = DBFETCH(player)->sp.player.password[0];
   salt[1] = DBFETCH(player)->sp.player.password[1];
   salt[2] = '\0';

   return(strcmp(DBFETCH(player)->sp.player.password,crypt(plaintext,salt)));
}

char *make_password(char *plaintext)
{
   static char salt_chars[] =
     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890./";
   char salt[3];
   char *tmp;

   if (!plaintext) return NULL;
   salt[0] = salt_chars[random() % 64];
   salt[1] = salt_chars[random() % 64];
   salt[2] = '\0';

   tmp = (char *)malloc(sizeof(char)*14);
   strcpy(tmp,crypt(plaintext,salt));
   free(plaintext);
   return(tmp);
}

void free_line(line *l)
{
  if (l->this_line) free((void *) l->this_line);
  free((void *) l);
}

void free_prog_text(line *l)
{
  line *next;

  while (l)
  {
    next = l->next;
    free_line(l);
    l = next;
  }
}

#ifdef DB_DOUBLING
static void db_grow(dbref newtop)
{
  object *newdb;
  
  if(newtop > db_top)
  {
    db_top = newtop;
    if(!db)
    {
      /* make the initial one */
      db_size = DB_INITIAL_SIZE;
      if((db = (object *)malloc(db_size * sizeof(object))) == 0) abort();
    }
    
    /* maybe grow it */
    if(db_top > db_size)
    {
      /* make sure it's big enough */
      while(db_top > db_size) db_size *= 2;
      if((newdb = (object *)realloc((void *) db,
        db_size * sizeof(object))) == 0) abort();
      db = newdb;
    }
  }
}

#else /* DB_DOUBLING */

static void db_grow(dbref newtop)
{
  object *newdb;
  
  if(newtop > db_top)
  {
    db_top = newtop;
    if(db)
    {
      if((newdb = (object *)
        realloc((void *) db,
        db_top * sizeof(object))) == 0)
        abort();
      db = newdb;
    }
    else
    {
      /* make the initial one */
      if((db = (object *)
        malloc(DB_INITIAL_SIZE * sizeof(object))) == 0)
        abort();
    }
  }
}
#endif /* DB_DOUBLING */

void db_clear_object(dbref i)
{
  DBSTORE(i, name, NULL);
#ifndef USE_DBP_STR
  DBSTORE(i, desc, NULL);
  DBSTORE(i, succ, NULL);
  DBSTORE(i, fail, NULL);
  DBSTORE(i, drop, NULL);
  DBSTORE(i, osucc, NULL);
  DBSTORE(i, ofail, NULL);
  DBSTORE(i, odrop, NULL);
#endif
  DBSTOREPROP(i, NULL);
  DBSTORE(i, backlinks, NULL);
  DBSTORE(i, backlocks, NULL);
  DBSTORE(i, exits, NOTHING);
  DBSTORE(i, location, NOTHING);
  DBSTORE(i, contents, NOTHING);
  DBSTORE(i, next, NOTHING);
  DBSTORE(i, nextowned, NOTHING);
  DBSTORE(i, key, TRUE_BOOLEXP);
  DBSTORE(i, link, NOTHING);
  DBSTORE(i, pennies, 0);
#ifdef TIMESTAMPS
  DBSTORE(i, time_created, time(NULL));
  DBSTORE(i, time_modified, DBFETCH(i)->time_created);
  DBSTORE(i, time_used, DBFETCH(i)->time_created);
#endif
}

void add_compost(dbref d)
{
  dbref scan;
  if ((compost_heap == NOTHING) || (d < compost_heap))
  {
    /* insert at head of list */
    DBSTORE(d, next, compost_heap);
    compost_heap = d;
  }
  else
  {
    for(scan = compost_heap; (DBFETCH(scan)->next != NOTHING) &&
      (DBFETCH(scan)->next < d); scan = DBFETCH(scan)->next);
    DBSTORE(d, next, DBFETCH(scan)->next);
    DBSTORE(scan, next, d);
  }
}

dbref new_object()
{
  dbref newobj;
  
  if(compost_heap != NOTHING)
  {
    newobj = compost_heap;
    compost_heap = DBFETCH(newobj)->next;
  }
  else
  {
    newobj = db_top;
    db_grow(db_top + 1);
  }
  
  /* clear it out */
  db_clear_object(newobj);

#ifdef USE_DBP
  dbp_load(newobj);
#endif

  return newobj;
}

#define DB_MSGLEN 512

void putref(FILE *f, dbref ref)
{
  fprintf(f, "%ld\n", ref);
}

static void putstring(FILE *f, char *s)
{
  if(s) fputs(s, f);
  putc('\n', f);
}

static void putbool_subexp(FILE *f, boolexp *b)
{
  switch(b->type)
  {
    case BOOLEXP_AND:
      putc('(', f);
      putbool_subexp(f, b->sub1);
      putc(AND_TOKEN, f);
      putbool_subexp(f, b->sub2);
      putc(')', f);
      break;
    case BOOLEXP_OR:
      putc('(', f);
      putbool_subexp(f, b->sub1);
      putc(OR_TOKEN, f);
      putbool_subexp(f, b->sub2);
      putc(')', f);
      break;
    case BOOLEXP_NOT:
      putc('(', f);
      putc(NOT_TOKEN, f);
      putbool_subexp(f, b->sub1);
      putc(')', f);
      break;
    case BOOLEXP_CONST:
      fprintf(f, "%ld", b->thing);
      break;
    case BOOLEXP_PROP:
      fprintf(f, "[%s:%s]", b->prop_name, b->prop_data);
      break;
    default:
      break;
  }
}

void putboolexp(FILE *f, boolexp *b)
{
  if(b != TRUE_BOOLEXP) putbool_subexp(f, b);
  putc('\n', f);
}

void macrodump(macrotable *node, FILE *f)
{
  if (!node) return;
  macrodump(node->left, f);
  putstring(f, node->name);
  putstring(f, node->definition);
  putref(f, node->implementor);
  macrodump(node->right, f);
}

char *file_line(FILE *f)
{
  if (!fgets(buf, BUFSIZ, f)) return NULL;
  buf[strlen(buf) - 1] = '\0';
  return dup_string(buf);
}

void foldtree (macrotable *center)
{
  int count = 0;
  macrotable *nextcent = center;
  
  for (; nextcent; nextcent = nextcent->left) count++;
  if (count > 1)
  {
    for (nextcent = center, count /= 2; count--; nextcent = nextcent->left);
    if (center->left) center->left->right = NULL;
    center->left = nextcent;
    foldtree(center->left);
  }
  for (count = 0, nextcent = center; nextcent; nextcent = nextcent->right)
    count++;
  if (count > 1)
  {
    for (nextcent = center, count /= 2; count--; nextcent = nextcent->right);
    if (center->right) center->right->left = NULL;
    foldtree(center->right);
  }
}

int macrochain(macrotable *lastnode, FILE *f)
{
  char *line1, *line2;
  macrotable *newmacro;
  
  if (!(line1 = file_line(f))) return 0;
  line2 = file_line(f);
  newmacro = (macrotable *)new_macro(line1, line2, getref(f));
  /* file_line returns malloc'd strings */
  /* new_macro dup_strings it's arguments, so we need to free */
  if (line1)
    free(line1);
  if (line2)
    free(line2);
  if (!macrotop) macrotop = (macrotable *)newmacro;
  else
  {
    newmacro->left = lastnode;
    lastnode->right = newmacro;
  }
  return (1 + macrochain(newmacro, f));
}

void macroload(FILE *f)
{
  int count = 0;
  
  macrotop = NULL;
  count = macrochain(macrotop, f);
  fclose(f);
  for (count /= 2; count--; macrotop = macrotop->right);
  foldtree(macrotop);
  return;
}

void write_program(line *first, dbref i)
{
  FILE *f;
  
  sprintf(buf, "muf/%ld.m", i);
  f = fopen(buf, "w");
  if (!f)
  {
    log_status("Couldn't open file %s!\n", buf);
    return;
  }
  
  while (first)
  {
    if (!first->this_line) continue;
    fputs(first->this_line, f);
    fputc('\n', f);
    first = first->next;
  }
  fclose(f);
}

int db_write_object(FILE *f, dbref i)
{
  int j;
  object *o = DBFETCH(i);
  
#ifndef STRINGS_ONLY
  putstring(f, NAME(i));
#endif
#ifndef USE_DBP_STR
  putstring(f, o->desc);
#else
  putc('\n', f);
#endif
#ifndef STRINGS_ONLY
  putref(f, o->location);
  putref(f, o->contents);
  putref(f, o->link);
  putref(f, o->exits);
  putref(f, o->pennies);
  putref(f, o->next);
  putref(f, o->nextowned);
  putref(f, o->owner);
  putboolexp(f, o->key);
#endif
#ifndef USE_DBP_STR
  putstring(f, o->fail);
  putstring(f, o->succ);
  putstring(f, o->drop);
  putstring(f, o->ofail);
  putstring(f, o->osucc);
  putstring(f, o->odrop);
#else
  putc('\n', f);
  putc('\n', f);
  putc('\n', f);
  putc('\n', f);
  putc('\n', f);
  putc('\n', f);
#endif /* USE_DBP_STR */
#ifndef STRINGS_ONLY
  putref(f, (FLAGS(i) & ~INTERACTIVE));
#ifdef TIMESTAMPS
  putref(f, o->time_created);
  putref(f, o->time_modified);
  putref(f, o->time_used);
#else
  putref(f, 0);
  putref(f, 0);
  putref(f, 0);
#endif
  putstring(f, "*backlinks*");
  dbreflist_dump(f, o->backlinks);
  putstring(f, "*end backlinks*");
  putstring(f, "*backlocks*");
  dbreflist_dump(f, o->backlocks);
  putstring(f, "*end backlocks*");
  putstring(f, "***Property list start ***");
#endif /* STRINGS_ONLY */

#ifndef USE_DBP
  putproperties(f, i);
#endif

#ifndef STRINGS_ONLY
  putstring(f, "***Property list end ***");
  
  switch (Typeof(i))
  {
    case TYPE_EXIT:
      putref(f, o->sp.exit.ndest);
      for (j = 0; j < o->sp.exit.ndest; j++) putref(f, (o->sp.exit.dest)[j]);
      break;
    case TYPE_PLAYER:
      putstring(f, o->sp.player.password);
  }
#endif  /* STRINGS_ONLY */
  return 0;
}

/* what a hack!  Let people write lachesis style dumps... */

int db_write_object_lachesis(FILE *f, dbref i)
{
  object *o = DBFETCH(i);
  int  j;
  
  putstring(f, NAME(i));
#ifndef USE_DBP_STR
  putstring(f, o->desc);
#else
  putstring(f, get_property_data(i, "desc", ACCESS_WI));
#endif
  putref(f, o->location);
  putref(f, o->contents);
  putref(f, o->next);
  putboolexp(f, o->key);
#ifndef USE_DBP_STR
  putstring(f, o->fail);
  putstring(f, o->succ);
  putstring(f, o->drop);
  putstring(f, o->ofail);
  putstring(f, o->osucc);
  putstring(f, o->odrop);
#else
  putstring(f, get_property_data(i, "fail", ACCESS_WI));
  putstring(f, get_property_data(i, "succ", ACCESS_WI));
  putstring(f, get_property_data(i, "drop", ACCESS_WI));
  putstring(f, get_property_data(i, "ofail", ACCESS_WI));
  putstring(f, get_property_data(i, "osucc", ACCESS_WI));
  putstring(f, get_property_data(i, "odrop", ACCESS_WI));
#endif
  putref(f, (FLAGS(i) & ~INTERACTIVE));
  putstring(f, "***Property list start ***");
#ifndef USE_DBP
  putproperties(f, i);
#endif
  putstring(f, "***Property list end ***");
  
  switch (Typeof(i))
  {
    case TYPE_THING:
      putref(f, o->link);
      putref(f, o->exits);
      putref(f, OWNER(i));
      putref(f, o->pennies);
      break;
    case TYPE_ROOM:
      putref(f, o->link);
      putref(f, o->exits);
      putref(f, OWNER(i));
      break;
    case TYPE_EXIT:
      putref(f, o->sp.exit.ndest);
      for (j = 0; j < o->sp.exit.ndest; j++) putref(f, (o->sp.exit.dest)[j]);
      putref(f, OWNER(i));
      break;
    case TYPE_PLAYER:
      putref(f, o->link);
      putref(f, o->exits);
      putref(f, o->pennies);
      putstring(f, o->sp.player.password);
      break;
    case TYPE_PROGRAM:
      putref(f, OWNER(i));
      break;
  }
  
  return 0;
}

dbref db_write_lachesis(FILE *f)
{
  dbref i;
  
  fputs("***Lachesis TinyMUCK DUMP Format***\n", f);
  for(i = 0; i < db_top; i++)
  {
    fprintf(f, "#%ld\n", i);
    db_write_object_lachesis(f, i);
  }
  fputs("***END OF DUMP***\n", f);
  fflush(f);
  return(db_top);
}

dbref db_write(FILE *f)
{
  dbref i;
  
  fputs("***MULCH***\n", f);
  for(i = 0; i < db_top; i++)
  {
    fprintf(f, "#%ld\n", i);
    db_write_object(f, i);
  }
  fputs("***END OF DUMP***\n", f);
  fflush(f);
  return(db_top);
}

dbref parse_dbref(char *s)
{
  char *p;
  long x;
  
  x = atol(s);
  if(x > 0) return x;
  else if(x == 0)
  {
    /* check for 0 */
    for(p = s; *p; p++)
    {
      if(*p == '0') return 0;
      if(!isspace(*p)) break;
    }
  }
  
  /* else x < 0 or s != 0 */
  return NOTHING;
}

dbref getref(FILE *f)
{
  fgets(buf, sizeof(buf), f);
  return(atol(buf));
}

static char *getstring_noalloc(FILE *f)
{
  char c;
  
  fgets(buf, sizeof(buf), f);
  if (strlen(buf) == DB_MSGLEN - 1)
  {
    /* ignore whatever comes after */
    if (buf[DB_MSGLEN - 2] != '\n') while ((c = fgetc(f)) != '\n');
  }

  buf[strlen(buf) - 1] = '\0';
  return buf;
}

#define getstring(x) dup_string(getstring_noalloc(x))

#ifdef COMPRESS
#define alloc_compressed(x) dup_string(compress(x))
#define getstring_compress(x) dup_string(compress(getstring_noalloc(x)))
#else
#define alloc_compressed(x) dup_string(x)
#define getstring_compress(x) getstring(x)
#endif /* COMPRESS */

static boolexp *negate_boolexp(boolexp *b)
{
  boolexp *n;
  
  /* Obscure fact: !NOTHING == NOTHING in old-format databases! */
  if(b == TRUE_BOOLEXP) return TRUE_BOOLEXP;
  
  n = (boolexp *) malloc(sizeof(boolexp));
  n->type = BOOLEXP_NOT;
  n->sub1 = b;
  
  return n;
}

/* returns true for numbers of form [ + | - ] <series of digits> */
int number(char *s)
{
  if (!s) return 0;
  while (isspace(*s)) s++;
  if (*s == '+' || *s == '-') s++;
  if (*s < '0' || *s > '9') return 0;
  return 1;
}

static boolexp *getboolexp1(FILE *f)
{
  boolexp *b;
  int c;
  int i;
  
  c = getc(f);
  switch(c)
  {
    case '\n':
      ungetc(c, f);
      return TRUE_BOOLEXP;
    case EOF:
      abort();
      break;
    case '(':
      b = (boolexp *) malloc(sizeof(boolexp));
      if((c = getc(f)) == '!')
      {
        b->type = BOOLEXP_NOT;
        b->sub1 = getboolexp1(f);
        if(getc(f) != ')') goto error;
        return b;
      }
      else
      {
        ungetc(c, f);
        b->sub1 = getboolexp1(f);
        switch(c = getc(f))
	{
          case AND_TOKEN:
            b->type = BOOLEXP_AND;
            break;
          case OR_TOKEN:
            b->type = BOOLEXP_OR;
            break;
          default:
            goto error;
        }
        b->sub2 = getboolexp1(f);
        if(getc(f) != ')') goto error;
        return b;
      }
    case '[':
      /* property type */
      b = (boolexp *)malloc(sizeof(boolexp));
      b->type = BOOLEXP_PROP;
      b->sub1 = b->sub2 = 0;
      for(i = 0; ((c = getc(f)) != PROP_DELIMITER) && (i < BUFSIZ); i++)
        buf[i] = c;
      if (i >= BUFSIZ && (c != PROP_DELIMITER)) goto error;
      buf[i] = '\0';
      b->prop_name = dup_string(buf);
      for (i = 0; (c = getc(f)) != ']'; i++) buf[i] = c;
      buf[i] = '\0';
      if (i >= BUFSIZ && c != ']') goto error;
      b->prop_data = dup_string(buf);
      return b;
    case '-':
    /* uncomment if old format db
        while((c = getc(f)) != '\n') if(c == EOF) abort();
        ungetc(c, f);
        return TRUE_BOOLEXP; */
    default:
      /* better be a dbref */
      ungetc(c, f);
      b = (boolexp *)malloc(sizeof(boolexp));
      b->type = BOOLEXP_CONST;
      b->thing = 0;
    
      i = (c == '-') ? -1 : 1;
      if (c == '-') c = getc(f);
      /* NOTE possibly non-portable code */
      /* Will need to be changed if putref/getref change */
      while(isdigit(c = getc(f)))
      {
        b->thing = b->thing * 10 + c - '0';
      }
      b->thing *= i;
      ungetc(c, f);
      return b;
    }
  error:
  abort();                      /* bomb out */
  return TRUE_BOOLEXP;
}

boolexp *getboolexp(FILE *f)
{
  boolexp *b;
  
  b = getboolexp1(f);
  if(getc(f) != '\n') abort(); /* parse error, we lose */
  return b;
}

void free_boolexp(boolexp *b)
{
  if(b != TRUE_BOOLEXP) {
    switch(b->type)
    {
      case BOOLEXP_AND:
      case BOOLEXP_OR:
        free_boolexp(b->sub1);
        free_boolexp(b->sub2);
        free((void *) b);
        break;
      case BOOLEXP_NOT:
        free_boolexp(b->sub1);
        free((void *) b);
        break;
      case BOOLEXP_CONST:
        free((void *) b);
        break;
      case BOOLEXP_PROP:
        free(b->prop_name);
        free(b->prop_data);
        free(b);
        break;
    }
  }
}

void db_free_object(dbref i)
{
  object *o;
  int j;
  inst *code;
    
  o = DBFETCH(i);
  if(NAME(i)) free(NAME(i));
#ifndef USE_DBP_STR
  if(o->desc) free(o->desc);
  if(o->succ) free(o->succ);
  if(o->fail) free(o->fail);
  if(o->drop) free(o->drop); 
  if(o->ofail) free(o->ofail);
  if(o->osucc) free(o->osucc);
  if(o->odrop) free(o->odrop); 
#endif
  if(o->key) free_boolexp(o->key);
  dbreflist_burn(o->backlinks);
  dbreflist_burn(o->backlocks);
#ifndef USE_DBP
  burn_proptree(o->properties);
#else
  DBFETCHPROP(i);
  burn_proptree(o->properties);
  o->properties = NULL;
#endif
  switch (Typeof(i))
  {
    case TYPE_EXIT:
      if (o->sp.exit.dest) free (o->sp.exit.dest);
      break;
    case TYPE_PLAYER:
      if (o->sp.player.password) free (o->sp.player.password);
      break;
    case TYPE_DAEMON:
      break;
    case TYPE_PROGRAM:
      dbreflist_burn(DBFETCH(i)->sp.program.editlocks);
      code = o->sp.program.code;
      if (code)
      {
        for (j = 0; j < o->sp.program.siz; j++)
        if (code[j].type == PROG_STRING && code[j].data.string)
          free((void *) code[j].data.string);
        free((void *) code);
      }
  }
  DBDIRTY(i);
}

void db_free()
{
  dbref i;
  
  if(db)
  {
    for(i = 0; i < db_top; i++) db_free_object(i);
    free((void *) db);
    db = 0;
    db_top = 0;
  }
  clear_players();
  clear_primitives();
  compost_heap = NOTHING;
}

line *get_new_line()
{
  line *new;
  
  new = (line *)malloc(sizeof(line));
  new->this_line = NULL;
  new->next = NULL;
  new->prev = NULL;
  return new;
}

line *read_program(dbref i)
{
  line *first, *prev, *new;
  FILE  *f;
  
  first = NULL;
  sprintf(buf, "muf/%ld.m", i);
  f = fopen(buf, "r");
  if (!f) return 0;
  
  while (fgets(buf, BUFSIZ, f))
    {
      new = get_new_line();
      buf[strlen(buf) - 1] = '\0';
      new->this_line = dup_string(buf);
      if (!first)
        first = prev = new;
      else
        {
          prev->next = new;
          new->prev = prev;
          prev = new;
        }
    }
  
  fclose(f);
  return first;
}

void db_read_object_old(FILE *f, object *o, dbref objno)
{
  dbref exits;
  int pennies;
  char *password, *gender;
  
  NAME(objno) = getstring(f);
#ifndef USE_DBP_STR
  o->desc = getstring_compress(f);
#endif
  o->location = getref(f);
  o->contents = getref(f);
  exits = getref(f);
  o->backlinks = NULL;
  o->backlocks = NULL;
  o->next = getref(f);
  o->nextowned = NOTHING;
  o->key = getboolexp(f);
#ifndef USE_DBP_STR
  o->fail = getstring_compress(f);
  o->succ = getstring_compress(f);
  o->ofail = getstring_compress(f);
  o->osucc = getstring_compress(f);
#endif
  o->properties = NULL;
  OWNER(objno) = getref(f);
  pennies = getref(f);
  FLAGS(objno) = getref(f);
  /* flags have to be checked for conflict --- if they happen to coincide
     with chown_ok flags and jump_ok flags, we bump them up to
     the corresponding HAVEN and ABODE flags                           */
  if (FLAGS(objno) & CHOWN_OK)
    {
      FLAGS(objno) &= ~CHOWN_OK;
      FLAGS(objno) |= HAVEN;
    }
  if (FLAGS(objno) & JUMP_OK)
  {
    FLAGS(objno) &= ~JUMP_OK;
    FLAGS(objno) |= ABODE;
  }
  password = getstring(f);
  /* convert GENDER flag to property */
  if (FLAGS(objno) & TYPE_PLAYER)
  {
    switch((FLAGS(objno) & GENDER_MASK) >> GENDER_SHIFT)
    {
      case GENDER_NEUTER:
        gender = "neuter";
        break;
      case GENDER_FEMALE:
        gender = "female";
        break;
      case GENDER_MALE:
        gender = "male";
        break;
      default:
        gender = "unassigned";
        break;
    }
    add_property(objno, "sex", gender, PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  }
  /* Blast the gender flags (Doran) */
  FLAGS(objno) &= (~GENDER_MASK);

  /* For downward compatibility with databases using the */
  /* obsolete ANTILOCK flag. */
  if(FLAGS(objno) & ANTILOCK)
  {
    o->key = negate_boolexp(o->key);
    FLAGS(objno) &= ~ANTILOCK;
  }

  switch (FLAGS(objno) & TYPE_MASK)
  {
    case TYPE_THING:
      o->link = exits;
      o->pennies = pennies;
      break;
    case TYPE_ROOM:
      o->link = o->location;
      o->location = NOTHING;
      o->exits = exits;
      break;
    case TYPE_EXIT:
      if (o->location == NOTHING)
      {
        o->sp.exit.ndest = 0;
        o->sp.exit.dest = NULL;
      }
      else
      {
        o->sp.exit.ndest = 1;
        o->sp.exit.dest = (dbref *) malloc(sizeof(dbref));
        (o->sp.exit.dest)[0] = o->location;
      }
      o->location = NOTHING;
      break;
    case TYPE_PLAYER:
      o->link = exits;
      o->exits = NOTHING;
      o->pennies = pennies;
      o->sp.player.password = make_password(password);
      OWNER(objno) = objno;
      add_player(objno);
      break;
    case TYPE_PROGRAM:
      o->sp.program.editlocks = NULL;
      o->link = o->owner;
      break;
    case TYPE_GARBAGE:
      OWNER(objno) = NOTHING;
      add_compost(objno);
      free(NAME(objno));
#ifndef USE_DBP_STR
      free(o->desc);
#endif
      NAME(objno) = COMPOST_NAME;
#ifndef USE_DBP_STR
      o->desc = COMPOST_DESC;
#endif
      FLAGS(objno) = TYPE_GARBAGE;
      break;
  }
}

void db_read_object_new(FILE *f, object *o, dbref objno)
{
  int j;
  char *gender;
  
  NAME(objno) = getstring(f);
#ifndef USE_DBP_STR
  o->desc = getstring_compress(f);
#endif
  o->location = getref(f);
  o->contents = getref(f);
  o->backlinks = NULL;
  o->backlocks = NULL;
  o->next = getref(f);
  o->nextowned = NOTHING;
  o->key = getboolexp(f);
#ifndef USE_DBP_STR
  o->fail = getstring_compress(f);
  o->succ = getstring_compress(f);
  o->ofail = getstring_compress(f);
  o->osucc = getstring_compress(f);
#endif
  o->properties = NULL;
  FLAGS(objno) = getref(f);
  /* flags have to be checked for conflict --- if they happen to coincide
     with chown_ok flags and jump_ok flags, we bump them up to
     the corresponding HAVEN and ABODE flags                           */
  if (FLAGS(objno) & CHOWN_OK)
    {
      FLAGS(objno) &= ~CHOWN_OK;
      FLAGS(objno) |= HAVEN;
    }
  if (FLAGS(objno) & JUMP_OK)
    {
      FLAGS(objno) &= ~JUMP_OK;
      FLAGS(objno) |= ABODE;
    }
  /* convert GENDER flag to property */
  if (FLAGS(objno) & TYPE_PLAYER)
  {
    switch((FLAGS(objno) & GENDER_MASK) >> GENDER_SHIFT)
    {
      case GENDER_NEUTER:
        gender = "neuter";
        break;
      case GENDER_FEMALE:
        gender = "female";
        break;
      case GENDER_MALE:
        gender = "male";
        break;
      default:
        gender = "unassigned";
        break;
    }
    add_property(objno, "sex", gender, PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  }
  /* Blast the gender flags (Doran) */
  FLAGS(objno) &= (~GENDER_MASK);

  /* o->password = getstring(f); */
  /* For downward compatibility with databases using the */
  /* obsolete ANTILOCK flag. */
  if(FLAGS(objno) & ANTILOCK)
  {
    o->key = negate_boolexp(o->key);
    FLAGS(objno) &= ~ANTILOCK;
  }
  switch (FLAGS(objno) & TYPE_MASK)
  {
  case TYPE_THING:
    o->link = getref(f);
    o->exits = getref(f);
    OWNER(objno) = getref(f);
    o->pennies = getref(f);
    break;
  case TYPE_ROOM:
    o->link = getref(f);
    o->exits = getref(f);
    OWNER(objno) = getref(f);
    break;
  case TYPE_EXIT:
    o->sp.exit.ndest = getref(f);
    o->sp.exit.dest = (dbref *) malloc(sizeof(dbref) * o->sp.exit.ndest);
    for (j = 0; j < o->sp.exit.ndest; j++)
    {
      (o->sp.exit.dest)[j] = getref(f);
    }
    OWNER(objno) = getref(f);
    break;
  case TYPE_PLAYER:
    o->link = getref(f);
    o->exits = getref(f);
    o->pennies = getref(f);
    o->sp.player.password = getstring(f);
    o->sp.player.password = make_password(o->sp.player.password);
    OWNER(objno) = objno;
    add_player(objno);
    break;
  }
}

void db_read_object_lachesis(FILE *f, object *o, dbref objno)
{
  int c, j, prop_flag = 0;
  char *gender;
  
  NAME(objno) = getstring(f);
#ifndef USE_DBP_STR
  o->desc = getstring_compress(f);
#endif
  o->location = getref(f);
  o->contents = getref(f);
  o->backlinks = NULL;
  o->backlocks = NULL;
  o->next = getref(f);
  o->nextowned = NOTHING;
  o->key = getboolexp(f);
#ifndef USE_DBP_STR
  o->fail = getstring_compress(f);
  o->succ = getstring_compress(f);
  o->drop = getstring_compress(f);
  o->ofail = getstring_compress(f);
  o->osucc = getstring_compress(f);
  o->odrop = getstring_compress(f);
#endif
  FLAGS(objno) = getref(f);
  c = getc(f);
  if (c == '*')
  {
    getproperties(f, objno, 0);
    prop_flag++;
  }
  else
  {
    /* do our own getref */
    int sign = 0;
    int  i = 0;
    
    if (c == '-') sign = 1;
    else if (c != '+')
    {
      buf[i] = c;
      i++;
    }
      
    while ((c = getc(f)) != '\n')
    {
      buf[i] = c;
      i++;
    }
    buf[i] = '\0';
    j = atol(buf);
    if (sign) j = -j;
      
    /* set gender stuff */
    /* convert GENDER flag to property */
    switch((FLAGS(objno) & GENDER_MASK) >> GENDER_SHIFT)
    {
      case GENDER_NEUTER:
        gender = "neuter";
        break;
      case GENDER_FEMALE:
        gender = "female";
        break;
      case GENDER_MALE:
        gender = "male";
        break;
      default:
        gender = "unassigned";
        break;
    }
    add_property(objno, "sex", gender, PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  }
  
  /* o->password = getstring(f); */
  /* For downward compatibility with databases using the */
  /* obsolete ANTILOCK flag. */
  if(FLAGS(objno) & ANTILOCK)
  {
    o->key = negate_boolexp(o->key);
    FLAGS(objno) &= ~ANTILOCK;
  }
  switch (FLAGS(objno) & TYPE_MASK)
  {
  case TYPE_THING:
    o->link = prop_flag ? getref(f) : j;
    o->exits = getref(f);
    OWNER(objno) = getref(f);
    o->pennies = getref(f);
    break;
  case TYPE_ROOM:
    o->link = prop_flag ? getref(f) : j;
    o->exits = getref(f);
    OWNER(objno) = getref(f);
    break;
  case TYPE_EXIT:
    o->sp.exit.ndest = prop_flag ? getref(f) : j;
    o->sp.exit.dest = (dbref *) malloc(sizeof(dbref) * (o->sp.exit.ndest));
    for (j = 0; j < o->sp.exit.ndest; j++)
      (o->sp.exit.dest)[j] = getref(f);
    OWNER(objno) = getref(f);
    break;
  case TYPE_PLAYER:
    o->link = prop_flag ? getref(f) : j;
    o->exits = getref(f);
    o->pennies = getref(f);
    o->sp.player.password = getstring(f);
    o->curr_prog = NOTHING;
    o->sp.player.insert_mode = 0;
    o->sp.player.password = make_password(o->sp.player.password);
    OWNER(objno) = objno;
    add_player(objno);
    break;
  case TYPE_PROGRAM:
    OWNER(objno) = getref(f);
    FLAGS(objno) &= ~INTERNAL;
    o->link = o->owner;
    o->sp.program.curr_line = 0;
    o->sp.program.code = 0;
    o->sp.program.siz = 0;
    o->sp.program.start = 0;
    o->sp.program.editlocks = NULL;
#ifdef COMPILE_ON_LOAD
    o->sp.program.first = read_program(objno);
    do_compile(NOTHING, objno);
    free_prog_text(o->sp.program.first);
#endif /* COMPILE_ON_LOAD */
    o->sp.program.first = 0;
    break;
  case TYPE_GARBAGE:
    add_compost(objno);
    free(NAME(objno));
#ifndef USE_DBP_STR
    free(o->desc);
#endif
    NAME(objno) = COMPOST_NAME;
#ifndef USE_DBP_STR
    o->desc = COMPOST_DESC;
#endif
    break;
  }
}

void db_read_object_doran(FILE *f, object *o, dbref objno)
{
  int  j, c, prop_flag = 0;
  char *gender;
  
  NAME(objno) = getstring(f);
#ifndef USE_DBP_STR
  o->desc = getstring_compress(f);
#endif
  o->location = getref(f);
  o->contents = getref(f);
  o->backlinks = NULL;
  o->backlocks = NULL;
  o->next = getref(f);
  o->nextowned = NOTHING;
  o->key = getboolexp(f);
#ifndef USE_DBP_STR
  o->fail = getstring_compress(f);
  o->succ = getstring_compress(f);
  o->drop = getstring_compress(f);
  o->ofail = getstring_compress(f);
  o->osucc = getstring_compress(f);
  o->odrop = getstring_compress(f);
#endif
  FLAGS(objno) = getref(f);
  o->time_created = getref(f);
  o->time_modified = getref(f);
  o->time_used = getref(f);
  c = getc(f);
  if (c == '*')
  {
    getproperties(f, objno, 0);
    prop_flag++;
  }
  else
  {
    /* do our own getref */
    int sign = 0;
    int  i = 0;
    
    if (c == '-') sign = 1;
    else if (c != '+')
    {
      buf[i] = c;
      i++;
    }
      
    while ((c = getc(f)) != '\n')
    {
      buf[i] = c;
      i++;
    }
    buf[i] = '\0';
    j = atol(buf);
    if (sign) j = -j;
      
    /* set gender stuff */
    /* convert GENDER flag to property */
    switch((FLAGS(objno) & GENDER_MASK) >> GENDER_SHIFT)
    {
      case GENDER_NEUTER:
        gender = "neuter";
        break;
      case GENDER_FEMALE:
        gender = "female";
        break;
      case GENDER_MALE:
        gender = "male";
        break;
      default:
        gender = "unassigned";
        break;
    }
    add_property(objno, "sex", gender, PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  }
  
  /* Blast the gender flags (Doran) */
  FLAGS(objno) &= (~GENDER_MASK);

  /* o->password = getstring(f); */
  /* For downward compatibility with databases using the */
  /* obsolete ANTILOCK flag. */
  if(FLAGS(objno) & ANTILOCK)
  {
    o->key = negate_boolexp(o->key);
    FLAGS(objno) &= ~ANTILOCK;
  }
  switch (FLAGS(objno) & TYPE_MASK)
  {
    case TYPE_THING:
      o->link = prop_flag ? getref(f) : j;
      o->exits = getref(f);
      OWNER(objno) = getref(f);
      o->pennies = getref(f);
      break;
    case TYPE_ROOM:
      o->link = prop_flag ? getref(f) : j;
      o->exits = getref(f);
      OWNER(objno) = getref(f);
      break;
    case TYPE_EXIT:
      o->sp.exit.ndest = prop_flag ? getref(f) : j;
      o->sp.exit.dest = (dbref *) malloc(sizeof(dbref) * (o->sp.exit.ndest));
      for (j = 0; j < o->sp.exit.ndest; j++)
        (o->sp.exit.dest)[j] = getref(f);
      OWNER(objno) = getref(f);
      break;
    case TYPE_PLAYER:
      o->link = prop_flag ? getref(f) : j;
      o->exits = getref(f);
      o->pennies = getref(f);
      o->sp.player.password = getstring(f);
      o->curr_prog = NOTHING;
      o->sp.player.insert_mode = 0;
      OWNER(objno) = objno;
      add_player(objno);
      break;
    case TYPE_DAEMON:
      if (prop_flag) getref(f); /* just throw away the data */
      getref(f);
      getref(f);
      free(getstring(f));
      recycle((dbref) 1, objno);
      break;
    case TYPE_PROGRAM:
      OWNER(objno) = getref(f);
      FLAGS(objno) &= ~INTERNAL;
      o->sp.program.curr_line = 0;
      o->sp.program.code = 0;
      o->sp.program.siz = 0;
      o->sp.program.start = 0;
      o->link = o->owner;
      o->sp.program.editlocks = NULL;
#ifdef COMPILE_ON_LOAD
      o->sp.program.first = read_program(objno);
      do_compile(NOTHING, objno);
      free_prog_text(o->sp.program.first);
#endif /* COMPILE_ON_LOAD */
      o->sp.program.first = 0;
      break;
    case TYPE_GARBAGE:
      add_compost(objno);
      free(NAME(objno));
      NAME(objno) = COMPOST_NAME;
#ifndef USE_DBP_STR
      free(o->desc);
      o->desc = COMPOST_DESC;
#endif
      break;
  }
}

void burn_dbref_list(dbref_list *drl)
{
  dbref_list *drl_tmp;
  for (;drl; drl = drl_tmp)
  {
    drl_tmp = drl->next;
    free(drl);
  }
}

void db_read_object_daemon(FILE *f, object *o, dbref objno,
  int permsflag)
{
  int  j, c, prop_flag = 0;

  NAME(objno) = getstring(f);
#ifndef USE_DBP_STR
  o->desc = getstring_compress(f);
#endif
  o->location = getref(f);
  o->contents = getref(f);
  o->backlinks = NULL;
  o->backlocks = NULL;
  o->next = getref(f);
  o->nextowned = getref(f);
#ifdef RESET_LISTS
  o->nextowned = NOTHING;
#endif
  o->owner = getref(f);
  o->key = getboolexp(f);
#ifndef USE_DBP_STR
  o->fail = getstring_compress(f);
  o->succ = getstring_compress(f);
  o->drop = getstring_compress(f);
  o->ofail = getstring_compress(f);
  o->osucc = getstring_compress(f);
  o->odrop = getstring_compress(f);
#endif
  FLAGS(objno) = getref(f);
  o->time_created = getref(f);
  o->time_modified = getref(f);
  o->time_used = getref(f);
  fgets(garbagebuf, BUFFER_LEN, f);/* getstring_compress(f); Someone kill me */
  o->backlinks = dbreflist_read(f);
  fgets(garbagebuf, BUFFER_LEN, f);/* getstring_compress(f); Someone kill me */
  o->backlocks = dbreflist_read(f);
  c = getc(f);
  if (c == '*')
  {
    getproperties(f, objno, permsflag);
    prop_flag++;
  }
  else
  {
    /* do our own getref */
    int sign = 0;
    int  i = 0;
    
    if (c == '-') sign = 1;
    else if (c != '+')
    {
      buf[i] = c;
      i++;
    }
     
    while ((c = getc(f)) != '\n')
    {
      buf[i] = c;
      i++;
    }
    buf[i] = '\0';
    j = atol(buf);
    if (sign) j = -j;
  }
  
  switch (FLAGS(objno) & TYPE_MASK)
  {
    case TYPE_THING:
      o->link = prop_flag ? getref(f) : j;
      o->exits = getref(f);
      o->pennies = getref(f);
      break;
    case TYPE_ROOM:
      o->link = prop_flag ? getref(f) : j;
      o->exits = getref(f);
      break;
    case TYPE_EXIT:
      o->sp.exit.ndest = prop_flag ? getref(f) : j;
      o->sp.exit.dest = (dbref *)malloc(sizeof(dbref) * (o->sp.exit.ndest));
      for (j = 0; j < o->sp.exit.ndest; j++) (o->sp.exit.dest)[j] = getref(f);
      break;
    case TYPE_PLAYER:
      o->link = prop_flag ? getref(f) : j;
      o->exits = getref(f);
      o->pennies = getref(f);
      o->sp.player.password = getstring(f);
      o->curr_prog = NOTHING;
      o->sp.player.insert_mode = 0;
      add_player(objno);
      break;
    case TYPE_DAEMON:
      recycle((dbref) 1, objno);
      break;
    case TYPE_PROGRAM:
      FLAGS(objno) &= ~INTERNAL;
      o->link = o->owner;
      o->sp.program.curr_line = 0;
      o->sp.program.code = 0;
      o->sp.program.siz = 0;
      o->sp.program.start = 0;
      o->sp.program.editlocks = NULL;
#ifdef COMPILE_ON_LOAD
      o->sp.program.first = read_program(objno);
      do_compile(NOTHING, objno);
      free_prog_text(o->sp.program.first);
#endif /* COMPILE_ON_LOAD */
      o->sp.program.first = 0;
      break;
    case TYPE_GARBAGE:
      add_compost(objno);
      free(NAME(objno));
      NAME(objno) = COMPOST_NAME;
#ifndef USE_DBP_STR
      free(o->desc);
      o->desc = COMPOST_DESC;
#endif
      break;
  }
}

void db_read_object_mulch(FILE *f, object *o, dbref objno)
{
  int c;
  int j;

  o->backlinks = NULL;
  o->backlocks = NULL;
  o->curr_prog = NOTHING;

  NAME(objno) = getstring(f);
#ifndef USE_DBP_STR
  o->desc = getstring_compress(f);
#else
  getstring_noalloc(f);
#endif /* DBP_STR */
  o->location = getref(f);
  o->contents = getref(f);
  o->link = getref(f);
  o->exits = getref(f);
  o->pennies = getref(f);
  o->next = getref(f);
  o->nextowned = getref(f);
#ifdef RESET_LISTS
  o->nextowned = NOTHING;
#endif
  o->owner = getref(f);
  o->key = getboolexp(f);
#ifndef USE_DBP_STR
  o->fail = getstring_compress(f);
  o->succ = getstring_compress(f);
  o->drop = getstring_compress(f);
  o->ofail = getstring_compress(f);
  o->osucc = getstring_compress(f);
  o->odrop = getstring_compress(f);
#else
  getstring_noalloc(f); 
  getstring_noalloc(f);
  getstring_noalloc(f);
  getstring_noalloc(f);
  getstring_noalloc(f);
  getstring_noalloc(f);
#endif /* USE_DBP_STR */
  FLAGS(objno) = getref(f);
  if (Typeof(objno) == TYPE_EXIT) o->link = NOTHING;
  o->time_created = getref(f);
  o->time_modified = getref(f);
  o->time_used = getref(f);
  fgets(garbagebuf, BUFFER_LEN, f);/* getstring_compress(f); Someone kill me */
  o->backlinks = dbreflist_read(f);
  fgets(garbagebuf, BUFFER_LEN, f);/* getstring_compress(f); Someone kill me */
  o->backlocks = dbreflist_read(f);
  c = getc(f);
  if (c == '*')
  {
    getproperties(f, objno, 1);
  }
  else
  {
    /* do our own getref */
    int sign = 0;
    int  i = 0;
    
    if (c == '-') sign = 1;
    else if (c != '+')
    {
      buf[i] = c;
      i++;
    }
     
    while ((c = getc(f)) != '\n')
    {
      buf[i] = c;
      i++;
    }
    buf[i] = '\0';
    j = atol(buf);
    if (sign) j = -j;
  }
  
  switch (FLAGS(objno) & TYPE_MASK)
  {
    case TYPE_EXIT:
      o->sp.exit.ndest = getref(f);
      o->sp.exit.dest = (dbref *)malloc(sizeof(dbref) * (o->sp.exit.ndest));
      for (j = 0; j < o->sp.exit.ndest; j++) (o->sp.exit.dest)[j] = getref(f);
      break;
    case TYPE_PLAYER:
      o->sp.player.password = getstring(f);
      o->sp.player.insert_mode = 0;
      add_player(objno);
      break;
    case TYPE_PROGRAM:
      FLAGS(objno) &= ~INTERNAL;
      o->sp.program.curr_line = 0;
      o->sp.program.code = 0;
      o->sp.program.siz = 0;
      o->sp.program.start = 0;
      o->sp.program.editlocks = NULL;
#ifdef COMPILE_ON_LOAD
      o->sp.program.first = read_program(objno);
      do_compile(NOTHING, objno);
      free_prog_text(o->sp.program.first);
#endif /* COMPILE_ON_LOAD */
      o->sp.program.first = 0;
      break;
    case TYPE_GARBAGE:
      add_compost(objno);
      free(NAME(objno));
      NAME(objno) = COMPOST_NAME;
#ifndef USE_DBP_STR
      free(o->desc);
      o->desc = COMPOST_DESC;
#endif
  }
}

void db_chown(dbref thing, dbref newowner)
{
  DBSTORE(thing, time_modified, time(NULL));
  remove_ownerlist(thing);
  DBSTORE(thing, owner, newowner);
  add_ownerlist(thing);
}

void add_ownerlist(dbref d)
{
  dbref owner;
  owner = DBFETCH(d)->owner;
  if (owner == d) return;
  if (Typeof(owner) == TYPE_PLAYER)
  {
    DBSTORE(d, nextowned, DBFETCH(owner)->nextowned);
    DBSTORE(owner, nextowned, d);
  }
  else fputs ("Error, illegal owner value.\n", stderr);
}

void remove_ownerlist(dbref d)
{
  dbref step;
  for (step = DBFETCH(d)->owner;
    (DBFETCH(step)->nextowned != NOTHING) &&
    (DBFETCH(step)->nextowned != d);
    step = DBFETCH(step)->nextowned);

  if (DBFETCH(step)->nextowned == d)
  {
    DBSTORE(step, nextowned, DBFETCH(DBFETCH(step)->nextowned)->nextowned);
  }

 DBSTORE(d, nextowned, NULL);
}

void add_backlocks_parse(dbref src, boolexp *b)
{
  if (b)
  {
    switch (b->type)
    {
      case BOOLEXP_AND:
      case BOOLEXP_OR:
        add_backlocks_parse(src, b->sub1);
        add_backlocks_parse(src, b->sub2);
        break;
      case BOOLEXP_NOT:
        add_backlocks_parse(src, b->sub1);
        break;
      case BOOLEXP_CONST:
	if (b->thing != NOTHING) {
          if (b->thing == HOME) {
             fprintf(stderr, "Object locked to HOME. %d\n", src);
             exit(999);
             }
          DBSTORE(b->thing, backlocks,
	  dbreflist_add(DBFETCH(b->thing)->backlocks, src));
          }
    }
  }
}

void remove_backlocks_parse(dbref src, boolexp *b)
{
  if (b)
  {
    switch (b->type)
    {
      case BOOLEXP_AND:
      case BOOLEXP_OR:
        remove_backlocks_parse(src, b->sub1);
        remove_backlocks_parse(src, b->sub2);
        break;
      case BOOLEXP_NOT:
        remove_backlocks_parse(src, b->sub1);
        break;
      case BOOLEXP_CONST:
	if (b->thing != NOTHING) DBSTORE(b->thing, backlocks,
	  dbreflist_remove(DBFETCH(b->thing)->backlocks, src));
    }
  }
}

void establish_lists()
{
  dbref d;

  for (d = 0; d < db_top; d++)
  {
    if (Typeof(d) != TYPE_GARBAGE)
    {
      add_backlocks_parse(d, DBFETCH(d)->key);
      add_backlinks(d);
      add_ownerlist(d);
    }
  }
}

void reset_lists()
{
  dbref d;

  fputs ("Resetting lists...\n", stderr);

  for (d = 0; d < db_top; d++)
  {
    dbreflist_burn(DBFETCH(d)->backlinks);
    dbreflist_burn(DBFETCH(d)->backlocks);
    DBSTORE(d, backlinks, NULL);
    DBSTORE(d, backlocks, NULL);
    DBSTORE(d, nextowned, NOTHING);
  }
  establish_lists();
}

/* returns object a is linked to b... */
int validate_lists_backlinks(dbref a, dbref b)
{
  int c, flag;

  switch(Typeof(a))
  {
    case TYPE_PLAYER:
    case TYPE_PROGRAM:
    case TYPE_THING:
      return ((DBFETCH(a)->link == HOME) ?
	(DBFETCH(OWNER(a))->link == b) :
        (DBFETCH(a)->link == b));
    case TYPE_ROOM:
      return ((DBFETCH(a)->link == HOME) ? 0 : (DBFETCH(a)->link == b));
    case TYPE_EXIT:
      for (c = 0, flag = 0; c < DBFETCH(a)->sp.exit.ndest; c++)
      {
	flag |= ((DBFETCH(a)->sp.exit.dest[c] == HOME) ?
	  0 :
	  (DBFETCH(a)->sp.exit.dest[c] == b));
      }
      return (flag);
  }
  return 0;
}

int validate_lists_backlocks (boolexp *a, dbref b)
{
  if (a)
  {
    switch(a->type)
    {
      case BOOLEXP_AND:
      case BOOLEXP_OR:
	return (validate_lists_backlocks(a->sub1, b) ||
	  validate_lists_backlocks(a->sub2, b));
      case BOOLEXP_NOT:
	return (validate_lists_backlocks(a->sub1, b));
      case BOOLEXP_CONST:
	return (a->thing == b);
      default:
	return 0;
    }
  }
  return 0;
}

int validate_lists_blback(dbref a, boolexp *bxp)
{
  int flag;
  dbref_list *drl;
  if (bxp)
  {
    switch(bxp->type)
    {
      case BOOLEXP_AND:
      case BOOLEXP_OR:
        return (validate_lists_blback(a, bxp->sub1) &&
          validate_lists_blback(a, bxp->sub2));
      case BOOLEXP_NOT:
        return (validate_lists_blback(a, bxp->sub1));
      case BOOLEXP_CONST:
        flag = 0;

	if (bxp->thing == NOTHING || bxp->thing == HOME) return 1;

        for (drl = DBFETCH(bxp->thing)->backlocks; drl; drl = drl->next)
        {
          if (drl->object == a) flag++;
        }
        if (flag == 0) return 0;
        return 1;
    }
  }
  return 1;
}

int validate_lists()
{
  dbref d, d2;
  dbref_list *drl;
  int counter, counter2, flag = 1, i;

  fputs ("Validating lists.\n", stderr);
  for (d = 0; d < db_top; d++)
  {
    if (!(d % 1000)) fprintf (stderr, "%ld...\n", d);

    if ((Typeof(d) != TYPE_GARBAGE) && (Typeof(OWNER(d)) != TYPE_PLAYER))
    {
      flag = 0;
      if (Typeof(d) == TYPE_PLAYER)
      {
	DBSTORE(d, owner, d);
	fprintf (stderr, "Player %ld doesn't own themself.\n", d);
      }
      else
      {
	DBSTORE(d, owner, GOD_DBREF);
	fprintf (stderr, "Object %ld not owned by a player.\n", d);
      }
    }

    /* check backlinks list */
    for (drl = DBFETCH(d)->backlinks, counter = 0;
      (drl) && (counter < COUNTER_MAX);
      drl = drl->next, counter++)
    {
      if (!validate_lists_backlinks(drl->object, d))
      {
	flag = 0;
	fprintf
	  (stderr, "Illegal backlink connection %ld->%ld\n", drl->object, d);
      }
    }

    if (counter == COUNTER_MAX)
    {
      flag = 0;
      fprintf (stderr, "Possible backlinks loop for object %ld.\n", d);
    }

    /* check backlocks */
    for (drl = DBFETCH(d)->backlocks, counter = 0;
      (drl) && (counter < COUNTER_MAX);
      drl = drl->next, counter++)
    {
      if (!validate_lists_backlocks(DBFETCH(drl->object)->key, d))
      {
	fprintf (stderr, "Illegal backlock %ld is not locked to %ld.\n",
	  drl->object, d);
      }
    }

    if (counter == COUNTER_MAX)
    {
      flag = 0;
      fprintf (stderr, "Possible backlocks loop on %ld.\n", d);
    }
  }

  for (d = 0; d < db_top; d++)
  {
    if (Typeof(d) != TYPE_GARBAGE)
    {
      for (d2 = OWNER(d), counter = counter2 = 0;
	(d2 != NOTHING) && (counter < COUNTER_MAX);
	d2 = DBFETCH(d2)->nextowned, counter++)
      {
        if (d2 == d) counter2++;
      }

      if (counter2 != 1)
      {
        flag = 0;
        fprintf (stderr, "Object %ld in %ld ownerlist %ld times.\n", d,
	  OWNER(d), counter2);
      }
    }

    counter = 0;
    switch(Typeof(d))
    {
      case TYPE_PLAYER:
        /* Check owner lists... */
        for (d2 = d, counter = 0;
          (d2 != NOTHING) && (counter < COUNTER_MAX);
	  d2 = DBFETCH(d2)->nextowned, counter++)
        {
          if (OWNER(d2) != d)
          {
            flag = 0;
            fprintf (stderr, "%ld is not owned by %ld.\n", d2, OWNER(d));
          }
        }

        if (counter == COUNTER_MAX)
        {
          flag = 0;
          fprintf (stderr, "Possible owner list loop for %ld.\n", OWNER(d));
        }
      case TYPE_THING:
      case TYPE_PROGRAM:
	if (DBFETCH(d)->link != NOTHING)
	{
	  d2 = (DBFETCH(d)->link == HOME) ? DBFETCH(OWNER(d))->link :
	    DBFETCH(d)->link;
          for (drl = DBFETCH(d2)->backlinks, counter = 0; drl; drl = drl->next)
          {
            if (drl->object == d) counter++;
          }
	  if (counter != 1)
	  {
	    flag = 0;
	    fprintf(stderr, "Object %ld in %ld backlink list %ld times.\n", d,
	      d2, counter);
	  }
	}
	else
	{
	  fprintf (stderr, "Object %ld linked to nothing.\n", d);
	  if (Typeof(d) == TYPE_PLAYER)
	  {
	    DBSTORE(d, link, DBFETCH(d)->location);
	  }
	  else
	  {
	    DBSTORE(d, link, OWNER(d));
	  }
	  flag = 0;
	}
	break;
      case TYPE_ROOM:
        d2 = DBFETCH(d)->link;
	if ((d2 != NOTHING) && (d2 != HOME))
	{
          for (drl = DBFETCH(d2)->backlinks; drl; drl = drl->next)
          {
            if (drl->object == d) counter++;
          }
	  if (counter != 1)
	  {
	    flag = 0;
	    fprintf(stderr, "Object %ld in %ld backlink list %ld times.\n", d,
	      d2, counter);
	  }
	}
	break;
      case TYPE_EXIT:
	for (i = 0; i < DBFETCH(d)->sp.exit.ndest; i++)
	{
          d2 = DBFETCH(d)->sp.exit.dest[i];
	  if (d2 != HOME)
	  {
            counter = 0;
            for (drl = DBFETCH(d2)->backlinks; drl; drl = drl->next)
            {
              if (drl->object == d) counter++;
            }
            if (counter == 0)
            {
	      flag = 0;
	      fprintf(stderr, "Object %ld not in %ld backlink list.\n", d, d2);
            }
	  }
	}
    }

    if (!validate_lists_blback(d, DBFETCH(d)->key))
    {
      fprintf (stderr, "Object %ld not in correct backlock list.\n", d);
      flag = 0;
    }
  }

  return flag;
}

dbref db_read(FILE *f)
{
  dbref i;
  object *o;
  char *special;
  int newformat;
  char c;

  if ( (c = getc(f)) == '*')
  {
    special = getstring(f);
    if(!strcmp(special, "**TinyMUCK DUMP Format***"))
    {
      newformat = 1;
      fputs ("Database in TinyMUCK format.\n", stderr);
    }
    else if (!strcmp(special, "**Lachesis TinyMUCK DUMP Format***"))
    {
      newformat = 2;
      fputs ("Database in Lachesis format.\n", stderr);
    }
    else if (!strcmp(special, "**Doran TinyMUCK DUMP Format***"))
    {
      newformat = 3;
      fputs ("Database in Doran format.\n", stderr);
    }
    else if (!strcmp(special, "**DaemonMUCK DUMP Format***"))
    {
      newformat = 4;
      fputs ("Database in DaemonMUCK format.\n", stderr);
    }
    else if (!strcmp(special, "**DaemonMUCK P DUMP Format***"))
    {
      newformat = 5;
      fputs ("Database in DaemonMUCK P format.\n", stderr);
    }
    else if (!strcmp(special, "**MULCH***"))
    {
      newformat = 6;
      fputs ("Database in MULCH format.\n", stderr);
    }
    free((void *)special);
    c = getc(f); /* get next char */
  }
  
  db_free();
  fputs("Initializing primitives.\n", stderr);
  init_primitives();
  fputs("Reading database.\n", stderr);
  for(i = 0;; i++)
  {
    if (!(i % 1000)) fprintf(stderr, "%ld...\n", i);
    switch(c)
    {
      case '#':
        /* another entry, yawn */
        if(i != getref(f)) return -1; /* we blew it */

        db_grow(i+1); /* make space */
	db_clear_object(i);

        o = DBFETCH(i); /* read it in */

#ifdef USE_DBP
        o->dbp_size = -1L;
        o->lru_next = NOTHING;
#endif

        switch (newformat)
        {
          case 0:
            db_read_object_old(f, o, i);
            break;
          case 1:
            db_read_object_new(f, o, i);
            break;
          case 2:
            db_read_object_lachesis(f, o, i);
            break;
          case 3:
            db_read_object_doran(f, o, i);
            break;
          case 4:
            db_read_object_daemon(f, o, i, 0); /* db sans perms */
            break;
          case 5:
            db_read_object_daemon(f, o, i, 1); /* db with perms */
            break;
	  case 6:
	    db_read_object_mulch(f, o, i);
        }
        break;
      case '*':
        special = getstring(f);
        if(strcmp(special, "**END OF DUMP***"))
        {
          free((void *) special);
	  fputs("Done.\n", stderr);
          return NOTHING;
        }
        else
        {
          free((void *) special);
	  if (newformat < 4) establish_lists();
#ifdef RESET_LISTS
          reset_lists();
#endif
#ifdef VALIDATE_LISTS
          if (!validate_lists()) reset_lists();
#endif
          return db_top;
        }
      default:
        return -1;
    }
    c = getc(f);
  } /* for */
} /* db_read */

void add_backlinks(dbref obj)
{
  int i;
  dbref tmp;

  if (obj == NOTHING) return;

  switch(Typeof(obj))
  {
    case TYPE_EXIT:
      for(i = 0; i < DBFETCH(obj)->sp.exit.ndest; i++)
      {
	tmp = DBFETCH(obj)->sp.exit.dest[i];
	if (tmp != HOME)
	{
	  DBSTORE(tmp, backlinks, dbreflist_add(DBFETCH(tmp)->backlinks, obj));
	}
      }
      return;
    case TYPE_ROOM:
      tmp = DBFETCH(obj)->link;
      if ((tmp != HOME) && (tmp != NOTHING))
      {
        DBSTORE(tmp, backlinks, dbreflist_add(DBFETCH(tmp)->backlinks, obj));
      }
      return;
    default:
      tmp = DBFETCH(obj)->link;
      if (tmp == HOME) tmp = DBFETCH(OWNER(obj))->link;
      if (tmp == NOTHING) return;
      DBSTORE(tmp, backlinks, dbreflist_add(DBFETCH(tmp)->backlinks, obj));
  }
}

void remove_backlinks(dbref obj)
{
  int i;
  dbref tmp;

  if (obj == NOTHING) return;
  switch(Typeof(obj))
  {
    case TYPE_EXIT:
      for(i = 0; i < DBFETCH(obj)->sp.exit.ndest; i++)
      {
	tmp = DBFETCH(obj)->sp.exit.dest[i];
	if (tmp != HOME)
	{
	  DBSTORE(tmp, backlinks,
	    dbreflist_remove(DBFETCH(tmp)->backlinks, obj));
	}
      }
      return;
    case TYPE_ROOM:
      tmp = DBFETCH(obj)->link;
      if ((tmp != HOME) && (tmp != NOTHING))
      {
        DBSTORE(tmp, backlinks, dbreflist_remove(DBFETCH(tmp)->backlinks, obj));
      }
      return;
    default:
      tmp = DBFETCH(obj)->link;
      if (tmp == HOME) tmp = DBFETCH(OWNER(obj))->link;
      if (tmp == NOTHING) return;
      DBSTORE(tmp, backlinks, dbreflist_remove(DBFETCH(tmp)->backlinks, obj));
  }
}