text: 
  /*
   * db_rw.c 
   */
  
  /*
   * $Id: db_rw.c,v 1.4 2005/08/08 09:43:06 murrayma Exp $ 
   */
  
  #include "copyright.h"
  #include "config.h"
  
  #include <sys/file.h>
  
  #include "mudconf.h"
  #include "config.h"
  #include "externs.h"
  #include "db.h"
  #include "vattr.h"
  #include "attrs.h"
  #include "alloc.h"
  #include "powers.h"
  
  extern const char *getstring_noalloc(FILE *, int);
  extern void putstring(FILE *, const char *);
  extern void db_grow(dbref);
  
  extern struct object *db;
  
  static int g_version;
  static int g_format;
  static int g_flags;
  
  /*
   * ---------------------------------------------------------------------------
   * * getboolexp1: Get boolean subexpression from file.
   */
  
  static BOOLEXP *getboolexp1(FILE *f) {
      BOOLEXP *b;
      char *buff, *s;
      int c, d, anum;
  
      c = getc(f);
      switch (c) {
          case '\n':
              ungetc(c, f);
              return TRUE_BOOLEXP;
              /*
               * break; 
               */
          case EOF:
              abort();		/*
                               * unexpected EOF in boolexp 
                               */
              break;
          case '(':
              b = alloc_bool("getboolexp1.openparen");
              switch (c = getc(f)) {
                  case NOT_TOKEN:
                      b->type = BOOLEXP_NOT;
                      b->sub1 = getboolexp1(f);
                      if ((d = getc(f)) == '\n')
                          d = getc(f);
                      if (d != ')')
                          goto error;
                      return b;
                  case INDIR_TOKEN:
                      b->type = BOOLEXP_INDIR;
                      b->sub1 = getboolexp1(f);
                      if ((d = getc(f)) == '\n')
                          d = getc(f);
                      if (d != ')')
                          goto error;
                      return b;
                  case IS_TOKEN:
                      b->type = BOOLEXP_IS;
                      b->sub1 = getboolexp1(f);
                      if ((d = getc(f)) == '\n')
                          d = getc(f);
                      if (d != ')')
                          goto error;
                      return b;
                  case CARRY_TOKEN:
                      b->type = BOOLEXP_CARRY;
                      b->sub1 = getboolexp1(f);
                      if ((d = getc(f)) == '\n')
                          d = getc(f);
                      if (d != ')')
                          goto error;
                      return b;
                  case OWNER_TOKEN:
                      b->type = BOOLEXP_OWNER;
                      b->sub1 = getboolexp1(f);
                      if ((d = getc(f)) == '\n')
                          d = getc(f);
                      if (d != ')')
                          goto error;
                      return b;
                  default:
                      ungetc(c, f);
                      b->sub1 = getboolexp1(f);
                      if ((c = getc(f)) == '\n')
                          c = getc(f);
                      switch (c) {
                          case AND_TOKEN:
                              b->type = BOOLEXP_AND;
                              break;
                          case OR_TOKEN:
                              b->type = BOOLEXP_OR;
                              break;
                          default:
                              goto error;
                      }
                      b->sub2 = getboolexp1(f);
                      if ((d = getc(f)) == '\n')
                          d = getc(f);
                      if (d != ')')
                          goto error;
                      return b;
              }
          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 '"':
              ungetc(c, f);
              buff = alloc_lbuf("getboolexp_quoted");
              StringCopy(buff, getstring_noalloc(f, 1));
              c = fgetc(f);
              if (c == EOF) {
                  free_lbuf(buff);
                  return TRUE_BOOLEXP;
              }
  
              b = alloc_bool("getboolexp1_quoted");
              anum = mkattr(buff);
              if (anum <= 0) {
                  free_bool(b);
                  free_lbuf(buff);
                  goto error;
              }
              free_lbuf(buff);
              b->thing = anum;
  
              /*
               * if last character is : then this is an attribute lock. A 
               * last character of / means an eval lock 
               */
  
              if ((c == ':') || (c == '/')) {
                  if (c == '/')
                      b->type = BOOLEXP_EVAL;
                  else
                      b->type = BOOLEXP_ATR;
                  buff = alloc_lbuf("getboolexp1.attr_lock");
                  StringCopy(buff, getstring_noalloc(f, 1));
                  b->sub1 = (BOOLEXP *) strsave(buff);
                  free_lbuf(buff);
              }
              return b;
          default:			/*
                               * dbref or attribute 
                               */
              ungetc(c, f);
              b = alloc_bool("getboolexp1.default");
              b->type = BOOLEXP_CONST;
              b->thing = 0;
  
              /*
               * This is either an attribute, eval, or constant lock.
               * Constant locks are of the form <num>, while
               * attribute * and * * * * eval locks are of the form
               * <anam-or-anum>:<string> or
               * <aname-or-anum>/<string> respectively. The
               * characters <nl>, |, and & terminate the string. 
               */
  
              if (isdigit(c)) {
                  while (isdigit(c = getc(f))) {
                      b->thing = b->thing * 10 + c - '0';
                  }
              } else if (isalpha(c)) {
                  buff = alloc_lbuf("getboolexp1.atr_name");
                  for (s = buff;
                          ((c = getc(f)) != EOF) && (c != '\n') && (c != ':') &&
                          (c != '/'); *s++ = c);
                  if (c == EOF) {
                      free_lbuf(buff);
                      free_bool(b);
                      goto error;
                  }
                  *s = '\0';
  
                  /*
                   * Look the name up as an attribute.  If not found,
                   * create a new attribute. 
                   */
  
                  anum = mkattr(buff);
                  if (anum <= 0) {
                      free_bool(b);
                      free_lbuf(buff);
                      goto error;
                  }
                  free_lbuf(buff);
                  b->thing = anum;
              } else {
                  free_bool(b);
                  goto error;
              }
  
              /*
               * if last character is : then this is an attribute lock. A 
               * last character of / means an eval lock 
               */
  
              if ((c == ':') || (c == '/')) {
                  if (c == '/')
                      b->type = BOOLEXP_EVAL;
                  else
                      b->type = BOOLEXP_ATR;
                  buff = alloc_lbuf("getboolexp1.attr_lock");
                  for (s = buff;
                          ((c = getc(f)) != EOF) && (c != '\n') && (c != ')') &&
                          (c != OR_TOKEN) && (c != AND_TOKEN); *s++ = c);
                  if (c == EOF)
                      goto error;
                  *s++ = 0;
                  b->sub1 = (BOOLEXP *) strsave(buff);
                  free_lbuf(buff);
              }
              ungetc(c, f);
              return b;
      }
  
  error:
      abort();			/*
                           * bomb out 
                           */
      return TRUE_BOOLEXP;
  }
  
  /*
   * ---------------------------------------------------------------------------
   * * getboolexp: Read a boolean expression from the flat file.
   */
  
  static BOOLEXP *getboolexp(FILE *f) {
      BOOLEXP *b;
      char c;
  
      b = getboolexp1(f);
      if (getc(f) != '\n')
          abort();		/*
                           * parse error, we lose 
                           */
  
      /*
       * MUSH (except for PernMUSH) and MUSE can have an extra CR, * MUD *
       * * * * * does not. 
       */
  
      if (((g_format == F_MUSH) && (g_version != 2)) || (g_format == F_MUSE)
              || (g_format == F_MUX)) {
          if ((c = getc(f)) != '\n')
              ungetc(c, f);
      }
      return b;
  }
  
  /*
   * ---------------------------------------------------------------------------
   * * get_list: Read attribute list from flat file.
   */
  
  static int get_list(FILE *f, dbref i, int new_strings) {
      dbref atr;
      int c;
      char *buff;
  
      buff = alloc_lbuf("get_list");
      while (1) {
          switch (c = getc(f)) {
              case '>':		/*
                               * read # then string 
                               */
                  atr = getref(f);
                  if (atr > 0) {
                      /*
                       * Store the attr 
                       */
  
                      atr_add_raw(i, atr, (char *) getstring_noalloc(f,
                                  new_strings));
                  } else {
                      /*
                       * Silently discard 
                       */
  
                      getstring_noalloc(f, new_strings);
                  }
                  break;
              case '\n':		/*
                               * ignore newlines. They're due to v(r). 
                               */
                  break;
              case '<':		/*
                               * end of list 
                               */
                  free_lbuf(buff);
                  c = getc(f);
                  if (c != '\n') {
                      ungetc(c, f);
                      fprintf(stderr, "No line feed on object %d\n", i);
                      return 1;
                  }
                  return 1;
              default:
                  fprintf(stderr,
                          "Bad character '%c' when getting attributes on object %d\n",
                          c, i);
                  /*
                   * We've found a bad spot.  I hope things aren't * *
                   * * * * * too bad. 
                   */
  
                  (void) getstring_noalloc(f, new_strings);
          }
      }
  }
  
  /*
   * ---------------------------------------------------------------------------
   * * putbool_subexp: Write a boolean sub-expression to the flat file.
   */
  static void putbool_subexp(FILE *f, BOOLEXP *b) {
      ATTR *va;
  
      switch (b->type) {
          case BOOLEXP_IS:
              putc('(', f);
              putc(IS_TOKEN, f);
              putbool_subexp(f, b->sub1);
              putc(')', f);
              break;
          case BOOLEXP_CARRY:
              putc('(', f);
              putc(CARRY_TOKEN, f);
              putbool_subexp(f, b->sub1);
              putc(')', f);
              break;
          case BOOLEXP_INDIR:
              putc('(', f);
              putc(INDIR_TOKEN, f);
              putbool_subexp(f, b->sub1);
              putc(')', f);
              break;
          case BOOLEXP_OWNER:
              putc('(', f);
              putc(OWNER_TOKEN, f);
              putbool_subexp(f, b->sub1);
              putc(')', f);
              break;
          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_ATR:
              va = atr_num(b->thing);
              if (va) {
                  fprintf(f, "%s:%s", va->name, (char *) b->sub1);
              } else {
                  fprintf(f, "%d:%s\n", b->thing, (char *) b->sub1);
              }
              break;
          case BOOLEXP_EVAL:
              va = atr_num(b->thing);
              if (va) {
                  fprintf(f, "%s/%s\n", va->name, (char *) b->sub1);
              } else {
                  fprintf(f, "%d/%s\n", b->thing, (char *) b->sub1);
              }
              break;
          default:
              fprintf(stderr, "Unknown boolean type in putbool_subexp: %d\n",
                      b->type);
      }
  }
  
  /*
   * ---------------------------------------------------------------------------
   * * putboolexp: Write boolean expression to the flat file.
   */
  
  static void putboolexp(FILE *f, BOOLEXP *b) {
      if (b != TRUE_BOOLEXP) {
          putbool_subexp(f, b);
      }
      putc('\n', f);
  }
  
  
  dbref db_read(FILE *f, int *db_format, int *db_version, int *db_flags) {
      dbref i, anum;
      char ch;
      const char *tstr;
      int header_gotten, size_gotten, nextattr_gotten;
      int read_attribs, read_name, read_zone, read_link, read_key,
          read_parent;
      int read_extflags, read_3flags, read_money, read_timestamps,
          read_new_strings;
      int read_powers, read_powers_player, read_powers_any;
      int deduce_version, deduce_name, deduce_zone, deduce_timestamps;
      int aflags, f1, f2, f3;
      BOOLEXP *tempbool;
  
      header_gotten = 0;
      size_gotten = 0;
      nextattr_gotten = 0;
      g_format = F_UNKNOWN;
      g_version = 0;
      g_flags = 0;
      read_attribs = 1;
      read_name = 1;
      read_zone = 0;
      read_link = 0;
      read_key = 1;
      read_parent = 0;
      read_money = 1;
      read_extflags = 0;
      read_3flags = 0;
      read_timestamps = 0;
      read_new_strings = 0;
      read_powers = 0;
      read_powers_player = 0;
      read_powers_any = 0;
      deduce_version = 1;
      deduce_zone = 1;
      deduce_name = 1;
      deduce_timestamps = 1;
      db_free();
      for (i = 0;; i++) {
  
          switch (ch = getc(f)) {
              case '-':		/* Misc tag */
                  switch (ch = getc(f)) {
                      case 'R':		/* Record number of players */
                          mudstate.record_players = getref(f);
                          break;
                      default:
                          (void) getstring_noalloc(f, 0);
                  }
                  break;
              case '+':		/*
                               * MUX and MUSH header 
                               */
                  switch (ch = getc(f)) {	/*
                                           * 2nd char selects 
                                           * type 
                                           */
                      case 'X':		/*
                                       * MUX VERSION 
                                       */
                          if (header_gotten) {
                              fprintf(stderr,
                                      "\nDuplicate MUX version header entry at object %d, ignored.\n",
                                      i);
                              tstr = getstring_noalloc(f, 0);
                              break;
                          }
                          header_gotten = 1;
                          deduce_version = 0;
                          g_format = F_MUX;
                          g_version = getref(f);
  
                          /*
                           * Otherwise extract feature flags 
                           */
  
                          if (g_version & V_GDBM) {
                              read_attribs = 0;
                              read_name = !(g_version & V_ATRNAME);
                          }
                          read_zone = (g_version & V_ZONE);
                          read_link = (g_version & V_LINK);
                          read_key = !(g_version & V_ATRKEY);
                          read_parent = (g_version & V_PARENT);
                          read_money = !(g_version & V_ATRMONEY);
                          read_extflags = (g_version & V_XFLAGS);
                          read_3flags = (g_version & V_3FLAGS);
                          read_powers = (g_version & V_POWERS);
                          read_new_strings = (g_version & V_QUOTED);
                          g_flags = g_version & ~V_MASK;
  
                          g_version &= V_MASK;
                          deduce_name = 0;
                          deduce_version = 0;
                          deduce_zone = 0;
                          break;
                      case 'S':		/*
                                       * SIZE 
                                       */
                          if (size_gotten) {
                              fprintf(stderr,
                                      "\nDuplicate size entry at object %d, ignored.\n",
                                      i);
                              tstr = getstring_noalloc(f, 0);
                          } else {
                              mudstate.min_size = getref(f);
                          }
                          size_gotten = 1;
                          break;
                      case 'A':		/*
                                       * USER-NAMED ATTRIBUTE 
                                       */
                          anum = getref(f);
                          tstr = getstring_noalloc(f, read_new_strings);
                          if (isdigit(*tstr)) {
                              aflags = 0;
                              while (isdigit(*tstr))
                                  aflags = (aflags * 10) + (*tstr++ - '0');
                              tstr++;	/*
                                       * skip ':' 
                                       */
                          } else {
                              aflags = mudconf.vattr_flags;
                          }
                          vattr_define((char *) tstr, anum, aflags);
                          break;
                      case 'F':		/*
                                       * OPEN USER ATTRIBUTE SLOT 
                                       */
                          anum = getref(f);
                          break;
                      case 'N':		/*
                                       * NEXT ATTR TO ALLOC WHEN NO
                                       * FREELIST 
                                       */
                          if (nextattr_gotten) {
                              fprintf(stderr,
                                      "\nDuplicate next free vattr entry at object %d, ignored.\n",
                                      i);
                              tstr = getstring_noalloc(f, 0);
                          } else {
                              mudstate.attr_next = getref(f);
                              nextattr_gotten = 1;
                          }
                          break;
                      default:
                          fprintf(stderr,
                                  "\nUnexpected character '%c' in MUX header near object #%d, ignored.\n",
                                  ch, i);
                          tstr = getstring_noalloc(f, 0);
                  }
                  break;
              case '!':		/*
                               * MUX entry/MUSH entry/MUSE non-zoned entry 
                               */
                  if (deduce_version) {
                      g_format = F_MUX;
                      g_version = 1;
                      deduce_name = 0;
                      deduce_zone = 0;
                      deduce_version = 0;
                  } else if (deduce_zone) {
                      deduce_zone = 0;
                      read_zone = 0;
                  }
                  i = getref(f);
                  db_grow(i + 1);
  
                  if (read_name) {
                      tstr = getstring_noalloc(f, read_new_strings);
                      if (deduce_name) {
                          if (isdigit(*tstr)) {
                              read_name = 0;
                              s_Location(i, atoi(tstr));
                          } else {
                              s_Name(i, (char *) tstr);
                              s_Location(i, getref(f));
                          }
                          deduce_name = 0;
                      } else {
                          s_Name(i, (char *) tstr);
                          s_Location(i, getref(f));
                      }
                  } else {
                      s_Location(i, getref(f));
                  }
  
                  /*
                   * ZONE on MUSE databases and some others 
                   */
  
                  if (read_zone)
                      s_Zone(i, getref(f));
  
                  /*
                   * else
                   * * s_Zone(i, NOTHING); 
                   */
  
                  /*
                   * CONTENTS and EXITS 
                   */
  
                  s_Contents(i, getref(f));
                  s_Exits(i, getref(f));
  
                  /*
                   * LINK 
                   */
  
                  if (read_link)
                      s_Link(i, getref(f));
                  else
                      s_Link(i, NOTHING);
  
                  /*
                   * NEXT 
                   */
  
                  s_Next(i, getref(f));
  
                  /*
                   * LOCK
                   */
  
                  if (read_key) {
                      tempbool = getboolexp(f);
                      atr_add_raw(i, A_LOCK, unparse_boolexp_quiet(1,
                                  tempbool));
                      free_boolexp(tempbool);
                  }
                  /*
                   * OWNER 
                   */
  
                  s_Owner(i, getref(f));
  
                  /*
                   * PARENT: PennMUSH uses this field for ZONE
                   * (which we  use as PARENT if we
                   * didn't already read in a  
                   * non-NOTHING parent. 
                   */
  
                  if (read_parent) {
                      s_Parent(i, getref(f));
                  } else {
                      s_Parent(i, NOTHING);
                  }
  
                  /*
                   * PENNIES 
                   */
  
                  if (read_money)	/*
                                   *  if not fix in
                                   * unscraw_foreign  
                                   */
                      s_Pennies(i, getref(f));
  
                  /*
                   * FLAGS 
                   */
  
                  f1 = getref(f);
                  if (read_extflags)
                      f2 = getref(f);
                  else
                      f2 = 0;
  
                  if (read_3flags)
                      f3 = getref(f);
                  else
                      f3 = 0;
  
                  s_Flags(i, f1);
                  s_Flags2(i, f2);
                  s_Flags3(i, f3);
  
  
  
                  if (read_powers) {
                      f1 = getref(f);
                      f2 = getref(f);
                      s_Powers(i, f1);
                      s_Powers2(i, f2);
                  }
  
                  /*
                   * ATTRIBUTES 
                   */
  
                  if (read_attribs) {
                      if (!get_list(f, i, read_new_strings)) {
                          fprintf(stderr,
                                  "\nError reading attrs for object #%d\n", i);
                          return -1;
                      }
                  }
                  /*
                   * check to see if it's a player 
                   */
  
                  if (Typeof(i) == TYPE_PLAYER) {
                      c_Connected(i);
                  }
                  break;
              case '*':		/*
                               * EOF marker 
                               */
                  tstr = getstring_noalloc(f, 0);
                  if (strcmp(tstr, "**END OF DUMP***")) {
                      fprintf(stderr, "\nBad EOF marker at object #%d\n", i);
                      return -1;
                  } else {
                      /*
                       * Fix up bizarro foreign DBs 
                       */
  
                      *db_version = g_version;
                      *db_format = g_format;
                      *db_flags = g_flags;
                      load_player_names();
                      return mudstate.db_top;
                  }
              default:
                  fprintf(stderr, "\nIllegal character '%c' near object #%d\n",
                          ch, i);
                  return -1;
          }
  
  
      }
  
  }
  
  static int db_write_object(FILE *f, dbref i, int db_format, int flags) {
      ATTR *a;
      char *got, *as;
      dbref aowner;
      int ca, aflags, save, j;
      BOOLEXP *tempbool;
  
      if (!(flags & V_ATRNAME))
          putstring(f, Name(i));
      putref(f, Location(i));
      if (flags & V_ZONE)
          putref(f, Zone(i));
      putref(f, Contents(i));
      putref(f, Exits(i));
      if (flags & V_LINK)
          putref(f, Link(i));
      putref(f, Next(i));
      if (!(flags & V_ATRKEY)) {
          got = atr_get(i, A_LOCK, &aowner, &aflags);
          tempbool = parse_boolexp(GOD, got, 1);
          free_lbuf(got);
          putboolexp(f, tempbool);
          if (tempbool)
              free_bool(tempbool);
      }
      putref(f, Owner(i));
      if (flags & V_PARENT)
          putref(f, Parent(i));
      if (!(flags & V_ATRMONEY))
          putref(f, Pennies(i));
      putref(f, Flags(i));
      if (flags & V_XFLAGS)
          putref(f, Flags2(i));
      if (flags & V_3FLAGS)
          putref(f, Flags3(i));
      if (flags & V_POWERS) {
          putref(f, Powers(i));
          putref(f, Powers2(i));
      }
      /*
       * write the attribute list 
       */
  
  
      if ((!(flags & V_GDBM)) || (mudstate.panicking == 1)) {
          for (ca = atr_head(i, &as); ca; ca = atr_next(&as)) {
              save = 0;
              a = atr_num(ca);
              if (a)
                  j = a->number;
              else
                  j = -1;
  
              if (j > 0) {
                  switch (j) {
                      case A_NAME:
                          if (flags & V_ATRNAME)
                              save = 1;
                          break;
                      case A_LOCK:
                          if (flags & V_ATRKEY)
                              save = 1;
                          break;
                      case A_LIST:
                      case A_MONEY:
                          break;
                      default:
                          save = 1;
                  }
              }
              if (save) {
                  got = atr_get_raw(i, j);
                  fprintf(f, ">%d\n", j);
                  putstring(f, got);
              }
          }
          fprintf(f, "<\n");
      }
      return 0;
  }
  
  dbref db_write(FILE *f, int format, int version) {
      dbref i;
      int flags;
      VATTR *vp;
  
      switch (format) {
          case F_MUX:
              flags = version;
              break;
          default:
              fprintf(stderr, "Can only write MUX format.\n");
              return -1;
      }
      i = mudstate.attr_next;
      fprintf(f, "+X%d\n+S%d\n+N%d\n", flags, mudstate.db_top, i);
      fprintf(f, "-R%d\n", mudstate.record_players);
  
      /*
       * Dump user-named attribute info 
       */
  
      vp = vattr_first();
      while (vp != NULL) {
          if (!(vp->flags & AF_DELETED))
              fprintf(f, "+A%d\n\"%d:%s\"\n", vp->number, vp->flags,
                      vp->name);
          vp = vattr_next(vp);
      }
  
      DO_WHOLE_DB(i) {
  
          if (!(Going(i))) {
              fprintf(f, "!%d\n", i);
              db_write_object(f, i, format, flags);
          }
      }
      fputs("***END OF DUMP***\n", f);
      fflush(f);
      return (mudstate.db_top);
  }
  