/* db_rw.c -- I/O to/from flat file databases */
#include "autoconf.h"
#include "copyright.h"
#ifndef lint
static char RCSid[] = "$Id: db_rw.c,v 1.8 1995/03/21 00:00:06 ambar Exp $";
USE(RCSid);
#endif
#include <sys/file.h>
#include "externs.h"
#include "vattr.h"
#include "attrs.h"
#include "alloc.h"
extern const char *FDECL(getstring_noalloc, (FILE *));
extern void FDECL(putstring, (FILE *, const char *));
extern void FDECL(db_grow, (dbref));
extern struct object *db;
static int g_version;
static int g_format;
static int g_flags;
/* ---------------------------------------------------------------------------
* getboolexp1: Get boolean subexpression from file.
*/
static BOOLEXP *
getboolexp1(f)
FILE *f;
{
BOOLEXP *b;
char *buff, *s;
int c, d, anum;
c = getc(f);
switch (c) {
case '\n':
ungetc(c, f);
return TRUE_BOOLEXP;
/* break; */
case EOF:
abort(); /* unexpected EOF in boolexp */
break;
case '(':
b = alloc_bool("getboolexp1.openparen");
switch (c = getc(f)) {
case NOT_TOKEN:
b->type = BOOLEXP_NOT;
b->sub1 = getboolexp1(f);
if ((d = getc(f)) == '\n')
d = getc(f);
if (d != ')')
goto error;
return b;
case INDIR_TOKEN:
b->type = BOOLEXP_INDIR;
b->sub1 = getboolexp1(f);
if ((d = getc(f)) == '\n')
d = getc(f);
if (d != ')')
goto error;
return b;
case IS_TOKEN:
b->type = BOOLEXP_IS;
b->sub1 = getboolexp1(f);
if ((d = getc(f)) == '\n')
d = getc(f);
if (d != ')')
goto error;
return b;
case CARRY_TOKEN:
b->type = BOOLEXP_CARRY;
b->sub1 = getboolexp1(f);
if ((d = getc(f)) == '\n')
d = getc(f);
if (d != ')')
goto error;
return b;
case OWNER_TOKEN:
b->type = BOOLEXP_OWNER;
b->sub1 = getboolexp1(f);
if ((d = getc(f)) == '\n')
d = getc(f);
if (d != ')')
goto error;
return b;
default:
ungetc(c, f);
b->sub1 = getboolexp1(f);
if ((c = getc(f)) == '\n')
c = getc(f);
switch (c) {
case AND_TOKEN:
b->type = BOOLEXP_AND;
break;
case OR_TOKEN:
b->type = BOOLEXP_OR;
break;
default:
goto error;
}
b->sub2 = getboolexp1(f);
if ((d = getc(f)) == '\n')
d = getc(f);
if (d != ')')
goto error;
return b;
}
case '-': /* obsolete NOTHING key, eat it */
while ((c = getc(f)) != '\n')
if (c == EOF)
abort(); /* unexp EOF */
ungetc(c, f);
return TRUE_BOOLEXP;
default: /* dbref or attribute */
ungetc(c, f);
b = alloc_bool("getboolexp1.default");
b->type = BOOLEXP_CONST;
b->thing = 0;
/* This is either an attribute, eval, or constant lock.
* Constant locks are of the form <num>, while attribute and
* eval locks are of the form <anam-or-anum>:<string> or
* <aname-or-anum>/<string> respectively.
* The characters <nl>, |, and & terminate the string.
*/
if (isdigit(c)) {
while (isdigit(c = getc(f))) {
b->thing = b->thing * 10 + c - '0';
}
} else if (isalpha(c)) {
buff = alloc_lbuf("getboolexp1.atr_name");
for (s = buff;
((c = getc(f)) != EOF) && (c != '\n') &&
(c != ':') && (c != '/');
*s++ = c);
if (c == EOF) {
free_lbuf(buff);
free_bool(b);
goto error;
}
*s = '\0';
/* Look the name up as an attribute. If not found,
* create a new attribute.
*/
anum = mkattr(buff);
if (anum <= 0) {
free_bool(b);
free_lbuf(buff);
goto error;
}
free_lbuf(buff);
b->thing = anum;
} else {
free_bool(b);
goto error;
}
/* if last character is : then this is an attribute lock.
* A last character of / means an eval lock */
if ((c == ':') || (c == '/')) {
if (c == '/')
b->type = BOOLEXP_EVAL;
else
b->type = BOOLEXP_ATR;
buff = alloc_lbuf("getboolexp1.attr_lock");
for (s = buff;
((c = getc(f)) != EOF) && (c != '\n') && (c != ')') &&
(c != OR_TOKEN) && (c != AND_TOKEN);
*s++ = c);
if (c == EOF)
goto error;
*s++ = 0;
b->sub1 = (BOOLEXP *) strsave(buff);
free_lbuf(buff);
}
ungetc(c, f);
return b;
}
error:
abort(); /* bomb out */
return TRUE_BOOLEXP;
}
/* ---------------------------------------------------------------------------
* getboolexp: Read a boolean expression from the flat file.
*/
static BOOLEXP *
getboolexp(f)
FILE *f;
{
BOOLEXP *b;
char c;
b = getboolexp1(f);
if (getc(f) != '\n')
abort(); /* parse error, we lose */
/* MUSH (except for PernMUSH) and MUSE can have an extra CR,
* MUD does not. */
if (((g_format == F_MUSH) && (g_version != 2)) ||
(g_format == F_MUSE)) {
if ((c = getc(f)) != '\n')
ungetc(c, f);
}
return b;
}
/* ---------------------------------------------------------------------------
* unscramble_attrnum: Fix up attribute numbers from foreign muds
*/
static int
unscramble_attrnum(attrnum)
int attrnum;
{
char anam[4];
switch (g_format) {
case F_MUSE:
switch (attrnum) {
case 39:
return A_IDLE;
case 40:
return A_AWAY;
case 41:
return 0; /* mailk */
case 42:
return A_ALIAS;
case 43:
return A_EFAIL;
case 44:
return A_OEFAIL;
case 45:
return A_AEFAIL;
case 46:
return 0; /* it */
case 47:
return A_LEAVE;
case 48:
return A_OLEAVE;
case 49:
return A_ALEAVE;
case 50:
return 0; /* channel */
case 51:
return A_QUOTA;
case 52:
return A_TEMP; /* temp for pennies */
case 53:
return 0; /* huhto */
case 54:
return 0; /* haven */
case 57:
return mkattr((char *) "TZ");
case 58:
return 0; /* doomsday */
case 59:
return mkattr((char *) "Email");
case 98:
return mkattr((char *) "Status");
case 99:
return mkattr((char *) "Race");
default:
return attrnum;
}
case F_MUSH:
/* Only need to muck with Pern variants */
if (g_version != 2)
return attrnum;
switch (attrnum) {
case 34:
return A_OENTER;
case 41:
return A_LEAVE;
case 42:
return A_ALEAVE;
case 43:
return A_OLEAVE;
case 44:
return A_OXENTER;
case 45:
return A_OXLEAVE;
default:
if ((attrnum >= 126) && (attrnum < 152)) {
anam[0] = 'W';
anam[1] = attrnum - 126 + 'A';
anam[2] = '\0';
return mkattr(anam);
}
if ((attrnum >= 152) && (attrnum < 178)) {
anam[0] = 'X';
anam[1] = attrnum - 152 + 'A';
anam[2] = '\0';
return mkattr(anam);
}
return attrnum;
}
default:
return attrnum;
}
}
/* ---------------------------------------------------------------------------
* get_list: Read attribute list from flat file.
*/
static int
get_list(f, i)
FILE *f;
dbref i;
{
dbref atr, aowner;
int c, aflags, xflags, anum;
char *buff, *buf2, *buf2p, *ownp, *flagp;
buff = alloc_lbuf("get_list");
while (1) {
switch (c = getc(f)) {
case '>': /* read # then string */
atr = unscramble_attrnum(getref(f));
if (atr > 0) {
/* Store the attr */
atr_add_raw(i, atr,
(char *) getstring_noalloc(f));
} else {
/* Silently discard */
getstring_noalloc(f);
}
break;
case ']': /* Pern 1.13 style text attribute */
strcpy(buff, (char *) getstring_noalloc(f));
/* Get owner number */
ownp = (char *) index(buff, '^');
if (!ownp) {
fprintf(stderr,
"Bad format in attribute on object %d\n",
i);
free_lbuf(buff);
return 0;
}
*ownp++ = '\0';
/* Get attribute flags */
flagp = (char *) index(ownp, '^');
if (!flagp) {
fprintf(stderr,
"Bad format in attribute on object %d\n",
i);
free_lbuf(buff);
return 0;
}
*flagp++ = '\0';
/* Convert Pern-style owner and flags to 2.0 format */
aowner = atoi(ownp);
xflags = atoi(flagp);
aflags = 0;
if (!aowner)
aowner = NOTHING;
if (xflags & 0x10)
aflags |= AF_LOCK | AF_NOPROG;
if (xflags & 0x20)
aflags |= AF_NOPROG;
/* Look up the attribute name in the attribute table.
* If the name isn't found, create a new attribute.
* If the create fails, try prefixing the attr name
* with ATR_ (Pern allows attributes to start with a
* non-alphabetic character.
*/
anum = mkattr(buff);
if (anum < 0) {
buf2 = alloc_mbuf("get_list.new_attr_name");
buf2p = buf2;
safe_mb_str((char *) "ATR_", buf2, &buf2p);
safe_mb_str(buff, buf2, &buf2p);
anum = mkattr(buf2);
free_mbuf(buf2);
}
if (anum < 0) {
fprintf(stderr,
"Bad attribute name '%s' on object %d, ignoring...\n",
buff, i);
(void) getstring_noalloc(f);
} else {
atr_add(i, anum, (char *) getstring_noalloc(f),
aowner, aflags);
}
break;
case '\n': /* ignore newlines. They're due to v(r). */
break;
case '<': /* end of list */
free_lbuf(buff);
c = getc(f);
if (c != '\n') {
ungetc(c, f);
fprintf(stderr,
"No line feed on object %d\n", i);
return 1;
}
return 1;
default:
fprintf(stderr,
"Bad character '%c' when getting attributes on object %d\n",
c, i);
/* We've found a bad spot. I hope things aren't
* too bad. */
(void) getstring_noalloc(f);
}
}
}
/* ---------------------------------------------------------------------------
* putbool_subexp: Write a boolean sub-expression to the flat file.
*/
static void
putbool_subexp(f, b)
FILE *f;
BOOLEXP *b;
{
ATTR *va;
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(CARRY_TOKEN, f);
putbool_subexp(f, b->sub1);
putc(')', f);
break;
case BOOLEXP_INDIR:
putc('(', f);
putc(INDIR_TOKEN, f);
putbool_subexp(f, b->sub1);
putc(')', f);
break;
case BOOLEXP_OWNER:
putc('(', f);
putc(OWNER_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:
va = atr_num(b->thing);
if (va) {
fprintf(f, "%s:%s", va->name, (char *) b->sub1);
} else {
fprintf(f, "%d:%s\n", b->thing, (char *) b->sub1);
}
break;
case BOOLEXP_EVAL:
va = atr_num(b->thing);
if (va) {
fprintf(f, "%s/%s\n", va->name, (char *) b->sub1);
} else {
fprintf(f, "%d/%s\n", b->thing, (char *) b->sub1);
}
break;
default:
fprintf(stderr, "Unknown boolean type in putbool_subexp: %d\n",
b->type);
}
}
/* ---------------------------------------------------------------------------
* putboolexp: Write boolean expression to the flat file.
*/
static void
putboolexp(f, b)
FILE *f;
BOOLEXP *b;
{
if (b != TRUE_BOOLEXP) {
putbool_subexp(f, b);
}
putc('\n', f);
}
/* ---------------------------------------------------------------------------
* upgrade_flags: Convert foreign flags to MUSH format.
*/
static void
upgrade_flags(flags1, flags2, thing, db_format, db_version)
FLAG *flags1, *flags2;
dbref thing;
int db_format, db_version;
{
FLAG f1, f2, newf1, newf2;
f1 = *flags1;
f2 = *flags2;
newf1 = 0;
newf2 = 0;
if (db_format == F_MUD) {
/* Old TinyMUD format */
newf1 = f1 & (TYPE_MASK | WIZARD | LINK_OK | DARK | STICKY | HAVEN);
if (f1 & MUD_ABODE)
newf2 |= ABODE;
if (f1 & MUD_ROBOT)
newf1 |= ROBOT;
if (f1 & MUD_CHOWN_OK)
newf1 |= CHOWN_OK;
} else if (db_format == F_MUSE) {
if (db_version == 1)
return;
/* Convert level-based players to normal */
switch (f1 & 0xf) {
case 0: /* room */
case 1: /* thing */
case 2: /* exit */
newf1 = f1 & 0x3;
break;
case 8: /* guest */
case 9: /* trial player */
case 10: /* member */
case 11: /* junior official */
case 12: /* official */
newf1 = TYPE_PLAYER;
break;
case 13: /* honorary wizard */
case 14: /* administrator */
case 15: /* director */
newf1 = TYPE_PLAYER | WIZARD;
break;
default: /* A bad type, mark going */
fprintf(stderr, "Funny object type for #%d\n", thing);
*flags1 = GOING;
return;
}
/* Player #1 is always a wizard */
if (thing == (dbref) 1)
newf1 |= WIZARD;
/* Set type-specific flags */
switch (newf1 & TYPE_MASK) {
case TYPE_PLAYER: /* Lose CONNECT TERSE QUITE NOWALLS WARPTEXT */
if (f1 & MUSE_BUILD)
newf2 |= BUILDER;
if (f1 & MUSE_SLAVE)
newf2 |= SLAVE;
if (f1 & MUSE_UNFIND)
newf2 |= UNFINDABLE;
break;
case TYPE_THING: /* lose LIGHT SACR_OK */
if (f1 & MUSE_KEY)
newf2 |= KEY;
if (f1 & MUSE_DEST_OK)
newf1 |= DESTROY_OK;
break;
case TYPE_ROOM:
if (f1 & MUSE_ABODE)
newf2 |= ABODE;
break;
case TYPE_EXIT:
if (f1 & MUSE_SEETHRU)
newf1 |= SEETHRU;
default:
break;
}
/* Convert common flags */
/* Lose: MORTAL ACCESSED MARKED SEE_OK UNIVERSAL */
if (f1 & MUSE_CHOWN_OK)
newf1 |= CHOWN_OK;
if (f1 & MUSE_DARK)
newf1 |= DARK;
if (f1 & MUSE_STICKY)
newf1 |= STICKY;
if (f1 & MUSE_HAVEN)
newf1 |= HAVEN;
if (f1 & MUSE_INHERIT)
newf1 |= INHERIT;
if (f1 & MUSE_GOING)
newf1 |= GOING;
if (f1 & MUSE_PUPPET)
newf1 |= PUPPET;
if (f1 & MUSE_LINK_OK)
newf1 |= LINK_OK;
if (f1 & MUSE_ENTER_OK)
newf1 |= ENTER_OK;
if (f1 & MUSE_VISUAL)
newf1 |= VISUAL;
if (f1 & MUSE_OPAQUE)
newf1 |= OPAQUE;
if (f1 & MUSE_QUIET)
newf1 |= QUIET;
} else if ((db_format == F_MUSH) && (db_version == 2)) {
/* Pern variants */
newf1 = f1 &
(TYPE_MASK | LINK_OK | HAVEN | QUIET | HALT | DARK |
GOING | PUPPET | CHOWN_OK | ENTER_OK | VISUAL | OPAQUE);
if (f1 & 0x1000000)
newf1 |= INHERIT;
if (f1 & 0x10000000)
newf1 |= HEARTHRU;
switch (newf1 & TYPE_MASK) {
case TYPE_PLAYER:
newf1 |= f1 &
(WIZARD | BUILDER | UNFINDABLE);
if (f1 & PERN_SLAVE)
newf2 |= SLAVE;
if (f1 & PERN_NOSPOOF)
newf1 |= NOSPOOF;
if (f1 & PERN_SUSPECT)
newf2 |= SUSPECT;
break;
case TYPE_EXIT:
newf1 |= f1 & (STICKY | SEETHRU);
if (f1 & PERN_KEY)
newf2 |= KEY;
if (f1 & WIZARD)
newf1 |= INHERIT;
break;
case TYPE_THING:
newf1 |= f1 & (DESTROY_OK | STICKY);
if (f1 & KEY)
newf2 |= KEY;
if (f1 & WIZARD)
newf1 |= INHERIT;
if (f1 & PERN_VERBOSE)
newf1 |= VERBOSE;
if (f1 & PERN_IMMORTAL)
newf1 |= IMMORTAL;
if (f1 & PERN_MONITOR)
newf1 |= MONITOR;
if (f1 & PERN_SAFE)
newf1 |= SAFE;
break;
case TYPE_ROOM:
newf1 |= f1 &
(FLOATING | ABODE | JUMP_OK);
if (f1 & WIZARD)
newf1 |= INHERIT;
if (f1 & PERN_UNFIND)
newf2 |= UNFINDABLE;
}
} else if ((db_format == F_MUSH) && (db_version >= 3)) {
newf1 = f1;
newf2 = f2;
switch (db_version) {
case 3:
(newf1 &= ~V2_ACCESSED); /* Clear ACCESSED */
case 4:
(newf1 &= ~V3_MARKED); /* Clear MARKED */
case 5:
/* Merge GAGGED into SLAVE, move SUSPECT */
if ((newf1 & TYPE_MASK) == TYPE_PLAYER) {
if (newf1 & V4_GAGGED) {
newf2 |= SLAVE;
newf1 &= ~V4_GAGGED;
}
if (newf1 & V4_SUSPECT) {
newf2 |= SUSPECT;
newf1 &= ~V4_SUSPECT;
}
}
case 6:
switch (newf1 & TYPE_MASK) {
case TYPE_PLAYER:
if (newf1 & V6_BUILDER) {
newf2 |= BUILDER;
newf1 &= ~V6_BUILDER;
}
if (newf1 & V6_SUSPECT) {
newf2 |= SUSPECT;
newf1 &= ~V6_SUSPECT;
}
if (newf1 & V6PLYR_UNFIND) {
newf2 |= UNFINDABLE;
newf1 &= ~V6PLYR_UNFIND;
}
if (newf1 & V6_SLAVE) {
newf2 |= SLAVE;
newf1 &= ~V6_SLAVE;
}
break;
case TYPE_ROOM:
if (newf1 & V6_FLOATING) {
newf2 |= FLOATING;
newf1 &= ~V6_FLOATING;
}
if (newf1 & V6_ABODE) {
newf2 |= ABODE;
newf1 &= ~V6_ABODE;
}
if (newf1 & V6ROOM_JUMPOK) {
newf1 |= JUMP_OK;
newf1 &= ~V6ROOM_JUMPOK;
}
if (newf1 & V6ROOM_UNFIND) {
newf2 |= UNFINDABLE;
newf1 &= ~V6ROOM_UNFIND;
}
break;
case TYPE_THING:
if (newf1 & V6OBJ_KEY) {
newf2 |= KEY;
newf1 &= ~V6OBJ_KEY;
}
break;
case TYPE_EXIT:
if (newf1 & V6EXIT_KEY) {
newf2 |= KEY;
newf1 &= ~V6EXIT_KEY;
}
break;
}
}
}
*flags1 = newf1;
*flags2 = newf2;
return;
}
/* ---------------------------------------------------------------------------
* efo_convert: Fix things up for Exits-From-Objects
*/
void
NDECL(efo_convert)
{
int i;
dbref link;
DO_WHOLE_DB(i) {
switch (Typeof(i)) {
case TYPE_PLAYER:
case TYPE_THING:
/* swap Exits and Link */
link = Link(i);
s_Link(i, Exits(i));
s_Exits(i, link);
break;
}
}
}
/* ---------------------------------------------------------------------------
* fix_typed_quotas: Explode standard quotas into typed quotas
*/
#ifdef STANDALONE
static void
fix_typed_quotas()
{
/* If we have a pre-2.2 database, only the QUOTA and RQUOTA attributes
* exist. For simplicity's sake, we assume that players will have the
* same quotas for all types, equal to the current value. This is
* going to produce incorrect values for RQUOTA; this is easily fixed
* by a @quota/fix done from within-game.
*
* If we have a early beta 2.2 release, we have quotas which are
* spread out over ten attributes. We're going to have to grab
* those, make the new quotas, and then delete the old attributes.
*/
int i, quota, rquota, tq, trq;
char *qbuf, *rqbuf;
#ifdef BETA_QUOTAS
char *roombuf, *r_roombuf, *exitbuf, *r_exitbuf,
*tbuf, *r_tbuf, *pbuf, *r_pbuf;
#endif
DO_WHOLE_DB(i) {
if (isPlayer(i)) {
qbuf = atr_get_raw(i, A_QUOTA);
rqbuf = atr_get_raw(i, A_RQUOTA);
if (!qbuf || !*qbuf)
qbuf = (char *) "1";
if (!rqbuf || !*rqbuf)
rqbuf = (char *) "0";
#ifdef BETA_QUOTAS
roombuf = atr_get_raw(i, A_QUOTAROOM);
r_roombuf = atr_get_raw(i, A_RQUOTAROOM);
exitbuf = atr_get_raw(i, A_QUOTAEXIT);
r_exitbuf = atr_get_raw(i, A_RQUOTAEXIT);
tbuf = atr_get_raw(i, A_QUOTATHING);
r_tbuf = atr_get_raw(i, A_RQUOTATHING);
pbuf = atr_get_raw(i, A_QUOTAPLAYER);
r_pbuf = atr_get_raw(i, A_RQUOTAPLAYER);
atr_add_raw(i, A_QUOTA,
tprintf("%s %s %s %s %s", qbuf,
(roombuf && *roombuf) ? roombuf : qbuf,
(exitbuf && *exitbuf) ? exitbuf : qbuf,
(tbuf && *tbuf) ? tbuf : qbuf,
(pbuf && *pbuf) ? pbuf : qbuf));
atr_add_raw(i, A_RQUOTA,
tprintf("%s %s %s %s %s", rqbuf,
(r_roombuf && *r_roombuf) ? r_roombuf : rqbuf,
(r_exitbuf && *r_exitbuf) ? r_exitbuf : rqbuf,
(r_tbuf && *r_tbuf) ? r_tbuf : rqbuf,
(r_pbuf && *r_pbuf) ? r_pbuf : rqbuf));
atr_add_raw(i, A_QUOTAROOM, NULL);
atr_add_raw(i, A_RQUOTAROOM, NULL);
atr_add_raw(i, A_QUOTAEXIT, NULL);
atr_add_raw(i, A_RQUOTAEXIT, NULL);
atr_add_raw(i, A_QUOTATHING, NULL);
atr_add_raw(i, A_RQUOTATHING, NULL);
atr_add_raw(i, A_QUOTAPLAYER, NULL);
atr_add_raw(i, A_RQUOTAPLAYER, NULL);
#else /* else if no BETA_QUOTAS */
atr_add_raw(i, A_QUOTA,
tprintf("%s %s %s %s %s",
qbuf, qbuf, qbuf, qbuf, qbuf));
atr_add_raw(i, A_RQUOTA,
tprintf("%s %s %s %s %s",
rqbuf, rqbuf, rqbuf, rqbuf, rqbuf));
#endif /* BETA_QUOTAS */
}
}
}
#endif /* STANDALONE */
/* ---------------------------------------------------------------------------
* fix_commands_flag: Set the COMMANDS flag on those objects which need it.
*/
#ifdef STANDALONE
static void
fix_commands_flag()
{
dbref thing;
int ca, aflags, aowner;
char *as, *buf;
DO_WHOLE_DB(thing) {
for (ca = atr_head(thing, &as); ca; ) {
buf = atr_get(thing, ca, &aowner, &aflags);
if ((*buf == '$') && index(buf, ':')) {
s_Flags2(thing, Flags2(thing) | HAS_COMMANDS);
ca = (int) NULL;
} else {
ca = atr_next(&as);
}
free_lbuf(buf);
}
}
}
#endif /* STANDALONE */
/* ---------------------------------------------------------------------------
* unscraw_foreign: Fix up strange object linking conventions for other formats
*/
static void
unscraw_pern_object(i, db_flags)
dbref i;
int db_flags;
{
dbref aowner;
int aflags;
char *p_str;
if (db_flags & V_PERNKEY) {
/* Use lock on players is really the page lock */
if (isPlayer(i)) {
p_str = atr_get(i, A_LUSE, &aowner, &aflags);
if (*p_str) {
atr_add_raw(i, A_LPAGE, p_str);
atr_clr(i, A_LUSE);
}
free_lbuf(p_str);
}
/* Enter lock on rooms is the teleport lock */
if (isRoom(i)) {
p_str = atr_get(i, A_LENTER, &aowner, &aflags);
if (*p_str) {
atr_add_raw(i, A_LTPORT, p_str);
atr_clr(i, A_LENTER);
}
}
}
}
void
unscraw_foreign(db_format, db_version, db_flags)
int db_format, db_version, db_flags;
{
dbref tmp, i, aowner;
int aflags;
char *p_str;
switch (db_format) {
case F_MUSE:
DO_WHOLE_DB(i) {
if (isExit(i)) {
/* MUSE exits are bass-ackwards */
tmp = Exits(i);
s_Exits(i, Location(i));
s_Location(i, tmp);
}
if (db_version > 3) {
/* MUSEs with pennies in an attribute have
* it stored in attr 255 (see
* unscramble_attrnum)
*/
p_str = atr_get(i, A_TEMP, &aowner, &aflags);
s_Pennies(i, atoi(p_str));
free_lbuf(p_str);
atr_clr(i, A_TEMP);
}
}
if (!(db_flags & V_LINK)) {
efo_convert();
}
break;
case F_MUSH:
if (db_version <= 3) {
if (db_version == 2) {
DO_WHOLE_DB(i) {
unscraw_pern_object(i, db_flags);
}
}
efo_convert();
}
if ((db_version <= 5) && (db_flags & V_GDBM)) {
/* Check for FORWARDLIST attribute */
DO_WHOLE_DB(i) {
if (atr_get_raw(i, A_FORWARDLIST))
s_Flags2(i, Flags2(i) | HAS_FWDLIST);
}
}
if (db_version <= 6) {
DO_WHOLE_DB(i) {
/* Make sure A_QUEUEMAX is empty */
atr_clr(i, A_QUEUEMAX);
if (db_flags & V_GDBM) {
/* HAS_LISTEN now tracks LISTEN attr */
if (atr_get_raw(i, A_LISTEN))
s_Flags2(i, Flags2(i) | HAS_LISTEN);
/* Undo V6 overloading of HAS_STARTUP
* with HAS_FWDLIST
*/
if ((db_version == 6) &&
(Flags2(i) & HAS_STARTUP) &&
atr_get_raw(i, A_FORWARDLIST)) {
/* We have FORWARDLIST */
s_Flags2(i, Flags2(i) | HAS_FWDLIST);
/* Maybe no STARTUP */
if (!atr_get_raw(i, A_STARTUP))
s_Flags2(i, Flags2(i) & ~HAS_STARTUP);
}
}
}
}
break;
case F_MUD:
efo_convert();
}
}
/* ---------------------------------------------------------------------------
* getlist_discard, get_atrdefs_discard: Throw away data from MUSE that we
* don't use.
*/
static void
getlist_discard(f)
FILE *f;
{
int count;
for (count = getref(f); count > 0; count--)
(void) getref(f);
}
static void
get_atrdefs_discard(f)
FILE *f;
{
const char *sp;
for (;;) {
sp = getstring_noalloc(f); /* flags or endmarker */
if (*sp == '\\')
return;
sp = getstring_noalloc(f); /* object */
sp = getstring_noalloc(f); /* name */
}
}
dbref
db_read(f, db_format, db_version, db_flags)
FILE *f;
int *db_format, *db_version, *db_flags;
{
dbref i, anum;
char ch, peek;
const char *tstr;
int header_gotten, size_gotten, nextattr_gotten;
int read_attribs, read_name, read_zone, read_link, read_key, read_parent;
int read_extflags, read_money, read_timestamps;
int read_pern_key, read_pern_comm, read_pern_parent;
int read_muse_parents, read_muse_atrdefs;
int read_powers_player, read_powers_any;
int has_typed_quotas, has_commands_flag, has_alpha_attrs;
int deduce_version, deduce_name, deduce_zone, deduce_timestamps;
int aflags, f1, f2;
BOOLEXP *tempbool;
header_gotten = 0;
size_gotten = 0;
nextattr_gotten = 0;
g_format = F_UNKNOWN;
g_version = 0;
g_flags = 0;
read_attribs = 1;
read_name = 1;
read_zone = 0;
read_link = 0;
read_key = 1;
read_parent = 0;
read_money = 1;
read_extflags = 0;
has_typed_quotas = 0;
has_commands_flag = 0;
has_alpha_attrs = 0;
read_timestamps = 0;
read_pern_key = 0;
read_pern_comm = 0;
read_pern_parent = 0;
read_powers_player = 0;
read_powers_any = 0;
read_muse_parents = 0;
read_muse_atrdefs = 0;
deduce_version = 1;
deduce_zone = 1;
deduce_name = 1;
deduce_timestamps = 1;
#ifdef STANDALONE
fprintf(stderr, "Reading ");
fflush(stderr);
#endif
db_free();
for (i = 0;; i++) {
if (!(i % 25)) {
cache_reset(0);
}
#ifdef STANDALONE
if (!(i % 100)) {
fputc('.', stderr);
fflush(stderr);
}
#endif
switch (ch = getc(f)) {
case '~': /* Database size tag */
if (size_gotten) {
fprintf(stderr,
"\nDuplicate size entry at object %d, ignored.\n",
i);
tstr = getstring_noalloc(f); /* junk */
break;
}
mudstate.min_size = getref(f);
size_gotten = 1;
break;
case '+': /* MUSH 2.0 header */
switch (ch = getc(f)) { /* 2nd char selects type */
case 'V': /* VERSION */
if (header_gotten) {
fprintf(stderr,
"\nDuplicate MUSH version header entry at object %d, ignored.\n",
i);
tstr = getstring_noalloc(f); /* junk */
break;
}
header_gotten = 1;
deduce_version = 0;
g_format = F_MUSH;
g_version = getref(f);
if ((g_version & V_MASK) == 2) {
/* Handle Pern veriants specially */
switch (g_version >> 8) {
case 4:
case 3:
read_pern_parent = 1;
case 2:
if ((g_version >> 8) != 4) {
read_pern_comm = 1;
g_flags |= V_COMM;
}
case 1:
read_pern_key = 1;
read_parent = 1;
g_flags |= V_PERNKEY | V_ZONE;
}
} else {
/* Otherwise extract feature flags */
if (g_version & V_GDBM) {
read_attribs = 0;
read_name = !(g_version & V_ATRNAME);
}
read_zone = (g_version & V_ZONE);
read_link = (g_version & V_LINK);
read_key = !(g_version & V_ATRKEY);
read_parent = (g_version & V_PARENT);
read_money = !(g_version & V_ATRMONEY);
read_extflags = (g_version & V_XFLAGS);
has_typed_quotas = (g_version & V_TQUOTAS);
has_commands_flag = (g_version & V_CMDSFLAG);
has_alpha_attrs = (g_version & V_ALPHA_ATTR);
g_flags = g_version & ~V_MASK;
}
g_version &= V_MASK;
deduce_name = 0;
deduce_version = 0;
deduce_zone = 0;
break;
case 'S': /* SIZE */
if (size_gotten) {
fprintf(stderr,
"\nDuplicate size entry at object %d, ignored.\n",
i);
tstr = getstring_noalloc(f); /* junk */
} else {
mudstate.min_size = getref(f);
}
size_gotten = 1;
break;
case 'A': /* USER-NAMED ATTRIBUTE */
anum = getref(f);
tstr = getstring_noalloc(f);
if (isdigit(*tstr)) {
aflags = 0;
while (isdigit(*tstr))
aflags = (aflags * 10) +
(*tstr++ - '0');
tstr++; /* skip ':' */
} else {
aflags = mudconf.vattr_flags;
}
vattr_define((char *) tstr, anum, aflags);
break;
case 'F': /* OPEN USER ATTRIBUTE SLOT */
anum = getref(f);
break;
case 'N': /* NEXT ATTR TO ALLOC WHEN NO FREELIST */
if (nextattr_gotten) {
fprintf(stderr,
"\nDuplicate next free vattr entry at object %d, ignored.\n",
i);
tstr = getstring_noalloc(f); /* junk */
} else {
mudstate.attr_next = getref(f);
nextattr_gotten = 1;
}
break;
default:
fprintf(stderr,
"\nUnexpected character '%c' in MUSH header near object #%d, ignored.\n",
ch, i);
tstr = getstring_noalloc(f); /* toss line */
}
break;
case '@': /* MUSE header */
if (header_gotten) {
fprintf(stderr,
"\nDuplicate MUSE header entry at object #%d.\n",
i);
return -1;
}
header_gotten = 1;
deduce_version = 0;
g_format = F_MUSE;
g_version = getref(f);
deduce_name = 0;
deduce_zone = 1;
read_money = (g_version <= 3);
read_link = (g_version >= 5);
read_powers_player = (g_version >= 6);
read_powers_any = (g_version == 6);
read_muse_parents = (g_version >= 8);
read_muse_atrdefs = (g_version >= 8);
if (read_link)
g_flags |= V_LINK;
break;
case '#':
if (deduce_version) {
g_format = F_MUD;
g_version = 1;
deduce_version = 0;
}
if (g_format != F_MUD) {
fprintf(stderr,
"\nMUD-style object found in non-MUD database at object #%d\n",
i);
return -1;
}
if (i != getref(f)) {
fprintf(stderr,
"\nSequence error at object #%d\n", i);
return -1;
}
db_grow(i + 1);
s_Name(i, getstring_noalloc(f));
atr_add_raw(i, A_DESC, (char *) getstring_noalloc(f));
s_Location(i, getref(f));
s_Contents(i, getref(f));
s_Exits(i, getref(f));
s_Link(i, NOTHING);
s_Next(i, getref(f));
/* s_Zone(i, NOTHING); */
tempbool = getboolexp(f);
atr_add_raw(i, A_LOCK,
unparse_boolexp_quiet(1, tempbool));
free_boolexp(tempbool);
atr_add_raw(i, A_FAIL, (char *) getstring_noalloc(f));
atr_add_raw(i, A_SUCC, (char *) getstring_noalloc(f));
atr_add_raw(i, A_OFAIL, (char *) getstring_noalloc(f));
atr_add_raw(i, A_OSUCC, (char *) getstring_noalloc(f));
s_Owner(i, getref(f));
s_Parent(i, NOTHING);
s_Pennies(i, getref(f));
f1 = getref(f);
f2 = 0;
upgrade_flags(&f1, &f2, i, g_format, g_version);
s_Flags(i, f1);
s_Flags2(i, f2);
s_Pass(i, getstring_noalloc(f));
if (deduce_timestamps) {
peek = getc(f);
if ((peek != '#') && (peek != '*')) {
read_timestamps = 1;
}
deduce_timestamps = 0;
ungetc(peek, f);
}
if (read_timestamps) {
aflags = getref(f); /* created */
aflags = getref(f); /* lastused */
aflags = getref(f); /* usecount */
}
break;
case '&': /* MUSH 2.0a stub entry/MUSE zoned entry */
if (deduce_version) {
deduce_version = 0;
g_format = F_MUSH;
g_version = 1;
deduce_name = 1;
deduce_zone = 0;
read_key = 0;
read_attribs = 0;
} else if (deduce_zone) {
deduce_zone = 0;
read_zone = 1;
g_flags |= V_ZONE;
}
case '!': /* MUSH entry/MUSE non-zoned entry */
if (deduce_version) {
g_format = F_MUSH;
g_version = 1;
deduce_name = 0;
deduce_zone = 0;
deduce_version = 0;
} else if (deduce_zone) {
deduce_zone = 0;
read_zone = 0;
}
i = getref(f);
db_grow(i + 1);
/* NAME and LOCATION */
if (read_name) {
tstr = getstring_noalloc(f);
if (deduce_name) {
if (isdigit(*tstr)) {
read_name = 0;
s_Location(i, atoi(tstr));
} else {
s_Name(i, tstr);
s_Location(i, getref(f));
}
deduce_name = 0;
} else {
s_Name(i, tstr);
s_Location(i, getref(f));
}
} else {
s_Location(i, getref(f));
}
/* ZONE on MUSE databases and some others */
if (read_zone)
s_Zone(i, getref(f));
/* else
s_Zone(i, NOTHING); */
/* CONTENTS and EXITS */
s_Contents(i, getref(f));
s_Exits(i, getref(f));
/* LINK */
if (read_link)
s_Link(i, getref(f));
else
s_Link(i, NOTHING);
/* NEXT */
s_Next(i, getref(f));
/* PARENT on PennMUSH 1.19.01+ databases */
if (read_pern_parent)
s_Parent(i, getref(f));
/* LOCK (and PennMUSH extended locks) */
if (read_key) {
tempbool = getboolexp(f);
atr_add_raw(i, A_LOCK,
unparse_boolexp_quiet(1, tempbool));
free_boolexp(tempbool);
if (read_pern_key) {
/* Read Pern 1.17-style locks */
tempbool = getboolexp(f);
atr_add_raw(i, A_LUSE,
unparse_boolexp_quiet(1,
tempbool));
free_boolexp(tempbool);
tempbool = getboolexp(f);
atr_add_raw(i, A_LENTER,
unparse_boolexp_quiet(1,
tempbool));
free_boolexp(tempbool);
}
}
/* OWNER */
s_Owner(i, getref(f));
/* PARENT: PennMUSH uses this field for ZONE (which we
* use as PARENT if we didn't already read in a
* non-NOTHING parent.
*/
if (read_parent) {
if (read_pern_parent) {
if (Parent(i) != NOTHING)
(void) getref(f);
else
s_Parent(i, getref(f));
} else {
s_Parent(i, getref(f));
}
} else {
s_Parent(i, NOTHING);
}
/* PENNIES */
if (read_money) /* if not fix in unscraw_foreign */
s_Pennies(i, getref(f));
/* FLAGS */
f1 = getref(f);
if (read_extflags)
f2 = getref(f);
else
f2 = 0;
upgrade_flags(&f1, &f2, i, g_format, g_version);
s_Flags(i, f1);
s_Flags2(i, f2);
/* POWERS from MUSE. Discard. */
if (read_powers_any || (isPlayer(i) && read_powers_player))
(void) getstring_noalloc(f);
/* COMM from PennMUSH */
if (read_pern_comm)
(void) getref(f);
/* ATTRIBUTES */
if (read_attribs) {
if (!get_list(f, i)) {
fprintf(stderr,
"\nError reading attrs for object #%d\n",
i);
return -1;
}
}
/* PARENTS from MUSE. Ewwww. */
if (read_muse_parents) {
getlist_discard(f);
getlist_discard(f);
}
/* ATTRIBUTE DEFINITIONS from MUSE. Ewwww. Ewwww. */
if (read_muse_atrdefs) {
get_atrdefs_discard(f);
}
/* check to see if it's a player */
if (isPlayer(i)) {
c_Connected(i);
}
break;
case '*': /* EOF marker */
tstr = getstring_noalloc(f);
if (strcmp(tstr, "**END OF DUMP***")) {
fprintf(stderr,
"\nBad EOF marker at object #%d\n",
i);
return -1;
} else {
#ifdef STANDALONE
fprintf(stderr, "\n");
fflush(stderr);
#endif
/* Fix up bizarro foreign DBs */
unscraw_foreign(g_format, g_version, g_flags);
*db_version = g_version;
*db_format = g_format;
*db_flags = g_flags;
#ifndef STANDALONE
load_player_names();
#endif
#ifdef STANDALONE
if (!has_typed_quotas)
fix_typed_quotas();
if (!has_commands_flag)
fix_commands_flag();
#endif
return mudstate.db_top;
}
default:
fprintf(stderr, "\nIllegal character '%c' near object #%d\n",
ch, i);
return -1;
}
}
}
static int
db_write_object(f, i, db_format, flags)
FILE *f;
dbref i;
int db_format, flags;
{
#ifndef STANDALONE
ATTR *a;
#endif
char *got, *as;
dbref aowner;
int ca, aflags, save, j;
BOOLEXP *tempbool;
if (!(flags & V_ATRNAME))
putstring(f, Name(i));
putref(f, Location(i));
if (flags & V_ZONE)
putref(f, Zone(i));
putref(f, Contents(i));
putref(f, Exits(i));
if (flags & V_LINK)
putref(f, Link(i));
putref(f, Next(i));
if (!(flags & V_ATRKEY)) {
got = atr_get(i, A_LOCK, &aowner, &aflags);
tempbool = parse_boolexp(GOD, got, 1);
free_lbuf(got);
putboolexp(f, tempbool);
free_bool(tempbool);
}
putref(f, Owner(i));
if (flags & V_PARENT)
putref(f, Parent(i));
if (!(flags & V_ATRMONEY))
putref(f, Pennies(i));
putref(f, Flags(i));
if (flags & V_XFLAGS)
putref(f, Flags2(i));
/* write the attribute list */
if (!(flags & V_GDBM)) {
for (ca = atr_head(i, &as); ca; ca = atr_next(&as)) {
save = 0;
#ifndef STANDALONE
a = atr_num(ca);
if (a)
j = a->number;
else
j = -1;
#else
j = ca;
#endif
if (j > 0) {
switch (j) {
case A_NAME:
if (flags & V_ATRNAME)
save = 1;
break;
case A_LOCK:
if (flags & V_ATRKEY)
save = 1;
break;
case A_LIST:
case A_MONEY:
break;
default:
save = 1;
}
}
if (save) {
got = atr_get_raw(i, j);
fprintf(f, ">%d\n%s\n", j, got);
}
}
fprintf(f, "<\n");
}
return 0;
}
dbref
db_write(f, format, version)
FILE *f;
int format, version;
{
dbref i;
int flags;
VATTR *vp;
al_store();
switch (format) {
case F_MUSH:
flags = version;
break;
default:
fprintf(stderr, "Can only write MUSH format.\n");
return -1;
}
#ifdef STANDALONE
fprintf(stderr, "Writing ");
fflush(stderr);
#endif
i = mudstate.attr_next;
fprintf(f, "+V%d\n+S%d\n+N%d\n", flags, mudstate.db_top, i);
/* Dump user-named attribute info */
vp = vattr_first();
while (vp != NULL) {
if (!(vp->flags & AF_DELETED))
fprintf(f, "+A%d\n%d:%s\n",
vp->number, vp->flags, vp->name);
vp = vattr_next(vp);
}
DO_WHOLE_DB(i) {
if (!(i % 25)) {
cache_reset(0);
}
#ifdef STANDALONE
if (!(i % 100)) {
fputc('.', stderr);
fflush(stderr);
}
#endif
fprintf(f, "!%d\n", i);
db_write_object(f, i, format, flags);
}
fputs("***END OF DUMP***\n", f);
fflush(f);
#ifdef STANDALONE
fprintf(stderr, "\n");
fflush(stderr);
#endif
return (mudstate.db_top);
}