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