/
teeny/db/
teeny/dbm/
teeny/docs/
teeny/includes/
teeny/misc/
teeny/news/
teeny/text/
#include "copyright.h"
#include "config.h"

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

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

/*
 * Increments a TeenyMud db, using the same methods that db/textdump.c
 * uses.
 *
 * 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 **** 
 */

int		Garbage = 0;

extern char    *malloc();
extern void     free();

#ifdef TIMESTAMPS
void stamp(int, FILE *);
#endif


int             Increment = 0;
int             total_objects = 0;
char           *prog;



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 */


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

  /* 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;
  }

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

  done = 0;
  for (i = 0; !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 */
      fprintf(out, "#%d\n%d\n", objnum + Increment, flags);

      /* Now suck in all the data elements */

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

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

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

      getlock(i, in, out);

      getstring(i, SUC, in, out);
      getstring(i, OSUC, in, out);
      getstring(i, FAIL, in, out);
      getstring(i, OFAIL, in, out);
      if (read_version > 2)
      {
#ifdef DROP_FIELDS
	getstring(i, DROP, in, out);
	getstring(i, ODROP, in, out);
#else
	skip_line(in);
	skip_line(in);
#endif				/* DROP_FIELDS */
      } else
      {
#ifdef DROP_FIELDS
        fprintf(out, "\n\n");
#endif				/* DROP_FIELDS */
      }
      if (read_version == 5)
      {
	skip_line(in);
	skip_line(in);
      }
      getstring(i, DESC, in, out);
      getstring(i, GENDER, in, out);
      if (read_version == 5)
      {
	skip_line(in);
      }
      break;
    case '@':			/* Garbage object */
      objnum = atoi(work + 1);
      if (objnum != i)
      {
	warning("text_load", "input DB is out of synch");
	goto fail;
      }
      fprintf(out, "@%d\n", objnum + Increment);
      break;
    case '*':			/* End of file */
      done = 1;
      break;
    }
  }

  /* 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, outf)
  int             obj;
  int             code;
  FILE           *f;
  FILE		 *outf;
{
  char            work[BUFFSIZ + 16];
  char           *p;
  int             ret;

  if (fgets(work, BUFFSIZ + 16, f) == NULL)
  {
    warning("get_string", "unexpected EOF in text_load");
    return;
  }
  fputs(work, outf);
/*
  for (p = work; *p != '\n' && *p; p++);
  *p = '\0';
  if (p == work)
  {
    fprintf(out,"\n");
  } else
  {
    fprintf(out,"%s\n",work);
  }
*/
}

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, outf)
  int             obj;
  int             code;
  FILE           *f;
  FILE           *outf;
{
  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 != SITE && code != TIMESTAMP)
  {
    if(num >= 0)
      num += Increment;
  }
  fprintf(outf,"%d\n",num);
}

static void 
getlock(obj, f, outf)
  int             obj;
  FILE           *f;
  FILE           *outf;
{
  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')
  {
    fputs(work, outf);
    return;
  }
  size = atoi(work);
  if (size <= 0)
  {
    warning("getlock", "bad lock field in text database");
    fprintf(outf,"\n");
    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(lock[i] >= 0) lock[i] += Increment;
  }
  putlock(lock, outf);
}


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

void main(int argc, char **argv)
{
  FILE *in;
  FILE *out;
  FILE *temp;
  char temp_path[80];
  char work[BUFFSIZ + 16];

  prog = argv[0];

  if (argc < 3 || argc > 5)
  {
    fprintf(stderr, "Usage: %s <input db> <output db> [<temp db>] [<#>]\n",
            prog);
    exit(-1);
  }
  if (!strcmp(argv[1], "-"))
  {
    in = stdin;
  } else
  {
    if ((in = fopen(argv[1], "r")) == NULL)
    {
      fprintf(stderr, "%s: couldn't open %s.\n", prog, argv[1]);
      exit(-1);
    }
  }
  if ((out = fopen(argv[2], "w")) == NULL)
  {
    fprintf(stderr, "%s: couldn't create %s.\n", prog, argv[2]);
    exit(-1);
  }
  if (argc > 3)
  {
    if (strlen(argv[3]) > 80)
    {
      fprintf(stderr, "%s: %s - path too long.\n", prog, argv[3]);
      exit(-1);
    }
    strcpy(temp_path, argv[3]);
  } else
  {
    sprintf(temp_path, "/tmp/cnvt%d", getpid());
  }
  if ((temp = fopen(temp_path, "w")) == NULL)
  {
    fprintf(stderr, "%s: couldn't create %s.\n", prog, temp_path);
    exit(-1);
  }
  if (argc == 5)
    Increment = atoi(argv[4]);
  if (convert_file(in, temp) == -1)
  {
    fprintf(stderr, "%s: conversion failed.\n", prog);
    exit(-1);
  }
  fclose(temp);
  if (Increment > 0)
  {
    fprintf(stderr, "%s: unfinished db left in %s, offset %d objects.\n",
            prog, temp_path, Increment);
    exit(1);
  }
#ifdef TIMESTAMPS
  fprintf(out, "!\n! TinyMUD -> TeenyMUD converter dump\n!\n%%%d\n~2\n",
          total_objects);
#else                           /* TIMESTAMPS */
  fprintf(out, "!\n! TinyMUD -> TeenyMUD converter dump\n!\n%%%d\n~1\n",
          total_objects);
#endif                          /* TIMESTAMPS */
  fflush(out);
  if ((temp = fopen(temp_path, "r")) == NULL)
  {
    fprintf(stderr, "%s: couldn't reopen %s for read.\n", prog, temp_path);
    exit(-1);
  }
  while (fgets(work, BUFFSIZ + 16, temp) != NULL)
  {
    fputs(work, out);
  }
  fflush(out);
  fputs("*** End of converter dump ***\n", out);
  fflush(out);
  fclose(out);
  fclose(temp);
  unlink(temp_path);

  exit(0);
}


#ifdef TIMESTAMPS
void 
stamp(obj,outf)
  int obj;
  FILE *outf;
{
  struct timeval  foo;

  (void) gettimeofday(&foo, (struct timezone *) 0);

  fprintf(outf, "%d\n", (int) (foo.tv_sec / 60));
}
#endif

/*
 * Reliable memory allocation. 
 */

char           *
ty_malloc(n, util)
  int             n;
  char           *util;		/* Identifies the calling routine */

{
  char           *p;
  extern char    *malloc();

  if ((p = (char *) malloc((size_t) n)) == NULL)
  {
    fatal(util, "memory allocation failed");
  }
  return (p);
}

/*
 * Reliable free(), which does NULL OK. 
 */

void 
ty_free(p)
  char           *p;
{
  if (p == NULL)
  {
    return;			/* Ok */
  }
  free(p);
}

/*
 * Converts ints to strings. 
 *
 */

char           *
ty_itoa(p, i)

  char           *p;
  int             i;

{
  char           *ty_itoa_prim();

  if (i < 0)
  {
    i = -i;
    *p++ = '-';
  } else
  if (i == 0)
  {
    *p++ = '0';
    return (p);
  }
  return (ty_itoa_prim(p, i));
}

/* recursively does it to it. Hee! Russ would love me. */

char           *
ty_itoa_prim(p, i)
  char           *p;
  int             i;
{
  if (i == 0)
  {
    return (p);
  } else
  {
    p = ty_itoa_prim(p, i / 10);
    *p++ = (char) (i % 10) + '0';
    return (p);
  }
}

warning(str1, str2)
  char *str1;
  char *str2;
{
  fprintf(stderr,"%s(warning): %s\n", str1, str2);
}