/* db.c */
#include "os.h"
#include "copyright.h"
#include "config.h"
#include "interface.h"
#include "db.h"
#include "attrib.h"
#include "externs.h"
#ifdef MEM_CHECK
#include "mem_check.h"
#endif
struct object *db = 0;
dbref db_top = 0;
dbref errobj;
#ifndef DB_INITIAL_SIZE
#define DB_INITIAL_SIZE 10000
#endif /* DB_INITIAL_SIZE */
dbref db_size = DB_INITIAL_SIZE;
/* manage boolean expression free list */
struct boolexp *alloc_bool (void)
{
#ifdef MEM_CHECK
add_check ("boolexp");
#endif
return ((struct boolexp *) malloc (sizeof (struct boolexp)));
}
void free_bool (struct boolexp *b)
{
if (b != TRUE_BOOLEXP && b) {
free ((char *) b);
#ifdef MEM_CHECK
del_check ("boolexp");
#endif
}
}
char *set_string (char **ptr, char *new)
{
/* if pointer not null unalloc it */
if (*ptr) {
free ((char *) *ptr);
#ifdef MEM_CHECK
del_check ("object_name");
#endif
}
if (!new || !*new)
return (*ptr = NULL);
*ptr = (char *) malloc (strlen (new) + 1);
#ifdef MEM_CHECK
add_check ("object_name");
#endif
strcpy (*ptr, new);
return (*ptr);
}
int db_init = 0;
static void db_grow (dbref newtop)
{
struct object *newdb;
if (newtop > db_top) {
db_top = newtop;
if (!db) {
/* make the initial one */
db_size = (db_init) ? db_init : DB_INITIAL_SIZE;
if ((db = (struct object *)
malloc (db_size * sizeof (struct object))) == NULL) {
fprintf (stderr, "ERROR: out of memory!\n");
fflush (stderr);
abort ();
}
}
/* maybe grow it */
if (db_top > db_size) {
/* make sure it's big enough */
while (db_top > db_size)
db_size *= 2;
if ((newdb = (struct object *)
realloc (db, db_size * sizeof (struct object))) == NULL) {
fprintf (stderr, "ERROR: out of memory!\n");
fflush (stderr);
abort ();
}
db = newdb;
}
}
}
dbref new_object ()
{
dbref newobj;
struct object *o;
#ifdef DESTROY
/* if stuff in free list use it */
if ((newobj = free_get ()) == NOTHING)
/* allocate more space */
#endif /* DESTROY */
{
newobj = db_top;
db_grow (db_top + 1);
}
/* clear it out */
o = db + newobj;
o->name = 0;
o->list = 0;
o->location = NOTHING;
o->contents = NOTHING;
o->exits = NOTHING;
o->next = NOTHING;
o->key = TRUE_BOOLEXP;
o->usekey = TRUE_BOOLEXP;
o->enterkey = TRUE_BOOLEXP;
o->owner = NOTHING;
o->zone = NOTHING;
o->penn = 0;
/* flags you must initialize yourself */
return newobj;
}
void putref (FILE *f, dbref ref)
{
fprintf (f, "%d\n", ref);
}
static void putstring (FILE *f, const char *s)
{
if (s) {
fputs (s, f);
}
putc ('\n', f);
}
static void putbool_subexp (FILE *f, struct boolexp *b)
{
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 (IN_TOKEN, f);
putbool_subexp (f, b->sub1);
putc (')', f);
break;
case BOOLEXP_IND:
putc ('(', f);
putc (AT_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:
fprintf (f, "%s:%s", b->atr_lock->name, uncompress (b->atr_lock->text));
default:
break;
}
}
void putboolexp (f, b)
FILE *f;
struct boolexp *b;
{
if (b != TRUE_BOOLEXP) {
putbool_subexp (f, b);
}
putc ('\n', f);
}
int db_write_object (FILE *f, dbref i)
{
struct object *o;
ALIST *list;
o = db + i;
putstring (f, o->name);
putref (f, o->location);
putref (f, o->contents);
putref (f, o->exits);
putref (f, o->next);
putboolexp (f, o->key);
putboolexp (f, o->usekey);
putboolexp (f, o->enterkey);
putref (f, o->owner);
putref (f, o->zone);
putref (f, Pennies (i));
putref (f, o->flags);
/* write the attribute list */
for (list = o->list; list; list = AL_NEXT (list)) {
if (!AL_BAD (list)) {
fprintf (f, "]%s^%d^%d\n", AL_NAME (list), db[AL_CREATOR (list)].owner,
AL_FLAGS (list));
fprintf (f, "%s\n", uncompress (AL_STR (list)));
}
}
fprintf (f, "<\n");
return 0;
}
dbref db_write (FILE *f)
{
dbref i;
fprintf (f, "+V2\n");
fprintf (f, "~%d\n", db_top);
for (i = 0; i < db_top; i++) {
fprintf (f, "!%d\n", i);
db_write_object (f, i);
}
fputs ("***END OF DUMP***\n", f);
fflush (f);
return (db_top);
}
dbref parse_dbref (const char *s)
{
const char *p;
long x;
x = atol (s);
if (x > 0) {
return x;
} else if (x == 0) {
/* check for 0 */
for (p = s; *p; p++) {
if (*p == '0')
return 0;
if (!isspace ((int)*p))
break;
}
}
/* else x < 0 or s != 0 */
return NOTHING;
}
dbref getref (FILE *f)
{
static char buf[BUFFER_LEN];
fgets (buf, sizeof (buf), f);
return (atol (buf));
}
static char *getstring_noalloc (FILE *f)
{
static char buf[2 * BUFFER_LEN];
char *p;
fgets (buf, sizeof (buf), f);
for (p = buf; *p; p++) {
if (*p == '\n') {
*p = '\0';
break;
}
}
return buf;
}
#define getstring(x,p) {p=NULL; SET(p,getstring_noalloc(x));}
#define getstring_compress(x,p) {p=NULL; SETC(p,getstring_noalloc(x));}
static struct boolexp *getboolexp1 (FILE *f)
{
struct boolexp *b;
int c;
char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];
c = getc (f);
switch (c) {
case '\n':
ungetc (c, f);
return TRUE_BOOLEXP;
/* break; */
case EOF:
fprintf (stderr, "ERROR: Unexpected EOF in boolexp. Object #%d", errobj);
return TRUE_BOOLEXP;
/*NOTREACHED*/ break;
case '(':
b = alloc_bool ();
if ((c = getc (f)) == NOT_TOKEN) {
b->type = BOOLEXP_NOT;
b->sub1 = getboolexp1 (f);
if (getc (f) != ')')
goto error;
return b;
} else if (c == IS_TOKEN) {
b->type = BOOLEXP_IS;
b->sub1 = getboolexp1 (f);
if (getc (f) != ')')
goto error;
return b;
} else if (c == IN_TOKEN) {
b->type = BOOLEXP_CARRY;
b->sub1 = getboolexp1 (f);
if (getc (f) != ')')
goto error;
return b;
} else if (c == AT_TOKEN) {
b->type = BOOLEXP_IND;
b->sub1 = getboolexp1 (f);
if (getc (f) != ')')
goto error;
return b;
} else {
ungetc (c, f);
b->sub1 = getboolexp1 (f);
switch (c = getc (f)) {
case AND_TOKEN:
b->type = BOOLEXP_AND;
break;
case OR_TOKEN:
b->type = BOOLEXP_OR;
break;
default:
goto error;
/* break */
}
b->sub2 = getboolexp1 (f);
if (getc (f) != ')')
goto error;
return b;
}
/* break; */
case '-':
/* obsolete NOTHING key */
/* eat it */
while ((c = getc (f)) != '\n')
if (c == EOF) {
fprintf (stderr, "ERROR: Unexpected EOF in boolexp. Object #%d\n",
errobj);
return TRUE_BOOLEXP;
}
ungetc (c, f);
return TRUE_BOOLEXP;
/* break */
default:
/* can be either a dbref or : seperated string */
ungetc (c, f);
b = alloc_bool ();
b->type = BOOLEXP_CONST;
b->thing = 0;
/* constant dbref */
if (isdigit ((int)(c = getc (f)))) {
while (isdigit ((int)c)) {
b->thing = b->thing * 10 + c - '0';
c = getc (f);
}
switch (c) {
case ':': /* old style boolexp lock */
{
char *p;
for (p = tbuf1;
((c = getc (f)) != EOF) && (c != '\n') && (c != ')'); *p++ = c);
*p = '\0';
if (c == EOF)
goto error;
b->atr_lock = alloc_atr (convert_atr (b->thing), tbuf1);
b->thing = 0;
b->type = BOOLEXP_ATR;
/* this is only needed because of the braindeath of the previous
* version of atrlocks.. bleah.
*/
if (c == '\n')
return (b);
}
default:
ungetc (c, f);
break;
}
return (b);
} else {
/* MUST be a colon seperated string for attrib lock */
char *p = tbuf1, *s;
*p++ = c;
for (; ((c = getc (f)) != EOF) && (c != '\n') && (c != ':'); *p++ = c);
*p = '\0';
if (c == EOF || c == '\n')
goto error;
if (c != ':')
goto error;
for (s = tbuf2;
((c = getc (f)) != EOF) && (c != '\n') && (c != ')' &&
(c != OR_TOKEN) && (c != AND_TOKEN)); *s++ = c);
if (c == EOF)
goto error;
*s++ = 0;
ungetc (c, f);
b->atr_lock = alloc_atr (tbuf1, tbuf2);
b->type = BOOLEXP_ATR;
return (b);
}
}
error:
fprintf (stderr, "ERROR: Unknown error in boolexp. Object #%d\n", errobj);
return TRUE_BOOLEXP;
}
struct boolexp *getboolexp (FILE *f)
{
struct boolexp *b;
b = getboolexp1 (f);
if (getc (f) != '\n') {
fprintf (stderr, "ERROR: Invalid boolexp format on object #%d\n", errobj);
return TRUE_BOOLEXP;
}
return b;
}
void free_boolexp (struct boolexp *b)
{
if (b != TRUE_BOOLEXP && b) {
switch (b->type) {
case BOOLEXP_AND:
case BOOLEXP_OR:
free_boolexp (b->sub1);
free_boolexp (b->sub2);
free_bool (b);
break;
case BOOLEXP_NOT:
case BOOLEXP_CARRY:
case BOOLEXP_IND:
case BOOLEXP_IS:
free_boolexp (b->sub1);
free_bool (b);
break;
case BOOLEXP_CONST:
free_bool (b);
break;
case BOOLEXP_ATR:
if (b->atr_lock->name)
free ((char *) b->atr_lock->name);
if (b->atr_lock->text)
free ((char *) b->atr_lock->text);
if (b->atr_lock)
free ((char *) b->atr_lock);
#ifdef MEM_CHECK
del_check ("bool_atr");
del_check ("bool_atr_name");
del_check ("bool_atr_val");
#endif
free_bool (b);
break;
}
}
}
struct boolexp *dup_bool (struct boolexp *b)
{
struct boolexp *r;
if (b == TRUE_BOOLEXP)
return (TRUE_BOOLEXP);
r = alloc_bool ();
switch (r->type = b->type) {
case BOOLEXP_AND:
case BOOLEXP_OR:
r->sub2 = dup_bool (b->sub2);
case BOOLEXP_NOT:
case BOOLEXP_IND:
case BOOLEXP_IS:
case BOOLEXP_CARRY:
r->sub1 = dup_bool (b->sub1);
case BOOLEXP_CONST:
r->thing = b->thing;
break;
case BOOLEXP_ATR:
r->atr_lock = alloc_atr (b->atr_lock->name, b->atr_lock->text);
break;
default:
fprintf (stderr, "ERROR: bad bool type in dup_bool!\n");
return (TRUE_BOOLEXP);
}
return (r);
}
void db_free ()
{
dbref i;
struct object *o;
if (db) {
for (i = 0; i < db_top; i++) {
o = &db[i];
SET (o->name, NULL);
atr_free (i);
if (o->key)
free_boolexp (o->key);
}
free ((char *) db);
db = NULL;
db_init = db_top = '\0';
}
}
/* read attribute list */
static int get_list (FILE *f, dbref i)
{
int c;
char *p, *q;
char tbuf1[BUFFER_LEN];
db[i].list = NULL;
while (1)
switch (c = getc (f)) {
case ']': /* new style attribs, read name then value */
strcpy (tbuf1, getstring_noalloc (f));
if (!(p = index (tbuf1, '^'))) {
fprintf (stderr, "ERROR: Bad format on new attributes. object #%d\n",
i);
return 0;
}
*p++ = '\0';
if (!(q = index (p, '^'))) {
fprintf (stderr, "ERROR: Bad format on new attributes. object #%d\n",
i);
return 0;
}
*q++ = '\0';
atr_new_add (i, tbuf1, getstring_noalloc (f), atoi (p),
((q) ? atoi (q) : NOTHING));
break;
case '>': /* old style attribs, read # then value */
atr_new_add (i, convert_atr (getref (f)), getstring_noalloc (f),
db[i].owner, NOTHING);
break;
case '<': /* end of list */
if ('\n' != getc (f)) {
fprintf (stderr, "ERROR: no line feed on object %d\n", i);
return (0);
}
return (1);
default:
if (c == EOF) {
fprintf (stderr, "ERROR: Unexpected EOF on file.\n");
return (0);
}
fprintf (stderr, "ERROR: Bad character %c on object %d\n", c, i);
return (0);
}
return 0;
}
dbref db_read (FILE *f)
{
int c;
dbref i;
const char *dummy;
struct object *o;
const char *end;
clear_players ();
db_free ();
for (i = 0;; i++) {
errobj = i;
c = getc (f);
switch (c) {
/* make sure database is at least this big *1.5 */
case '~':
db_init = (getref (f) * 3) / 2;
break;
/* skip over MUSH 2.0 header stuff so we can move up eventually */
case '+':
dummy = getstring_noalloc (f);
break;
/* old fashioned database */
case '#':
/* another entry, yawn */
if (i != getref (f))
return -1; /* we blew it */
/* make space */
db_grow (i + 1);
/* read it in */
o = db + i;
o->list = NULL;
getstring (f, o->name);
s_Desc (i, getstring_noalloc (f));
o->location = getref (f);
o->contents = getref (f);
o->exits = getref (f);
o->next = getref (f);
o->key = getboolexp (f);
#ifdef ADD_LOCKS
o->usekey = TRUE_BOOLEXP;
o->enterkey = TRUE_BOOLEXP;
#else
o->usekey = getboolexp (f);
o->enterkey = getboolexp (f);
#endif
s_Fail (i, getstring_noalloc (f));
s_Succ (i, getstring_noalloc (f));
s_Ofail (i, getstring_noalloc (f));
s_Osucc (i, getstring_noalloc (f));
o->owner = getref (f);
#ifdef ADD_ZONES
o->zone = NOTHING;
#else
o->zone = getref (f);
#endif
s_Pennies (i, getref (f));
o->flags = getref (f);
s_Pass (i, getstring_noalloc (f));
/* check to see if it's a player */
if (Typeof (i) == TYPE_PLAYER)
add_player (i);
break;
/* new database */
case '!': /* non-zone oriented database */
case '&': /* zone oriented database */
/* make space */
i = getref (f);
db_grow (i + 1);
/* read it in */
o = db + i;
getstring (f, o->name);
o->location = getref (f);
/* --- get zone number --- */
(c == '&') ? (int) getref (f) : 0;
/* ----------------------- */
o->contents = getref (f);
o->exits = getref (f);
o->next = getref (f);
o->key = getboolexp (f);
#ifdef ADD_LOCKS
o->usekey = TRUE_BOOLEXP;
o->enterkey = TRUE_BOOLEXP;
#else
o->usekey = getboolexp (f);
o->enterkey = getboolexp (f);
#endif
o->owner = getref (f);
#ifdef ADD_ZONES
o->zone = NOTHING;
#else
o->zone = getref (f);
#endif
s_Pennies (i, getref (f));
o->flags = getref (f);
/* read attribute list for item */
if (!get_list (f, i)) {
fprintf (stderr, "ERROR: bad attribute list object %d\n", i);
return -1;
}
/* check to see if it's a player */
if (Typeof (i) == TYPE_PLAYER) {
add_player (i);
db[i].flags &= ~PLAYER_CONNECT;
}
break;
case '*':
end = getstring_noalloc (f);
if (strcmp (end, "**END OF DUMP***")) {
fprintf (stderr, "ERROR: No end of dump %d\n", i);
return -1;
} else {
{
fprintf (stderr, "done\n");
FIX;
return db_top;
}
}
default:
fprintf (stderr, "ERROR: failed object %d\n", i);
return -1;
}
}
}