/
teeny/db/
teeny/dbm/
teeny/docs/
teeny/includes/
teeny/misc/
teeny/news/
teeny/text/
/* textdump.c */

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

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

#include "teeny.h"
#include "db.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 line starting with ~, and followed by a number. This number is the version
 * number of the database. TeenyMUD 1.2 can read any version number from 0 to
 * 5, ignoring fields that the server does not support. A summary of version
 * numbers supported by this release: 0 -- original, unmodified TeenyMUD 1.0
 * or 1.1 textdump. 1 -- "standard" TeenyMUD 1.2 dump. 2 -- includes the
 * TIMESTAMP field in the dump. 3 -- includes the DROP/ODROP fields. 4 --
 * contains both TIMESTAMP and DROP/ODROP. 5 -- used to mark a TeenyMUD-C
 * dump. 
 *
 * 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 */

static void     getstring();
static void     skip_line();
static void     getnumber();
static void     getlock();
static void     putnumber();
static void     putstring();
static void     putlock();
static int      convert_flags();

/* figure out what version we are */
#ifdef TIMESTAMPS
#ifdef DROP_FIELDS
static int      dump_version = 4;	/* TIMESTAMP, DROP, and ODROP */

#else
static int      dump_version = 2;	/* TIMESTAMP */

#endif				/* DROP_FIELDS */
#else
#ifdef DROP_FIELDS
static int      dump_version = 3;	/* DROP and ODROP */

#else
static int      dump_version = 1;	/* nothing extra */

#endif				/* DROP_FIELDS */
#endif				/* TIMESTAMPS */


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 ((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] != '%')
  {
    warning("text_load", "could not get object count");
    goto fail;
  }
  objcount = atoi(work + 1);
  if (objcount <= 0)
  {
    warning("text_load", "DB of size <= 0? No way..");
    goto fail;
  }
  /* Set up a blank DB */

  initialize_db(objcount + SLACK, 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 */
      read_version = atoi(work + 1);
      if (read_version > 5)
      {
	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 == 5)
	flags = convert_flags(flags);	/* 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 */

      getstring(i, NAME, in);
      getnumber(i, NEXT, in);

      getnumber(i, SITE, in);
      getnumber(i, LOC, in);
      getnumber(i, HOME, in);
      getnumber(i, OWNER, in);
      getnumber(i, CONTENTS, in);
      getnumber(i, EXITS, in);

      if ((read_version == 2) || (read_version > 3))
      {
#ifdef TIMESTAMPS
	getnumber(i, TIMESTAMP, in);
#else
	skip_line(in);
#endif				/* TIMESTAMPS */
      } else
      {
#ifdef TIMESTAMPS
	stamp(i);
#endif				/* TIMESTAMPS */
      }

      getlock(i, in);

      getstring(i, SUC, in);
      getstring(i, OSUC, in);
      getstring(i, FAIL, in);
      getstring(i, OFAIL, in);
      if (read_version > 2)
      {
#ifdef DROP_FIELDS
	getstring(i, DROP, in);
	getstring(i, ODROP, in);
#else
	skip_line(in);
	skip_line(in);
#endif				/* DROP_FIELDS */
      } else
      {
#ifdef DROP_FIELDS
	if (set_str_elt(i, DROP, (char *) NULL) == -1)
	  goto fail;
	if (set_str_elt(i, ODROP, (char *) NULL) == -1)
	  goto fail;
#endif				/* DROP_FIELDS */
      }
      if (read_version == 5)
      {
	skip_line(in);
	skip_line(in);
      }
      getstring(i, DESC, in);
      getstring(i, GENDER, in);
      if (read_version == 5)
      {
	skip_line(in);
      }
      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. 
 *
 */

static 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");
  }
}

static 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;
  }
}

static 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 (set_int_elt(obj, code, num) == -1)
  {
    warning("getnumber", "could not set int elt in text_load");
  }
}

static void 
getlock(obj, f)
  int             obj;
  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, LOCK, (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, LOCK, lock) == -1)
    warning("getlock", "error setting lock elt in text_load");
}

/*
 * Dump out the database in text format. 
 */

void 
text_dump(name)
  char           *name;
{
  FILE           *out;
  int             obj;
  struct dsc     *thedsc;
  struct obj_data *theobj;

  if ((out = fopen(name, "w")) == NULL)
  {
    warning("text_dump", "could not open dump file");
    return;
  }
  /* Write out a header. */

  fprintf(out,
       "!\n! TeenyMUD 1.2 textdump\n! Version: %d\tActual objects: %d\n!\n",
	  dump_version, actual_objects);
  fprintf(out, "%%%d\n~%d\n", total_objects, dump_version);

  /* Now dump the DB. */

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

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

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

      thedsc = main_index[obj];
      theobj = lookup_obj(obj);

      putnumber(DSC_FLAGS(thedsc), out);
      putstring(DSC_NAME(thedsc), out);
      putnumber(thedsc->list_next, out);

      putnumber(theobj->pennies, out);
      putnumber(theobj->loc, out);
      putnumber(thedsc->home_dropto, out);
      putnumber(DSC_OWNER(thedsc), out);
      putnumber(theobj->contents, out);
      putnumber(theobj->exits, out);
#ifdef TIMESTAMPS
      putnumber(theobj->timestamp, out);
#endif				/* TIMESTAMPS */

      putlock(theobj->lock, out);

#ifndef COMPRESS
      putstring(theobj->suc, out);
      putstring(theobj->osuc, out);
      putstring(theobj->fail, out);
      putstring(theobj->ofail, out);
#ifdef DROP_FIELDS
      putstring(theobj->drop, out);
      putstring(theobj->odrop, out);
#endif				/* DROP_FIELDS */
      putstring(theobj->desc, out);
#else				/* COMPRESS */
      putstring(uncompress(theobj->suc), out);
      putstring(uncompress(theobj->osuc), out);
      putstring(uncompress(theobj->fail), out);
      putstring(uncompress(theobj->ofail), out);
#ifdef DROP_FIELDS
      putstring(uncompress(theobj->drop), out);
      putstring(uncompress(theobj->odrop), out);
#endif				/* DROP_FIELDS */
      putstring(uncompress(theobj->desc), out);
#endif				/* COMPRESS */
      putstring(theobj->gender, out);

      cache_trim();
    } else
    {

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

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

  /* Write a trailer */

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


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

static void 
putnumber(num, f)
  int             num;
  FILE           *f;
{
  char            work[16], *p;

  p = ty_itoa(work, num);
  *p++ = '\n';
  *p = '\0';
  fputs(work, f);
}
static void 
putstring(str, f)
  char           *str;
  FILE           *f;
{
  if (str != NULL)
  {
    fputs(str, f);
  }
  fputs("\n", f);
}
static void 
putlock(lock, f)
  int            *lock;
  FILE           *f;
{
  int             count, i, space;
  char            work[BUFFSIZ], *p, *q;

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

  p = ty_itoa(p, count);
  *p++ = ' ';
  space -= p - work;

  for (i = 1; count > 0 && space > 8; count--, i++)
  {
    q = p;
    p = ty_itoa(p, lock[i]);
    *p++ = ' ';
    space -= p - q;
  }
  p--;
  *p++ = '\n';
  *p = '\0';
  fputs(work, f);
}

/*
 * the following defines and function are for converting TeenyMUD-C style
 * flags into TeenyMUD 1.2 style flags. 
 */

#define O_STICKY	0x0004
#define O_WIZARD	0x0008
#define O_TEMPLE	0x0010
#define O_LINK_OK	0x0020
#define O_DARK		0x0040
#define O_GOD		0x0080
#define O_HAVEN		0x0100
#define O_JUMP_OK	0x0200
#define O_ABODE		0x0400

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

  newflags = (oldflags & TYPE_MASK);

  if (oldflags & O_STICKY)
    newflags |= STICKY;
  if (oldflags & O_WIZARD)
    newflags |= WIZARD;
  if (oldflags & O_TEMPLE)
    newflags |= TEMPLE;
  if (oldflags & O_LINK_OK)
    newflags |= LINK_OK;
  if (oldflags & O_DARK)
    newflags |= DARK;
  if (oldflags & O_GOD)
    newflags |= WIZARD;
  if (oldflags & O_HAVEN)
    newflags |= HAVEN;
  if (oldflags & O_JUMP_OK)
    newflags |= JUMP_OK;
  if (oldflags & O_ABODE)
    newflags |= ABODE;

  newflags |= (oldflags & INTERNAL_FLAGS);

  return newflags;
}