dbm/
misc/
old-docs/
/* textdump.c */

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

#include <stdio.h>
#include <ctype.h>
#if defined(ALLOCA_H) && !defined(NO_ALLOCA)
#include <alloca.h>
#endif

#include "teeny.h"
#include "db.h"
#include "strlist.h"

/*
 * Provides a simple text dump format for TeenyMUD. a la TinyMUD. This should
 * be considered as more or less an adjunct component of the database proper,
 * since it roots around directly with DB structures.
 * 
 * Dump format:
 * 
 * Preamble of lines starting with !'s. These are comments.
 * 
 * A line starting with &, and followed by a number. This number is the total
 * number of objects contained in the dump (including garbage)
 * 
 * A sequence of objects. Real objects start with a line like #127 to indicate
 * object number, and are followed by a sequence of lines, one per field,
 * describing the object data. Garbage objects are coded by a single line
 * like @1283.
 * 
 * A line at the end **** END OF DUMP ****
 */

extern struct dsc **main_index;
extern int      total_objects;
extern int      actual_objects;
extern int      garbage_count;
extern struct obj_data *lookup_obj();

#ifdef COMPRESS
extern char    *uncompress();

#endif				/* COMPRESS */

void            getstring();
void            getstrings();
void            skip_line();
void            getnumber();
void            getlock();
void            putnumber();
void            putstrings();
void            putlock();
static int      convert_flags();
static void     convert_name();
static void     convert_gender();

int             text_load(name)
  char           *name;
{
  FILE           *in;
  char            work[BUFFSIZ + 16];
  int             objcount, objnum, i;
  int             garbagecount;
  int             done;
  int             flags, creatednum, objflags;
  int             read_version = 0;
  extern int      slack;

  if (name[strlen(name) - 1] == 'Z') {
    sprintf(work, "uncompress < %s", name);
    if ((in = popen(work, "r")) == NULL) {
      warning("text_load", "could not open dump file");
      return (-1);
    }
  } else {
    if ((in = fopen(name, "r")) == NULL) {
      warning("text_load", "could not open dump file");
      return (-1);
    }
  }
  /* Read and ignore the preamble */

  do {
    if (fgets(work, BUFFSIZ + 16, in) == NULL) {
      warning("text_load", "bad preamble");
      goto fail;
    }
  } while (work[0] == '!');

  if (work[0] == '%') {
    objcount = atoi(work + 1);
  } else if (work[0] == '&') {
    char           *z;

    objcount = atoi(work + 1);
    for (z = work; *z && *z != ' '; z++);
    if (!*z)
      read_version = 100;
    else {
      z++;
      read_version = atoi(z);
    }
  } else {
    warning("text_load", "couldn't object count");
    goto fail;
  }

  if (objcount <= 0) {
    warning("text_load", "DB of size <= 0? No way..");
    goto fail;
  }
  /* Set up a blank DB */

  initialize_db(objcount + SLACK);
  slack = objcount + SLACK;

  /*
   * We kludge the garbage objects a bit. We track them, and every time we
   * find a garbage object, we count it. We don't touch garbage_objects,
   * though, so the db code thinks there aren't any until the end when we
   * tell it all of a sudden. This means the db code will keep numbers in
   * synch for us.
   */

  garbagecount = 0;

  /* Do it to it. Loop away sucking in objects and building them */

  done = 0;
  for (i = 0; i < objcount && !done; i++) {
    if (fgets(work, BUFFSIZ + 16, in) == NULL)
      goto fail;

    switch (work[0]) {
    case '~':			/* version number */
      if (read_version > 99) {
	warning("text_load", "found version number in new style db");
	goto fail;
      }
      read_version = atoi(work + 1);
      if (read_version > 4) {
	warning("text_load", "input DB version not supported");
	goto fail;
      }
      i--;			/* a kludge to fix the for loop */
      break;
    case '#':			/* Actual object */
      objnum = atoi(work + 1);
      if (objnum != i) {
	warning("text_load", "input DB is out of synch");
	goto fail;
      }
      if (fgets(work, BUFFSIZ + 16, in) == NULL)
	goto fail;
      flags = atoi(work);
      if (read_version < 102)
	flags = convert_flags(flags, read_version);	/* convert 'em */
      creatednum = create_obj(flags & TYPE_MASK);

      if (creatednum != objnum) {
	warning("text_load"
		,"input db and internal db out of synch.");
	goto fail;
      }
      /* Set up the object flags, being CAREFUL */

      if (get_int_elt(i, FLAGS, &objflags) == -1)
	goto fail;
      flags = (flags & ~INTERNAL_FLAGS)
	| (objflags & INTERNAL_FLAGS);
      if (set_int_elt(i, FLAGS, flags) == -1)
	goto fail;

      /* Now suck in all the data elements */
      if (read_version < 100) {
	getstring(i, NAME, in);
	if (isplayer(i)) {
	  char           *z;
	  (void) get_str_elt(i, NAME, &z);
	  convert_name(i, z);
	}
      }
      getnumber(i, NEXT, in);

      getnumber(i, QUOTA, in);
      getnumber(i, LOC, in);
      getnumber(i, HOME, in);
      getnumber(i, OWNER, in);
      getnumber(i, CONTENTS, in);
      getnumber(i, EXITS, in);
      if (read_version > 99) {
	getnumber(i, ROOMS, in);
      }
      if ((read_version == 2) || (read_version > 3)) {
	getnumber(i, TIMESTAMP, in);
      } else {
	stamp(i);
      }

      getlock(i, LOCK, in);
      if (read_version > 99) {
	getlock(i, ELOCK, in);
	getlock(i, DESTINATIONS, in);
      }
      if (read_version < 99) {
	getstring(i, SUC, in);
	getstring(i, OSUC, in);
	getstring(i, FAIL, in);
	getstring(i, OFAIL, in);
	if (read_version > 2) {
	  getstring(i, DROP, in);
	  getstring(i, ODROP, in);
	}
	getstring(i, DESC, in);
	if (fgets(work, BUFFSIZ + 16, in) == NULL)
	  goto fail;
	convert_gender(i, work);
      } else {
	getstrings(i, in, read_version);
      }
      break;
    case '@':			/* Garbage object */
      garbagecount++;
      total_objects++;		/* Fake out the DB */
      main_index[i] = (struct dsc *) NULL;
      break;
    case '*':			/* End of file */
      done = 1;
      break;
    }
  }

  garbage_count = garbagecount;

  /* total_objects is already correct */

  (void) fclose(in);
  return (0);
fail:
  (void) fclose(in);
  warning("text_load", "load failed");
  return (-1);
}

/*
 * Generic routines for reading junk in to an object.
 * 
 */

void            getstring(obj, code, f)
  int             obj;
  int             code;
  FILE           *f;
{
  char            work[BUFFSIZ + 16];
  char           *p;
  int             ret;

  if (fgets(work, BUFFSIZ + 16, f) == NULL) {
    warning("get_string", "unexpected EOF in text_load");
    return;
  }
  for (p = work; *p != '\n' && *p; p++);
  *p = '\0';
  if (p == work) {
    ret = set_str_elt(obj, code, (char *) NULL);
  } else {
    ret = set_str_elt(obj, code, work);
  }
  if (ret == -1) {
    warning("getstring", "error setting string elt in text_load");
  }
}

void            skip_line(f)
  FILE           *f;
{
  char            temp[BUFFSIZ + 16];

  if (fgets(temp, BUFFSIZ + 16, f) == NULL) {
    warning("skip_line", "unexpected EOF in text_load");
    return;
  }
}

void            getnumber(obj, code, f)
  int             obj;
  int             code;
  FILE           *f;
{
  char            work[BUFFSIZ + 16];
  int             num;

  if (fgets(work, BUFFSIZ + 16, f) == NULL) {
    warning("getnumber", "unexpected EOF in text_load");
    return;
  }
  if ((!isdigit(work[0])) && !(work[0] == '-' && isdigit(work[1]))) {
    warning("getnumber", "bad integer read in text_load");
    return;
  }
  num = atoi(work);
  if (code == TIMESTAMP && (num < 32000000))	/* 16bit */
    num *= 60;
  if (set_int_elt(obj, code, num) == -1) {
    warning("getnumber", "could not set int elt in text_load");
  }
}

static void     getlock(obj, elt, f)
  int             obj, elt;
  FILE           *f;
{
  char            work[BUFFSIZ + 16], *p;
  int             size, *lock, i;

  if (fgets(work, BUFFSIZ + 16, f) == NULL) {
    warning("get_string", "unexpected EOF in text_load");
    return;
  }
  /* OK. Locks are slightly complex. We malloc memory HERE for it */

  if (work[0] == '\n') {
    if (set_lock_elt(obj, elt, (int *) NULL) == -1)
      warning("getlock", "error setting lock elt in text_load");
    return;
  }
  size = atoi(work);
  if (size <= 0) {
    warning("getlock", "bad lock field in text database");
    return;
  }
  lock = (int *) ty_malloc(sizeof(int) * (size + 1), "getlock");
  lock[0] = size;

  p = work;
  for (i = 1; i <= size; i++) {
    while (!isspace(*p) && *p)
      p++;
    while (isspace(*p))
      p++;

    if (*p == '\0') {
      warning("getlock", "bad lock field in text database");
      return;
    }
    lock[i] = atoi(p);
  }
  if (set_lock_elt(obj, elt, lock) == -1)
    warning("getlock", "error setting lock elt in text_load");
}

static void     getstrings(obj, f, version)
  int             obj;
  FILE           *f;
  int             version;
{
  char            work[BUFFSIZ + 16];
  int             ret, code = 0;
  char           *p;

  do {
    if (fgets(work, BUFFSIZ + 16, f) == NULL) {
      warning("getstrings", "unexpected EOF in text_load");
      return;
    }
    for (p = work; *p && *p != '\n'; p++);
    *p = '\0';

    if (p != work && work[0] != '<') {
      for (p = work; *p && *p != ':'; p++);
      *p++ = '\0';

      code = atoi(work);
      if (code > 0) {
	if (version < 103 && code == NAME && isplayer(obj)) {
	  convert_name(obj, p);
	} else if (version < 104 && code == 19 /* GENDER */ ) {
	  convert_gender(obj, p);
	} else
	  ret = set_str_elt(obj, code, p);
	if (ret == -1) {
	  warning("getstrings", "error setting string elt in text_load");
	}
      } else {
	warning("getstrings", "error getting code");
	return;
      }
    }
  } while (work[0] != '<');
}

/*
 * Dump out the database in text format, with the specified offset.
 */

void            text_dump(name, offset)
  char           *name;
{
  FILE           *out;
  int             obj;
  struct dsc     *thedsc;
  struct obj_data *theobj;
  char            cmd[256];

  if (offset < 1)
    offset = 0;

  if (name[strlen(name) - 1] == 'Z') {
    sprintf(cmd, "compress > %s", name);
    if ((out = popen(cmd, "w")) == NULL) {
      warning("text_dump", "could open dump file");
      return;
    }
  } else {
    if ((out = fopen(name, "w")) == NULL) {
      warning("text_dump", "could not open dump file");
      return;
    }
  }
  /* Write out a header. */

  if (offset == 0) {
    fprintf(out,
	    "!\n! %s textdump\n! Actual objects: %d\n!\n", version,
	    actual_objects);
    fprintf(out, "&%d 105\n", total_objects);
  } else {
    fprintf(out,
	    "!\n! %s offset textdump\n! Actual objects: %d, Offset: %d\n!\n",
	    version, actual_objects, offset);
    fprintf(out, "&%d 105\n", total_objects);
  }

  /* Now dump the DB. */

  for (obj = 0; obj < total_objects; obj++) {
    if (exists_object(obj)) {

      /* Go get the object, and dump it */

      thedsc = main_index[obj];
      theobj = lookup_obj(obj);
      if (theobj == NULL) {	/* awe, fuck */
	log_error("text_dump: couldn't load obj #%d! writing it as garbage.\n",
		  obj);
	fprintf(out, "@%d\n", obj + offset);
      } else {

	fprintf(out, "#%d\n", obj + offset);

	putnumber(DSC_FLAGS(thedsc), out);
	putnumber((thedsc->list_next + offset), out);

	putnumber(theobj->pennies, out);	/* quota */
	putnumber((theobj->loc + offset), out);
	putnumber((thedsc->home_dropto + offset), out);
	putnumber((DSC_OWNER(thedsc) + offset), out);
	putnumber((theobj->contents + offset), out);
	putnumber((theobj->exits + offset), out);
	putnumber((theobj->rooms + offset), out);
	putnumber(theobj->timestamp, out);

	putlock(theobj->lock, out, offset);
	putlock(theobj->elock, out, offset);
	putlock(theobj->destinations, out, offset);

	putstrings(obj, out);

	cache_trim();
      }
    } else {

      /* This object does not exist. Make a garbage entry */

      fprintf(out, "@%d\n", obj + offset);
    }
  }

  /* Write a trailer */

  fputs("**** END OF DUMP ****\n", out);
  (void) fclose(out);
}


/*
 * General output routines for text_dump()
 */

void            putnumber(num, f)
  int             num;
  FILE           *f;
{
  (void) fprintf(f, "%d\n", num);
}
void            putstrings(obj, f)
  int             obj;
  FILE           *f;
{
  StringList     *strs;
  char           *p;

  for (strs = Strings; strs->code; strs++) {
    if (get_str_elt(obj, strs->code, &p) == -1) {
      log_error("text_dump: bad %s string on object %d. cleared.\n",
		strs->name, obj);
      p = NULL;
    }
    if (p != NULL)
      fprintf(f, "%d:%s\n", strs->code, p);
  }
  fputs("<\n", f);
}
void            putlock(lock, f, offset)
  int            *lock;
  FILE           *f;
  int             offset;
{
  int             count, i;

  if (lock == NULL || lock[0] == 0) {
    fputs("\n", f);
    return;
  }
  count = lock[0];		/* How many FOLLOW */

  (void) fprintf(f, "%d", count);

  for (i = 1; count > 0; count--, i++) {
    if ((offset == 0) || (lock[i] < 0))
      (void) fprintf(f, " %d", lock[i]);
    else
      (void) fprintf(f, " %d", lock[i] + offset);
  }
  fputs("\n", f);
}

/* for converting from TeenyMUD 1.x to beyond */

static int      convert_flags(oldflags, version)
  int             oldflags, version;
{
  int             newflags;

  if (version < 99) {		/* 1.0 - 1.2 */
    if (oldflags & 0x0004)
      newflags |= STICKY;
    if (oldflags & 0x0008)
      newflags |= WIZARD;
    if (oldflags & 0x0020)
      newflags |= LINK_OK;
    if (oldflags & 0x0040)
      newflags |= DARK;
    if (oldflags & 0x0080)
      newflags |= HAVEN;
    if (oldflags & 0x0100)
      newflags |= JUMP_OK;
    if (oldflags & 0x0200)
      newflags |= ABODE;
    if (oldflags & 0x0400)
      newflags |= BUILDER;
  }
  if (version < 101) {
    switch (oldflags & 0x0003) {
    case 0:
      newflags = TYP_PLAYER;
      break;
    case 1:
      newflags = TYP_THING;
      break;
    case 2:
      newflags = TYP_ROOM;
      break;
    case 3:
      newflags = TYP_EXIT;
      break;
    }
  }
  if (version < 102) {
    newflags = (oldflags & 0x00000003);
    if (oldflags & 0x00000004) {/* STICKY */
      oldflags &= ~0x00000004;
      oldflags |= STICKY;
    }
    newflags |= (oldflags & ~(0x00000003 | INTERNAL_FLAGS));
  }
  if (version < 105) {		/* TEMPLE */
    newflags = (oldflags & ~0x00000010);
  }
  return newflags;
}

/* convert an old player name */

static void     convert_name(obj, str)
  int             obj;
  char           *str;
{
  char           *work, *p;

#ifdef NO_ALLOCA
  work = (char *) ty_malloc(strlen(str) + 1, "convert_name");
#else
  work = (char *) alloca(strlen(str) + 1);
#endif
  (void) strcpy(work, str);

  p = work;
  while (*p && *p != ' ')
    p++;
  *p++ = 0;
  if (set_str_elt(obj, NAME, work) == -1)
    warning("convert_name", "couldn't set player name");

  if (set_str_elt(obj, PASSWORD, crypt(p, CRYPT_KEY)) == -1)
    warning("convert_name", "couldn't set player pword");

#ifdef NO_ALLOCA
  ty_free(work);
#endif
}

static void     convert_gender(obj, str)
  int             obj;
  char           *str;
{
  int             flags;

  if (str == NULL || !isplayer(obj))
    return;

  if (get_int_elt(obj, FLAGS, &flags) == -1)
    return;

  switch (str[0]) {
  case 'N':
  case 'n':
  case 'I':
  case 'i':
    flags |= GENDER_NEUTER;
    break;
  case 'F':
  case 'f':
    flags |= GENDER_FEMALE;
    break;
  case 'M':
  case 'm':
    flags |= GENDER_MALE;
    break;
  default:
    return;
  }

  (void) set_int_elt(obj, FLAGS, flags);
}