/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/db.c,v 1.11 90/09/28 12:19:46 rearl Exp $ */

/*
 * $Log:	db.c,v $
 * Revision 1.11  90/09/28  12:19:46  rearl
 * Took out alloc_string(), moved it to stringutil.c
 * 
 * Revision 1.10  90/09/18  07:55:13  rearl
 * Added hash tables, changed program `locked' field to INTERNAL flag.
 * 
 * Revision 1.9  90/09/16  04:41:54  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.8  90/09/10  02:19:46  rearl
 * Introduced string compression of properties, for the
 * COMPRESS compiler option.
 * 
 * Revision 1.7  90/09/01  05:57:03  rearl
 * Took out TEST_MALLOC references.
 * 
 * Revision 1.6  90/08/27  03:22:33  rearl
 * Disk-based MUF source code, added neccesary locking.
 * 
 * Revision 1.5  90/08/15  03:00:59  rearl
 * Cleaned up some stuff, took out #ifdef GENDER.
 * 
 * Revision 1.4  90/08/02  18:47:46  rearl
 * Fixed calls to logging functions.
 * 
 * Revision 1.3  90/07/29  17:31:20  rearl
 * One picky pointer cast fixed.
 * 
 * Revision 1.2  90/07/23  03:12:27  casie
 * Cleaned up various gcc warnings.
 * 
 * Revision 1.1  90/07/19  23:03:24  casie
 * Initial revision
 * 
 *
 */

#include "copyright.h"
#include "config.h"

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

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

struct object *db = 0;
dbref db_top = 0;
#ifdef RECYCLE
dbref recyclable = NOTHING;
#endif

#ifndef DB_INITIAL_SIZE
#define DB_INITIAL_SIZE 10000
#endif /* DB_INITIAL_SIZE */

#ifdef DB_DOUBLING

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

struct macrotable *macrotop;

struct plist *new_prop(void);
void free_prop(struct plist *p);
int  number(const char *s);
void putproperties(FILE *f, struct plist *p);
void putprop(FILE *f, struct plist *p);
void getproperties(FILE *f, dbref objno);
void getpropertieslach(FILE *f, dbref objno);

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

void
  free_prog_text(struct line *l)
{
  struct line *next;
  
  while (l) {
    next = l -> next;
    free_line(l);
    l = next;
  }
}

#ifdef DB_DOUBLING

static void db_grow(dbref newtop)
{
  struct object *newdb;
  
  if(newtop > db_top) {
    db_top = newtop;
    if(!db) {
      /* make the initial one */
      db_size = DB_INITIAL_SIZE;
      if((db = (struct object *)
	  malloc(db_size * sizeof(struct 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 = (struct object *)
	  realloc((void *) db,
		  db_size * sizeof(struct object))) == 0) {
	abort();
      } 
      db = newdb;
    }
  }
}

#else /* DB_DOUBLING */

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

void db_clear_object(dbref i)
{
  struct object *o = DBFETCH(i);
  
  NAME(i) = 0;
  o->location = NOTHING;
  o->contents = NOTHING;
  o->next = NOTHING;
  o->key = TRUE_BOOLEXP;
  o->attributes = 0;
  o->properties = 0;
  DBDIRTY(i);
  o->exits=NOTHING;
  o->contents=NOTHING;
  o->curr_prog=NOTHING;
  o->insert_mode=0;
  o->run=0;
  /* flags you must initialize yourself */
  /* type-specific fields you must also initialize */
}

dbref new_object(void)
{
  dbref newobj;
  
#ifdef RECYCLE
  if(recyclable != NOTHING) {
    newobj = recyclable;
    recyclable = DBFETCH(newobj)->next;
  } else
#endif    
    {
      newobj = db_top;
      db_grow(db_top + 1);
    }
  
  /* clear it out */
  db_clear_object(newobj);
  return newobj;
}

#define DB_MSGLEN 1024

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

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

static void putbool_subexp(FILE *f, struct 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, "%d", b->thing);
    break;
  case BOOLEXP_PROP:
    putc('[', f);
    putprop(f, b -> prop_check);
    putc(']', f);
    break;
  default:
    break;
  }
}

void
  putprop(FILE *f, struct plist *prop)
{
  fputs((char *)prop -> type, f);
  putc(PROP_DELIMITER, f);
  fputs(prop -> class, f);
}

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

void
  putproperties(FILE *f, struct plist *p)
{
  while (p)
    {
      putprop(f, p);
      putc('\n', f);
      p = p -> next;
    }
}

void
  macrodump(struct 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)
{
  char buf[BUFFER_LEN];
  
  if (!fgets(buf, BUFFER_LEN, f)) return NULL;
  buf[strlen(buf) - 1] = '\0';
  return alloc_string(buf);
}

void
  foldtree (struct macrotable *center)
{
  int count = 0;
  struct 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 (struct macrotable *lastnode, FILE *f)
{
  char *line, *line2;
  struct macrotable *newmacro;
  
  if (!(line = file_line(f))) return 0;
  line2 = file_line(f);
  newmacro = (struct macrotable *)new_macro(line, line2, getref(f));
  if (!macrotop) macrotop = (struct 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(struct line *first, dbref i)
{
  FILE *f;
  char fname[BUFFER_LEN];
  
  sprintf(fname, "muf/%d.m", (int) i);
  f = fopen(fname, "w");
  if (!f) {
    log_status("Couldn't open file %s!\n", fname);
    return;
  }
  
  while (first) {
    if (!first -> this_line)
      continue;
    fputs((char *)first -> this_line, f);
    fputc('\n', f);
    first = first -> next;
  }
  fclose(f);
}

int db_write_object(FILE *f, dbref i)
{
  struct object *o = DBFETCH(i);
  int  j;
  
  putstring(f, NAME(i));
  putref(f, o->location);
  putref(f, o->contents);
  putref(f, o->next);
  putboolexp(f, o->key);
  putref(f, (FLAGS(i) & ~INTERACTIVE));
  putstring(f, "***Property list start ***");
  putproperties(f, o->attributes);
  putproperties(f, o->properties);
  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->sp.thing.value);
    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->sp.player.pennies);
    putstring(f, o->sp.player.password);
    break;
    
  case TYPE_PROGRAM:
    putref(f, OWNER(i));
    break;
  }
  
  return 0;
}

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

dbref parse_dbref(const char *s)
{
  const 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)
{
  static char buf[DB_MSGLEN];
  
  fgets(buf, sizeof(buf), f);
  return(atol(buf));
}

static const char *getstring_noalloc(FILE *f)
{
  static char buf[DB_MSGLEN];
  char *p;
  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')
	  ;
    }
  for(p = buf; *p; p++) {
    if(*p == '\n') {
      *p = '\0';
      break;
    }
  }
  
  return buf;
}

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

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

static struct boolexp *getboolexp1(FILE *f)
{
  struct boolexp *b;
  struct plist   *p;
  char   buf[BUFFER_LEN]; /* holds string for reading in property */
  int c;
  int i; /* index into buf */

  c = getc(f);
  switch(c) {
  case '\n':
    ungetc(c, f);
    return TRUE_BOOLEXP;
    /* break; */
  case EOF:
    abort();            /* unexpected EOF in boolexp */
    break;
  case '(':
    b = (struct boolexp *) malloc(sizeof(struct 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;
        /* break */
      }
      b->sub2 = getboolexp1(f);
      if(getc(f) != ')') goto error;
      return b;
    }
    /* break; */
  case '-':
    /* obsolete NOTHING key */
    /* eat it */
    while((c = getc(f)) != '\n') if(c == EOF) abort(); /* unexp EOF */
    ungetc(c, f);
    return TRUE_BOOLEXP;
    /* break */
  case '[':
    /* property type */
    b = (struct boolexp *) malloc(sizeof(struct boolexp));
    b -> type = BOOLEXP_PROP;
    b -> sub1 = b -> sub2 = 0;
    p = b -> prop_check = new_prop();
    i = 0;
    while ((c = getc(f)) != PROP_DELIMITER && i < BUFFER_LEN)
      {
        buf[i] = c;
        i++;
      }
    if (i >= BUFFER_LEN && c != PROP_DELIMITER)
      goto error;
    buf[i] = '\0';
    p -> type = alloc_string(buf);
    i = 0;
    while ((c = getc(f)) != ']')
      {
        buf[i] = c;
        i++;
      }
    buf[i] = '\0';
    if (i >= BUFFER_LEN && c != ']')
      goto error;
    p -> class = alloc_string(buf);
    return b;
  default:
    /* better be a dbref */
    ungetc(c, f);
    b = (struct boolexp *) malloc(sizeof(struct boolexp));
    b->type = BOOLEXP_CONST;
    b->thing = 0;

    /* 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';
    }
    ungetc(c, f);
    return b;
  }

 error:
  abort();                      /* bomb out */
  return TRUE_BOOLEXP;
}


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

void free_boolexp(struct 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_prop(b -> prop_check);
      free((void *) b);
      break;
    }
  }
}

void
  getproperties(FILE *f, dbref object)
{
  char   buf[BUFFER_LEN], s[BUFFER_LEN];
  int    i, j;
  char *type, *class;

  /* get rid of first line */
  fgets(buf, sizeof(buf), f);

  /* initialize first line stuff */
  fgets(buf, sizeof(buf), f);
  while (strcmp(buf, "***Property list end ***\n")) /* fgets reads in \n too! */
    {
      for (i = 0; buf[i] != PROP_DELIMITER; i++)
        s[i] = buf[i];
      s[i] = '\0';
      type = alloc_string(s);
      for (i++, j = 0; i < strlen(buf) - 1; i++, j++) /* don't include \n */
        s[j] = buf[i];
      s[j] = '\0';
      class = alloc_compressed(s);
      add_property(object, type, class);
      free((void *) class);
      free((void *) type);

      fgets(buf, sizeof(buf), f);
    }
}

void
  getpropertieslach(FILE *f, dbref object)
{
  char   buf[BUFFER_LEN], s[BUFFER_LEN];
  int    i, j;
  int    flag;
  char   *type, *class;
  const static char *toattr[11] = { "Ahear",  "Aahear", "Amhear",  "Adesc",
			            "Adrop",   "Afail",  "Asucc", "Listen",
			            "Sex",   "Idesc",  "Odesc" };
  /* get rid of first line */
  fgets(buf, sizeof(buf), f);

  /* initialize first line stuff */
  fgets(buf, sizeof(buf), f);
  while (strcmp(buf, "***Property list end ***\n")) /* fgets reads in \n too! */
    {
      for (i = 0; buf[i] != PROP_DELIMITER; i++)
        s[i] = buf[i];
      s[i] = '\0';
      type = alloc_string(s);
      for (i++, j = 0; i < strlen(buf) - 1; i++, j++) /* don't include \n */
        s[j] = buf[i];
      s[j] = '\0';
      if(s[0] == '^')
	class = alloc_compressed(s + 1);
      else if(!strncmp(s, "*s*", 3))     /* for MAGE 1.0 db */
	class = alloc_compressed(s + 3);
      else if(!strncmp(s, "*i*", 3))
	class = alloc_compressed(s + 3);
      else
	class = alloc_compressed(s);

      /* convert Va - Vz properties to attributes.. */
      if((tolower(type[0]) == 'v') && (strlen(type) == 2)) {
	if(tolower(type[1]) < 'a' || tolower(type[1]) > 'z') {
	  add_property(object, type, class);  /* normal property */
	} else {
	  type[0] = 'V';
	  type[1] = tolower(type[1]);
	  add_attr(object, type, class);
	}
      }
      else {  /* convert special properties to attributes */
	flag = 1;
	for(i = 0; (i < 11) && flag; i++) {
	  if(!string_compare(toattr[i], type)) {
	    add_attr(object, toattr[i], class);
	    flag = 0;
	  }
	}
	if(flag)    /* type was not was of the attributes */
	  add_property(object, type, class);
      }

      free((void *) class);
      free((void *) type);

      fgets(buf, sizeof(buf), f);
    }
}

void
  free_prop(struct plist *p)
{
  if (p -> class)
    free((void *) p -> class);
  if (p -> type)
    free((void *) p -> type);
  free((void *) p);
}


void db_free_object(dbref i)
{
  struct object *o;
  
  o = DBFETCH(i);
  if(NAME(i)) free((void *) NAME(i));
  if(o->key) free_boolexp(o->key);
  if (o->attributes)
    {
      struct plist *p, *next;

      for(p = o->attributes; p; p = next)
	{
	  next = p -> next;
	  free_prop(p);
	}
    }
  if (o->properties)
    {
      struct plist *p, *next;
      
      for (p = o->properties; p; p = next)
	{
	  next = p -> next;
	  free_prop(p);
	}
    }
  if(Typeof(i) == TYPE_EXIT && o->sp.exit.dest) {
    free((void *) o->sp.exit.dest);
  } else if(Typeof(i) == TYPE_PLAYER && o->sp.player.password) {
    free((void *) o->sp.player.password);
  }
  if (Typeof(i) == TYPE_PROGRAM)
    {
      int          j;
      struct inst         *code;
      
      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(void)
{
  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();
#ifdef RECYCLE
  recyclable = NOTHING;
#endif    
}

struct plist *
  new_prop(void)
{
  struct plist *p;
  
  p = (struct plist *) malloc(sizeof(struct plist));
  p -> type = 0; p -> class = 0; p -> next = 0;
  return p;
}

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

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

void db_read_object_mage(FILE *f, register struct object *o, dbref objno)
{
  int  j, c, prop_flag = 0;

  NAME(objno) = getstring(f);
  o->location = getref(f);
  o->contents = getref(f);
  o->next = getref(f);
  o->key = getboolexp(f);
  o->flags = getref(f);

  c = getc(f);
  if (c == '*')
    {
      getproperties(f, objno);
      prop_flag++;
    }
  else
    {
      /* do our own getref */
      int sign = 0;
      char buf[BUFFER_LEN];
      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);
    OWNER(objno) = getref(f);
    o->sp.thing.value = 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->sp.player.pennies = getref(f);
    o->sp.player.password = getstring(f);
    o->curr_prog = NOTHING;
    o->insert_mode = 0;
    o->run = 0;
    break;
  case TYPE_PROGRAM:
    OWNER(objno) = getref(f);
    FLAGS(objno) &= ~INTERNAL;
    o -> sp.program.curr_line = 0;
/*    o -> sp.program.first = read_program(objno); */
    o -> sp.program.code = 0;
    o -> sp.program.siz = 0;
    o -> sp.program.start = 0;
/*    do_compile(GOD, objno);
    free_prog_text(o -> sp.program.first); */
    break;
#ifdef RECYCLE
  case TYPE_GARBAGE:
    o->next = recyclable;
    recyclable = objno;
    free((void *)NAME(objno));
    NAME(objno) = "<garbage>";
    add_attr(objno, "Desc", "<recyclable>");
    break;
#endif
  }
}

void db_read_object_lachesis(FILE *f,register struct object *o, dbref objno)
{
  int  j, c, prop_flag = 0;
  
  NAME(objno) = getstring(f);
  add_attr(objno, "Desc", getstring_compress(f));
  o->location = getref(f);
  o->contents = getref(f);
  o->next = getref(f);
  o->key = getboolexp(f);
  add_attr(objno, "Fail", getstring_compress(f));
  add_attr(objno, "Succ", getstring_compress(f));
  add_attr(objno, "Drop", getstring_compress(f));
  add_attr(objno, "Ofail", getstring_compress(f));
  add_attr(objno, "Osucc", getstring_compress(f));
  add_attr(objno, "Odrop", getstring_compress(f));

  /* OWNER(objno) = getref(f); */
  /* o->pennies = getref(f); */
  o->flags = getref(f); /*  FLAGS(objno) = getref(f);*/

  c = getc(f);
  if (c == '*')
    {
      getpropertieslach(f, objno);
      prop_flag++;
    }
  else
    {
      /* do our own getref */
      int sign = 0;
      char buf[BUFFER_LEN];
      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:
          add_attr(objno, "sex", "neuter");
          break;
        case GENDER_FEMALE:
          add_attr(objno, "sex", "female");
          break;
        case GENDER_MALE:
	  add_attr(objno, "sex", "male");
          break;
        default:
	  add_attr(objno, "sex", "unassigned");
          break;
        }
    }
  
  /* o->password = getstring(f); */
  /* For downward compatibility with databases using the */
  /* obsolete ANTILOCK flag. */
  if(o->flags & ANTILOCK) {
    o->key = negate_boolexp(o->key);
    o->flags &= ~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->sp.thing.value = 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->sp.player.pennies = getref(f);
    o->sp.player.password = getstring(f);
    o->curr_prog = NOTHING;
    o->insert_mode = 0;
    o->run = 0;
    break;
  case TYPE_PROGRAM:
    OWNER(objno) = getref(f);
    FLAGS(objno) &= ~INTERNAL;
    o -> sp.program.curr_line = 0;
    o -> sp.program.first = read_program(objno);
    o -> sp.program.code = 0;
    o -> sp.program.siz = 0;
    o -> sp.program.start = 0;
    do_compile(GOD, objno);
    free_prog_text(o -> sp.program.first);
    break;
#ifdef RECYCLE
  case TYPE_GARBAGE:
    o->next = recyclable;
    recyclable = objno;
    free((void *)NAME(objno));
    NAME(objno) = "<garbage>";
    add_attr(objno, "Desc", "<recyclable>");
    break;
#endif      
  }
}

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

  newformat = 0;

  if ( (c = getc(f)) == '*') {
    special = getstring(f);
    if(!strcmp(special, "**MAGE 1.1 DUMP Format***")) {
      newformat = 1;
    }
    else if (!strcmp(special, "**Lachesis TinyMUCK DUMP Format***"))
      {
        newformat = 2;
      }
    free((void *)special);
    c = getc(f); /* get next char */
  }
  db_free();
  init_primitives();
  for(i = 0;; i++) {
    switch(c) {
    case '#':
      /* another entry, yawn */
      if(i != getref(f)) {
        /* we blew it */
        return -1;
      }
      /* make space */
      db_grow(i+1);

      /* read it in */
      o = DBFETCH(i);
      switch (newformat)
        {
/*        case 0:
          db_read_object_old(f, o, i);
          break; */
        case 1:
          db_read_object_mage(f, o, i);
          break;
        case 2:
          db_read_object_lachesis(f, o, i);
          break;
        }
      if (Typeof(i) == TYPE_PLAYER) {
        OWNER(i) = i;
        add_player(i);
      }
      break;
    case '*':
      special = getstring(f);
      if(strcmp(special, "**END OF DUMP***")) {
        free((void *) special);
        return -1;
      } else {
        free((void *) special);
        return db_top;
      }
    default:
      return -1;
      /* break; */
    }
    c = getc(f);
  } /* for */
} /* db_read */