/* db.c */
#include "copyright.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "config.h"
#include "interface.h"
#include "db.h"
#include "attrib.h"
#include "externs.h"
#ifdef MEM_CHECK
#include "mem_check.h"
#endif
#ifdef USE_NALLOC
#include "nalloc.h"
NALLOC *db_strings = NULL;
#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;
extern char ccom[];
/* manage boolean expression free list */
struct boolexp *alloc_bool()
{
#ifdef MEM_CHECK
add_check("boolexp");
#endif
#ifdef USE_NALLOC
return ((struct boolexp *) na_alloc(db_strings, sizeof(struct boolexp)));
#else
return ((struct boolexp *) malloc(sizeof(struct boolexp)));
#endif
}
void free_bool(b)
struct boolexp *b;
{
if (b != TRUE_BOOLEXP && b) {
#ifdef USE_NALLOC
na_unalloc(db_strings, (char *) b);
#else
free((char *) b);
#endif
#ifdef MEM_CHECK
del_check("boolexp");
#endif
}
}
char *set_string(ptr, new)
char **ptr;
char *new;
{
/* if pointer not null unalloc it */
if (*ptr) {
#ifdef USE_NALLOC
na_unalloc(db_strings, *ptr);
#else
free((char *) *ptr);
#endif
#ifdef MEM_CHECK
del_check("object_name");
#endif
}
if (!new || !*new)
return (*ptr = NULL);
#ifdef USE_NALLOC
*ptr = (char *)na_ualloc(db_strings, strlen(new) + 1);
#else
*ptr = (char *) malloc(strlen(new) +1);
#endif
#ifdef MEM_CHECK
add_check("object_name");
#endif
strcpy(*ptr, new);
return (*ptr);
}
int db_init = 0;
static void db_grow(newtop)
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;
#ifdef USE_NALLOC
if((db = 5 + (struct object *)
bigalloc ((db_size + 5) * sizeof(struct object))) == NULL) {
#else
if((db = (struct object *)
malloc(db_size * sizeof(struct object))) == NULL) {
#endif
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;
#ifdef USE_NALLOC
if((newdb = 5 + (struct object *)
bigalloc((5 + db_size) * sizeof(struct object))) == NULL) {
#else
if((newdb = (struct object *)
realloc(db, db_size * sizeof(struct object))) == NULL) {
#endif
fprintf(stderr, "ERROR: out of memory!\n");
fflush(stderr);
abort();
}
#ifdef USE_NALLOC
memcpy(newdb, db, db_top * sizeof(struct object));
bigfree(db - 5);
#endif
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(f, ref)
FILE *f;
dbref ref;
{
fprintf(f, "%d\n", ref);
}
static void putstring(f, s)
FILE *f;
const char *s;
{
if (s) {
fputs(s, f);
}
putc('\n', f);
}
static void putbool_subexp(f, b)
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(f, i)
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(f)
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(s)
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(*p))
break;
}
}
/* else x < 0 or s != 0 */
return NOTHING;
}
dbref getref(f)
FILE *f;
{
static char buf[BUFFER_LEN];
fgets(buf, sizeof(buf), f);
return (atol(buf));
}
static const char *getstring_noalloc(f)
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(f)
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(c = getc(f))) {
while(isdigit(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(f)
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(b)
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:
#ifdef USE_NALLOC
if (b->atr_lock->name)
na_unalloc(db_strings, (char *)b->atr_lock->name);
if (b->atr_lock->text)
na_unalloc(db_strings, (char *)b->atr_lock->text);
if (b->atr_lock)
na_unalloc(db_strings, (char *)b->atr_lock);
#else
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);
#endif
#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(b)
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);
}
#ifdef USE_NALLOC
bigfree(db - 5);
#else
free((char *)db);
#endif
db = NULL;
db_init = db_top = '\0';
}
#ifdef USE_NALLOC
if(db_strings)
na_close(db_strings);
db_strings = na_open(sizeof(char *));
#endif
}
/* read attribute list */
int get_list(f, i)
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);
}
}
dbref db_read(f)
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;
}
}
}