tinymush-3.1p2/game/backups/
tinymush-3.1p2/game/bin/
tinymush-3.1p2/game/data/
tinymush-3.1p2/game/modules/
tinymush-3.1p2/game/modules/old/
tinymush-3.1p2/src/modules/comsys/
tinymush-3.1p2/src/modules/hello/
tinymush-3.1p2/src/modules/mail/
tinymush-3.1p2/src/tools/
/* db.c - attribute interface, some flatfile and object routines */
/* $Id: db.c,v 1.137 2003/03/01 17:38:27 rmg Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include <sys/stat.h>

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by interface */
#include "interface.h"	/* required by code */

#include "attrs.h"	/* required by code */
#include "vattr.h"	/* required by code */
#include "match.h"	/* required by code */
#include "powers.h"	/* required by code */
#include "udb.h"	/* required by code */
#include "ansi.h"	/* required by code */

#ifndef O_ACCMODE
#define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
#endif

/*
 * Restart definitions 
 */
#define RS_CONCENTRATE		0x00000002
#define RS_RECORD_PLAYERS	0x00000004
#define RS_NEW_STRINGS		0x00000008
#define RS_COUNT_REBOOTS	0x00000010

OBJ *db = NULL;
NAME *names = NULL;
NAME *purenames = NULL;

extern int sock;
extern int ndescriptors;
extern int maxd;
extern int slave_socket;

#ifdef CONCENTRATE
extern int conc_pid;

#endif
extern pid_t slave_pid;
extern void FDECL(desc_addhash, (DESC *));

#ifdef TEST_MALLOC
int malloc_count = 0;
int malloc_bytes = 0;
char *malloc_ptr;
char *malloc_str;
#endif /* TEST_MALLOC */

extern VATTR *FDECL(vattr_rename, (char *, char *));

/* ---------------------------------------------------------------------------
 * Temp file management, used to get around static limits in some versions
 * of libc.
 */

FILE *t_fd;
int t_is_pipe;

#ifdef TLI
int t_is_tli;

#endif

static void tf_xclose(fd)
FILE *fd;
{
	if (fd) {
		if (t_is_pipe)
			pclose(fd);
#ifdef TLI
		else if (t_is_tli)
			t_close(fd);
#endif
		else
			fclose(fd);
	} else {
		close(0);
	}
	t_fd = NULL;
	t_is_pipe = 0;
}

static int tf_fiddle(tfd)
int tfd;
{
	if (tfd < 0) {
		tfd = open(DEV_NULL, O_RDONLY, 0);
		return -1;
	}
	if (tfd != 0) {
		dup2(tfd, 0);
		close(tfd);
	}
	return 0;
}

static int tf_xopen(fname, mode)
char *fname;
int mode;
{
	int fd;

	fd = open(fname, mode, 0600);
	fd = tf_fiddle(fd);
	return fd;
}

/* #define t_xopen(f,m) t_fiddle(open(f, m, 0600)) */

static const char *mode_txt(mode)
int mode;
{
	switch (mode & O_ACCMODE) {
	case O_RDONLY:
		return "r";
	case O_WRONLY:
		return "w";
	}
	return "r+";
}

void NDECL(tf_init)
{
	fclose(stdin);
	tf_xopen(DEV_NULL, O_RDONLY);
	t_fd = NULL;
	t_is_pipe = 0;
}

int tf_open(fname, mode)
char *fname;
int mode;
{
	tf_xclose(t_fd);
	return tf_xopen(fname, mode);
}

#ifdef TLI
int tf_topen(fam, mode)
int fam, mode;
{
	tf_xclose(t_fd);
	return tf_fiddle(t_open(fam, mode, NULL));
}

#endif

void tf_close(fdes)
int fdes;
{
	tf_xclose(t_fd);
	tf_xopen(DEV_NULL, O_RDONLY);
}

FILE *tf_fopen(fname, mode)
char *fname;
int mode;
{
	tf_xclose(t_fd);
	if (tf_xopen(fname, mode) >= 0) {
		t_fd = fdopen(0, mode_txt(mode));
		return t_fd;
	}
	return NULL;
}

void tf_fclose(fd)
FILE *fd;
{
	tf_xclose(t_fd);
	tf_xopen(DEV_NULL, O_RDONLY);
}

FILE *tf_popen(fname, mode)
char *fname;
int mode;
{
	tf_xclose(t_fd);
	t_fd = popen(fname, mode_txt(mode));
	if (t_fd != NULL) {
		t_is_pipe = 1;
	}
	return t_fd;
}

/* #define GNU_MALLOC_TEST 1 */

#ifdef GNU_MALLOC_TEST
extern unsigned int malloc_sbrk_used;	/* amount of data space used now */

#endif

/*
 * Check routine forward declaration. 
 */
static int FDECL(fwdlist_ck, (int, dbref, dbref, int, char *));

extern void FDECL(pcache_reload, (dbref));
extern void FDECL(desc_reload, (dbref));

/* *INDENT-OFF* */

/* List of built-in attributes */
ATTR attr[] =
{
{"Aahear",	A_AAHEAR,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Aclone",	A_ACLONE,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Aconnect",	A_ACONNECT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Adesc",	A_ADESC,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Adfail",	A_ADFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Adisconnect",	A_ADISCONNECT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Adrop",	A_ADROP,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Aefail",	A_AEFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Aenter",	A_AENTER,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Afail",	A_AFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Agfail",	A_AGFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Ahear",	A_AHEAR,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Akill",	A_AKILL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Aleave",	A_ALEAVE,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Alfail",	A_ALFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Alias",	A_ALIAS,	AF_NOPROG|AF_NOCMD|AF_NOCLONE|AF_PRIVATE|AF_CONST,
								NULL},
{"Allowance",	A_ALLOWANCE,	AF_MDARK|AF_NOPROG|AF_WIZARD,	NULL},
{"Amail",	A_AMAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Amhear",	A_AMHEAR,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Amove",	A_AMOVE,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Apay",	A_APAY,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Arfail",	A_ARFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Asucc",	A_ASUCC,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Atfail",	A_ATFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Atport",	A_ATPORT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Atofail",	A_ATOFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Aufail",	A_AUFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Ause",	A_AUSE,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Away",	A_AWAY,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Charges",	A_CHARGES,	AF_NOPROG,			NULL},
{"ChownLock",	A_LCHOWN,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Comment",	A_COMMENT,	AF_NOPROG|AF_MDARK|AF_WIZARD,	NULL},
{"Conformat",	A_LCON_FMT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"ControlLock",	A_LCONTROL,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Cost",	A_COST,		AF_NOPROG,			NULL},
{"Daily",	A_DAILY,	AF_NOPROG,			NULL},
{"DarkLock",	A_LDARK,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Desc",	A_DESC,		AF_DEFAULT|AF_VISUAL|AF_NOPROG,		NULL},
{"DefaultLock",	A_LOCK,		AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Destroyer",	A_DESTROYER,	AF_MDARK|AF_WIZARD|AF_NOPROG,	NULL},
{"Dfail",	A_DFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Drop",	A_DROP,		AF_DEFAULT|AF_NOPROG,			NULL},
{"DropLock",	A_LDROP,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Ealias",	A_EALIAS,	AF_NOPROG,			NULL},
{"Efail",	A_EFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Enter",	A_ENTER,	AF_DEFAULT|AF_NOPROG,			NULL},
{"EnterLock",	A_LENTER,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Exitformat",	A_LEXITS_FMT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Exitto",	A_EXITVARDEST,	AF_NOPROG,			NULL},
{"Fail",	A_FAIL,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Filter",	A_FILTER,	AF_NOPROG,			NULL},
{"Forwardlist",	A_FORWARDLIST,	AF_NOPROG,			fwdlist_ck},
{"Gfail",	A_GFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"GiveLock",	A_LGIVE,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"HeardLock",	A_LHEARD,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"HearsLock",	A_LHEARS,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Idesc",	A_IDESC,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Idle",	A_IDLE,		AF_NOPROG,			NULL},
{"Infilter",	A_INFILTER,	AF_NOPROG,			NULL},
{"Inprefix",	A_INPREFIX,	AF_NOPROG,			NULL},
{"Kill",	A_KILL,		AF_DEFAULT|AF_NOPROG,			NULL},
{"KnownLock",	A_LKNOWN,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"KnowsLock",	A_LKNOWS,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Lalias",	A_LALIAS,	AF_NOPROG,			NULL},
{"Last",	A_LAST,	AF_VISUAL|AF_WIZARD|AF_NOCMD|AF_NOPROG|AF_NOCLONE,
								NULL},
{"Lastip",	A_LASTIP,	AF_NOPROG|AF_NOCMD|AF_NOCLONE|AF_GOD,
								NULL},
{"Lastpage",	A_LASTPAGE,	AF_INTERNAL|AF_NOCMD|AF_NOPROG|AF_GOD|AF_PRIVATE,
								NULL},
{"Lastsite",	A_LASTSITE,	AF_NOPROG|AF_NOCMD|AF_NOCLONE|AF_GOD,
								NULL},
{"Leave",	A_LEAVE,	AF_DEFAULT|AF_NOPROG,			NULL},
{"LeaveLock",	A_LLEAVE,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Lfail",	A_LFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"LinkLock",	A_LLINK,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Listen",	A_LISTEN,	AF_NOPROG,			NULL},
{"Logindata",	A_LOGINDATA,	AF_MDARK|AF_NOPROG|AF_NOCMD|AF_CONST,
								NULL},
{"Mailcurf",	A_MAILCURF,	AF_MDARK|AF_WIZARD|AF_NOPROG|AF_NOCLONE,
								NULL},
{"Mailflags",	A_MAILFLAGS,	AF_MDARK|AF_WIZARD|AF_NOPROG|AF_NOCLONE,
								NULL},
{"Mailfolders",	A_MAILFOLDERS,	AF_MDARK|AF_WIZARD|AF_NOPROG|AF_NOCLONE,
								NULL},
{"Mailmsg",	A_MAILMSG,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{"Mailsub",	A_MAILSUB,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{"Mailsucc",	A_MAIL,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Mailto",	A_MAILTO,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{"MovedLock",	A_LMOVED,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"MovesLock",	A_LMOVES,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Move",	A_MOVE,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Name",	A_NAME,		AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{"Nameformat",	A_NAME_FMT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Newobjs",	A_NEWOBJS,	AF_MDARK|AF_NOPROG|AF_GOD|AF_NOCMD|AF_NOCLONE,
								NULL},
{"Odesc",	A_ODESC,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Odfail",	A_ODFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Odrop",	A_ODROP,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Oefail",	A_OEFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Oenter",	A_OENTER,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Ofail",	A_OFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Ogfail",	A_OGFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Okill",	A_OKILL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Oleave",	A_OLEAVE,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Olfail",	A_OLFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Omove",	A_OMOVE,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Opay",	A_OPAY,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Orfail",	A_ORFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Osucc",	A_OSUCC,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Otfail",	A_OTFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Otport",	A_OTPORT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Otofail",	A_OTOFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Oufail",	A_OUFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Ouse",	A_OUSE,		AF_DEFAULT|AF_NOPROG,			NULL},
{"Oxenter",	A_OXENTER,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Oxleave",	A_OXLEAVE,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Oxtport",	A_OXTPORT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"PageLock",	A_LPAGE,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"ParentLock",	A_LPARENT,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Pay",		A_PAY,		AF_NOPROG,			NULL},
{"Prefix",	A_PREFIX,	AF_NOPROG,			NULL},
{"Progcmd",	A_PROGCMD,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{"Queuemax",	A_QUEUEMAX,	AF_MDARK|AF_WIZARD|AF_NOPROG,	NULL},
{"Quota",	A_QUOTA,	AF_MDARK|AF_NOPROG|AF_GOD|AF_NOCMD|AF_NOCLONE,
								NULL},
{"ReceiveLock",	A_LRECEIVE,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Reject",	A_REJECT,	AF_NOPROG,			NULL},
{"Rfail",	A_RFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Rquota",	A_RQUOTA,	AF_MDARK|AF_NOPROG|AF_GOD|AF_NOCMD|AF_NOCLONE,
								NULL},
{"Runout",	A_RUNOUT,	AF_NOPROG,			NULL},
{"Semaphore",	A_SEMAPHORE,	AF_NOPROG|AF_WIZARD|AF_NOCMD|AF_NOCLONE,
								NULL},
{"Sex",		A_SEX,	AF_VISUAL|AF_NOPROG,			NULL},
{"Signature",	A_SIGNATURE,	AF_NOPROG,			NULL},
{"Speechformat",A_SPEECHFMT,	AF_DEFAULT|AF_NOPROG,		NULL},
{"SpeechLock",	A_LSPEECH,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Startup",	A_STARTUP,	AF_NOPROG,			NULL},
{"Succ",	A_SUCC,		AF_DEFAULT|AF_NOPROG,			NULL},
{"TeloutLock",	A_LTELOUT,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Tfail",	A_TFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Timeout",	A_TIMEOUT,	AF_MDARK|AF_NOPROG|AF_WIZARD,	NULL},
{"Tport",	A_TPORT,	AF_DEFAULT|AF_NOPROG,			NULL},
{"TportLock",	A_LTPORT,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Tofail",	A_TOFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Ufail",	A_UFAIL,	AF_DEFAULT|AF_NOPROG,			NULL},
{"Use",		A_USE,		AF_DEFAULT|AF_NOPROG,			NULL},
{"UseLock",	A_LUSE,		AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"UserLock",	A_LUSER,	AF_NOPROG|AF_NOCMD|AF_IS_LOCK,	NULL},
{"Va",		A_VA,		0,				NULL},
{"Vb",		A_VA+1,		0,				NULL},
{"Vc",		A_VA+2,		0,				NULL},
{"Vd",		A_VA+3,		0,				NULL},
{"Ve",		A_VA+4,		0,				NULL},
{"Vf",		A_VA+5,		0,				NULL},
{"Vg",		A_VA+6,		0,				NULL},
{"Vh",		A_VA+7,		0,				NULL},
{"Vi",		A_VA+8,		0,				NULL},
{"Vj",		A_VA+9,		0,				NULL},
{"Vk",		A_VA+10,	0,				NULL},
{"Vl",		A_VA+11,	0,				NULL},
{"Vm",		A_VA+12,	0,				NULL},
{"Vn",		A_VA+13,	0,				NULL},
{"Vo",		A_VA+14,	0,				NULL},
{"Vp",		A_VA+15,	0,				NULL},
{"Vq",		A_VA+16,	0,				NULL},
{"Vr",		A_VA+17,	0,				NULL},
{"Vs",		A_VA+18,	0,				NULL},
{"Vt",		A_VA+19,	0,				NULL},
{"Vu",		A_VA+20,	0,				NULL},
{"Vv",		A_VA+21,	0,				NULL},
{"Vw",		A_VA+22,	0,				NULL},
{"Vx",		A_VA+23,	0,				NULL},
{"Vy",		A_VA+24,	0,				NULL},
{"Vz",		A_VA+25,	0,				NULL},
{"Vrml_url",	A_VRML_URL,	AF_NOPROG,			NULL},
{"Htdesc",	A_HTDESC,	AF_DEFAULT|AF_VISUAL|AF_NOPROG,		NULL},
{"*Atrlist",	A_LIST,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,	NULL},
{"*Password",	A_PASS,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,	NULL},
{"*Money",	A_MONEY,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{"*Invalid",	A_TEMP,		AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{NULL,		0,		0,				NULL}};

/* *INDENT-ON* */

/* ---------------------------------------------------------------------------
 * fwdlist_set, fwdlist_clr: Manage cached forwarding lists
 */

void fwdlist_set(thing, ifp)
dbref thing;
FWDLIST *ifp;
{
	FWDLIST *fp, *xfp;
	int i, stat = 0;

	/* If fwdlist is null, clear */

	if (!ifp || (ifp->count <= 0)) {
		fwdlist_clr(thing);
		return;
	}
	/* Copy input forwardlist to a correctly-sized buffer */

	fp = (FWDLIST *) XMALLOC(sizeof(FWDLIST), "fwdlist_set");
	fp->data = (int *) XCALLOC(ifp->count, sizeof(int),
				   "fwdlist_set.data");
	for (i = 0; i < ifp->count; i++) {
		fp->data[i] = ifp->data[i];
	}
	fp->count = ifp->count;

	/*
	 * Replace an existing forwardlist, or add a new one 
	 */

	xfp = fwdlist_get(thing);
	if (xfp) {
	    if (xfp->data)
		XFREE(xfp->data, "fwdlist_set.xfp_data");
	    XFREE(xfp, "fwdlist_set.xfp");
	    stat = nhashrepl(thing, (int *)fp, &mudstate.fwdlist_htab);
	} else {
	    stat = nhashadd(thing, (int *)fp, &mudstate.fwdlist_htab);
	}
	if (stat < 0) {              /* the add or replace failed */
	    if (fp->data)
		XFREE(fp->data, "fwdlist_set.fp_data");
	    XFREE(fp, "fwdlist_set.fp");
	}
}

void fwdlist_clr(thing)
dbref thing;
{
	FWDLIST *xfp;

	/* If a forwardlist exists, delete it */

	xfp = fwdlist_get(thing);
	if (xfp) {
	    if (xfp->data)
		XFREE(xfp->data, "fwdlist_clr.data");
	    XFREE(xfp, "fwdlist_clr");
	    nhashdelete(thing, &mudstate.fwdlist_htab);
	}
}

/* ---------------------------------------------------------------------------
 * fwdlist_load: Load text into a forwardlist.
 */

int fwdlist_load(fp, player, atext)
FWDLIST *fp;
dbref player;
char *atext;
{
    dbref target;
    char *tp, *bp, *dp;
    int i, count, errors, fail;
    int *tmp_array;
    
    tmp_array = (int *)XCALLOC((LBUF_SIZE / 2), sizeof(int), 
    		"fwdlist_load.tmp");

    count = 0;
    errors = 0;
    bp = tp = alloc_lbuf("fwdlist_load.str");
    strcpy(tp, atext);
    do {
	for (; *bp && isspace(*bp); bp++) ;	/* skip spaces */
	for (dp = bp; *bp && !isspace(*bp); bp++) ;	/* remember string */
	if (*bp)
	    *bp++ = '\0';	/* terminate string */
	if ((*dp++ == '#') && isdigit(*dp)) {
	    target = atoi(dp);

	    if (!mudstate.standalone) {
		    fail = (!Good_obj(target) ||
			    (!God(player) &&
			     !controls(player, target) &&
			     (!Link_ok(target) ||
			      !could_doit(player, target, A_LLINK))));
	    } else {
		    fail = !Good_obj(target);
	    }
	    
	    if (fail) {
		if (!mudstate.standalone)
			notify(player,
			       tprintf("Cannot forward to #%d: Permission denied.",
				       target));
		errors++;
	    } else if (count < mudconf.fwdlist_lim) {
		if (fp->data)
		    fp->data[count++] = target;
		else 
		    tmp_array[count++] = target;
	    } else {
		if (!mudstate.standalone)
			notify(player,
			       tprintf("Cannot forward to #%d: Forwardlist limit exceeded.",
				       target));
		errors++;
	    }
	}
    } while (*bp);

    free_lbuf(tp);

    if ((fp->data == NULL) && (count > 0)) {
	fp->data = (int *) XCALLOC(count, sizeof(int), "fwdlist_load.data");
	for (i = 0; i < count; i++)
	    fp->data[i] = tmp_array[i];
    }

    fp->count = count;
    XFREE(tmp_array, "fwdlist_load.tmp");
    return errors;
}

/*
 * ---------------------------------------------------------------------------
 * fwdlist_rewrite: Generate a text string from a FWDLIST buffer.
 */

int fwdlist_rewrite(fp, atext)
FWDLIST *fp;
char *atext;
{
	char *tp, *bp;
	int i, count;

	if (fp && fp->count) {
		count = fp->count;
		tp = alloc_sbuf("fwdlist_rewrite.errors");
		bp = atext;
		for (i = 0; i < fp->count; i++) {
			if (Good_obj(fp->data[i])) {
				sprintf(tp, "#%d ", fp->data[i]);
				safe_str(tp, atext, &bp);
			} else {
				count--;
			}
		}
		*bp = '\0';
		free_sbuf(tp);
	} else {
		count = 0;
		*atext = '\0';
	}
	return count;
}

/* ---------------------------------------------------------------------------
 * fwdlist_ck:  Check a list of dbref numbers to forward to for AUDIBLE
 */

static int fwdlist_ck(key, player, thing, anum, atext)
int key, anum;
dbref player, thing;
char *atext;
{
	FWDLIST *fp;
	int count;

	if (mudstate.standalone) 
		return 1;

	count = 0;

	if (atext && *atext) {
		fp = (FWDLIST *) XMALLOC(sizeof(FWDLIST), "fwdlist_ck.fp");
		fp->data = NULL;
		fwdlist_load(fp, player, atext);
	} else {
		fp = NULL;
	}

	/* Set the cached forwardlist */

	fwdlist_set(thing, fp);
	count = fwdlist_rewrite(fp, atext);
	if (fp) {
	    if (fp->data)
		XFREE(fp->data, "fwdlist_ck.fp_data");
	    XFREE(fp, "fwdlist_ck.fp");
	}
	return ((count > 0) || !atext || !*atext);
}

FWDLIST *fwdlist_get(thing)
dbref thing;
{
	dbref aowner;
	int aflags, alen, errors;
	char *tp;
	static FWDLIST *fp = NULL;

	if (!mudstate.standalone)
		return (FWDLIST *)nhashfind((thing), &mudstate.fwdlist_htab);
		
	if (!fp) {
	    fp = (FWDLIST *) XMALLOC(sizeof(FWDLIST), "fwdlist_get");
	    fp->data = NULL;
	}
	tp = atr_get(thing, A_FORWARDLIST, &aowner, &aflags, &alen);
	errors = fwdlist_load(fp, GOD, tp);
	free_lbuf(tp);
	return fp;
}

static char *set_string(ptr, new)
char **ptr, *new;
{
	/* if pointer not null, free it */

	if (*ptr)
		XFREE(*ptr, "set_string");

	/* if new string is not null allocate space for it and copy it */

	if (!new)		/* || !*new */
		return (*ptr = NULL);	/* Check with GAC about this */
	*ptr = XSTRDUP(new, "set_string");
	return (*ptr);
}

/* ---------------------------------------------------------------------------
 * Name, s_Name: Get or set an object's name.
 */

INLINE void safe_name(thing, outbuf, bufc)
    dbref thing;
    char *outbuf, **bufc;
{
	dbref aowner;
	int aflags, alen;
	time_t save_access_time;
	char *buff;

	/* Retrieving a name never counts against an object's access time. */
	save_access_time = AccessTime(thing);

	if (!purenames[thing]) {
		buff = atr_get(thing, A_NAME, &aowner, &aflags, &alen);
		set_string(&purenames[thing], strip_ansi(buff));
		free_lbuf(buff);
	}

	if (!names[thing]) {
		buff = atr_get(thing, A_NAME, &aowner, &aflags, &alen);
		set_string(&names[thing], buff);
    		s_NameLen(thing, alen);
		free_lbuf(buff);
	}
	safe_known_str(names[thing], NameLen(thing), outbuf, bufc);

	s_AccessTime(thing, save_access_time);
}

INLINE char *Name(thing)
dbref thing;
{
	dbref aowner;
	int aflags, alen;
	time_t save_access_time;
	char *buff;

	save_access_time = AccessTime(thing);

	if (!purenames[thing]) {
		buff = atr_get(thing, A_NAME, &aowner, &aflags, &alen);
		set_string(&purenames[thing], strip_ansi(buff));
		free_lbuf(buff);
	}

	if (!names[thing]) {
		buff = atr_get(thing, A_NAME, &aowner, &aflags, &alen);
		set_string(&names[thing], buff);
    		s_NameLen(thing, alen);
    		free_lbuf(buff);
	}
	s_AccessTime(thing, save_access_time);
	return names[thing];
}

INLINE char *PureName(thing)
dbref thing;
{
	dbref aowner;
	int aflags, alen;
	time_t save_access_time;
	char *buff;

	save_access_time = AccessTime(thing);

	if (!names[thing]) {
		buff = atr_get(thing, A_NAME, &aowner, &aflags, &alen);
		set_string(&names[thing], buff);
    		s_NameLen(thing, alen);
		free_lbuf(buff);
	}

	if (!purenames[thing]) {
		buff = atr_get(thing, A_NAME, &aowner, &aflags, &alen);
		set_string(&purenames[thing], strip_ansi(buff));
		free_lbuf(buff);
	}
	s_AccessTime(thing, save_access_time);
	return purenames[thing];
}

INLINE void s_Name(thing, s)
dbref thing;
char *s;
{
    int len;

    /* Truncate the name if we have to */

    if (s) {
	len = strlen(s);
	if (len > MBUF_SIZE) {
	    len = MBUF_SIZE - 1;
	    s[len] = '\0';
	}
    } else {
	len = 0;
    }
	
    atr_add_raw(thing, A_NAME, (char *)s);
    s_NameLen(thing, len);
    set_string(&names[thing], (char *)s);
    set_string(&purenames[thing], strip_ansi((char *)s));
}

void safe_exit_name(it, buff, bufc)
dbref it;
char *buff, **bufc;
{
	char *s = *bufc;
	int ansi_state = ANST_NORMAL;

	safe_name(it, buff, bufc);

	while (*s && (*s != EXIT_DELIMITER)) {
		if (*s == ESC_CHAR) {
			track_esccode(s, ansi_state);
		} else {
			++s;
		}
	}

	*bufc = s;
	safe_str(ansi_transition_esccode(ansi_state, ANST_NORMAL), buff, bufc);
}

void s_Pass(thing, s)
dbref thing;
const char *s;
{
	atr_add_raw(thing, A_PASS, (char *)s);
}

/* ---------------------------------------------------------------------------
 * do_attribute: Manage user-named attributes.
 */

extern NAMETAB attraccess_nametab[];

void do_attribute(player, cause, key, aname, value)
dbref player, cause;
int key;
char *aname, *value;
{
	int success, negate, f;
	char *buff, *sp, *p, *q, *tbuf, *tokst;
	VATTR *va;
	ATTR *va2;

	/* Look up the user-named attribute we want to play with.
	 * Note vattr names have a limited size.
	 */

	buff = alloc_sbuf("do_attribute");
	for (p = buff, q = aname;
	     *q && ((p - buff) < (VNAME_SIZE - 1));
	     p++, q++)
		*p = toupper(*q);
	*p = '\0';

	if (!(ok_attr_name(buff) && (va = vattr_find(buff)))) {
		notify(player, "No such user-named attribute.");
		free_sbuf(buff);
		return;
	}
	switch (key) {
	case ATTRIB_ACCESS:

		/* Modify access to user-named attribute */

		for (sp = value; *sp; sp++)
			*sp = toupper(*sp);
		sp = strtok_r(value, " ", &tokst);
		success = 0;
		while (sp != NULL) {

			/* Check for negation */

			negate = 0;
			if (*sp == '!') {
				negate = 1;
				sp++;
			}
			/* Set or clear the appropriate bit */

			f = search_nametab(player, attraccess_nametab, sp);
			if (f > 0) {
				success = 1;
				if (negate)
					va->flags &= ~f;
				else
					va->flags |= f;
					
				/* Set the dirty bit */
				va->flags |= AF_DIRTY;
			} else {
				notify(player,
				    tprintf("Unknown permission: %s.", sp));
			}

			/* Get the next token */

			sp = strtok_r(NULL, " ", &tokst);
		}
		if (success && !Quiet(player))
			notify(player, "Attribute access changed.");
		break;

	case ATTRIB_RENAME:

		/* Make sure the new name doesn't already exist */

		va2 = atr_str(value);
		if (va2) {
			notify(player,
			     "An attribute with that name already exists.");
			free_sbuf(buff);
			return;
		}
		if (vattr_rename(va->name, value) == NULL)
			notify(player, "Attribute rename failed.");
		else
			notify(player, "Attribute renamed.");
		break;

	case ATTRIB_DELETE:

		/* Remove the attribute */

		vattr_delete(buff);
		notify(player, "Attribute deleted.");
		break;

	case ATTRIB_INFO:

		/* Print info, like @list user_attr does */

	        if (!(va->flags & AF_DELETED)) {
		    tbuf = alloc_lbuf("attribute_info");
		    sprintf(tbuf, "%s(%d):", va->name, va->number);
		    listset_nametab(player, attraccess_nametab, va->flags,
				    tbuf, 1);
		    free_lbuf(tbuf);
		} else {
		    notify(player, "That attribute has been deleted.");
		}
		break;
	}
	free_sbuf(buff);
	return;
}

/* ---------------------------------------------------------------------------
 * do_fixdb: Directly edit database fields
 */

void do_fixdb(player, cause, key, arg1, arg2)
dbref player, cause;
int key;
char *arg1, *arg2;
{
	dbref thing, res;

	init_match(player, arg1, NOTYPE);
	match_everything(0);
	thing = noisy_match_result();
	if (thing == NOTHING)
		return;

	res = NOTHING;
	switch (key) {
	case FIXDB_OWNER:
	case FIXDB_LOC:
	case FIXDB_CON:
	case FIXDB_EXITS:
	case FIXDB_NEXT:
		init_match(player, arg2, NOTYPE);
		match_everything(0);
		res = noisy_match_result();
		break;
	case FIXDB_PENNIES:
		res = atoi(arg2);
		break;
	}

	switch (key) {
	case FIXDB_OWNER:
		s_Owner(thing, res);
		if (!Quiet(player))
			notify(player, tprintf("Owner set to #%d", res));
		break;
	case FIXDB_LOC:
		s_Location(thing, res);
		if (!Quiet(player))
			notify(player, tprintf("Location set to #%d", res));
		break;
	case FIXDB_CON:
		s_Contents(thing, res);
		if (!Quiet(player))
			notify(player, tprintf("Contents set to #%d", res));
		break;
	case FIXDB_EXITS:
		s_Exits(thing, res);
		if (!Quiet(player))
			notify(player, tprintf("Exits set to #%d", res));
		break;
	case FIXDB_NEXT:
		s_Next(thing, res);
		if (!Quiet(player))
			notify(player, tprintf("Next set to #%d", res));
		break;
	case FIXDB_PENNIES:
		s_Pennies(thing, res);
		if (!Quiet(player))
			notify(player, tprintf("Pennies set to %d", res));
		break;
	case FIXDB_NAME:
		if (Typeof(thing) == TYPE_PLAYER) {
			if (!ok_player_name(arg2)) {
				notify(player,
				    "That's not a good name for a player.");
				return;
			}
			if (lookup_player(NOTHING, arg2, 0) != NOTHING) {
				notify(player,
				       "That name is already in use.");
				return;
			}
			STARTLOG(LOG_SECURITY, "SEC", "CNAME")
				log_name(thing),
				log_printf(" renamed to %s", strip_ansi(arg2));
			ENDLOG
				if (Suspect(player)) {
				raw_broadcast(WIZARD,
					      "[Suspect] %s renamed to %s",
					      Name(thing), arg2);
			}
			delete_player_name(thing, Name(thing));
			s_Name(thing, arg2);
			add_player_name(thing, arg2);
		} else {
			if (!ok_name(arg2)) {
				notify(player,
				 "Warning: That is not a reasonable name.");
			}
			s_Name(thing, arg2);
		}
		if (!Quiet(player))
			notify(player, tprintf("Name set to %s", arg2));
		break;
	}
}

/* ---------------------------------------------------------------------------
 * init_attrtab: Initialize the attribute hash tables.
 */

void NDECL(init_attrtab)
{
	ATTR *a;
	char *buff, *p, *q;

	hashinit(&mudstate.attr_name_htab, 100 * HASH_FACTOR, HT_STR);
	buff = alloc_sbuf("init_attrtab");
	for (a = attr; a->number; a++) {
		anum_extend(a->number);
		anum_set(a->number, a);
		for (p = buff, q = (char *)a->name; *q; p++, q++)
			*p = toupper(*q);
		*p = '\0';
		hashadd(buff, (int *)a, &mudstate.attr_name_htab, 0);
	}
	free_sbuf(buff);
}

/* ---------------------------------------------------------------------------
 * atr_str: Look up an attribute by name.
 */

ATTR *atr_str(s)
char *s;
{
	char *buff, *p, *q;
	ATTR *a;
	VATTR *va;
	static ATTR tattr;

	/* Convert the buffer name to uppercase. Limit length of name.  */

	buff = alloc_sbuf("atr_str");
	for (p = buff, q = s; *q && ((p - buff) < (VNAME_SIZE - 1)); p++, q++)
		*p = toupper(*q);
	*p = '\0';

	if (!ok_attr_name(buff)) {
		free_sbuf(buff);
		return NULL;
	}

	/* Look for a predefined attribute */

	if (!mudstate.standalone) {
		a = (ATTR *) hashfind(buff, &mudstate.attr_name_htab);
		if (a != NULL) {
			free_sbuf(buff);
			return a;
		}
	} else {
		for (a = attr; a->name; a++) {
			if (!string_compare(a->name, s)) {
				free_sbuf(buff);
				return a;
			}
		}
	}

	/* Nope, look for a user attribute */

	va = vattr_find(buff);
	free_sbuf(buff);

	/* If we got one, load tattr and return a pointer to it. */

	if (va != NULL) {
		tattr.name = va->name;
		tattr.number = va->number;
		tattr.flags = va->flags;
		tattr.check = NULL;
		return &tattr;
	}

	if (mudstate.standalone) {
		/*
		 * No exact match, try for a prefix match on predefined attribs.
		 * Check for both longer versions and shorter versions. 
		 */

		for (a = attr; a->name; a++) {
			if (string_prefix(s, a->name))
				return a;
			if (string_prefix(a->name, s))
				return a;
		}
	}

	/* All failed, return NULL */

	return NULL;
}

/* ---------------------------------------------------------------------------
 * anum_extend: Grow the attr num lookup table.
 */

ATTR **anum_table = NULL;
int anum_alc_top = 0;

void anum_extend(newtop)
int newtop;
{
	ATTR **anum_table2;
	int delta, i;

	if (!mudstate.standalone)
		delta = mudconf.init_size;
	else
		delta = 1000;

	if (newtop <= anum_alc_top)
		return;
	if (newtop < anum_alc_top + delta)
		newtop = anum_alc_top + delta;
	if (anum_table == NULL) {
		anum_table = (ATTR **) XCALLOC(newtop + 1, sizeof(ATTR *),
					       "anum_extend");
	} else {
		anum_table2 = (ATTR **) XCALLOC(newtop + 1, sizeof(ATTR *),
						"anum_extend");
		for (i = 0; i <= anum_alc_top; i++)
			anum_table2[i] = anum_table[i];
		XFREE(anum_table, "anum_extend");
		anum_table = anum_table2;
	}
	anum_alc_top = newtop;
}

/* ---------------------------------------------------------------------------
 * atr_num: Look up an attribute by number.
 */

ATTR *atr_num(anum)
int anum;
{
	VATTR *va;
	static ATTR tattr;

	/* Look for a predefined attribute */

	if (anum < A_USER_START)
		return anum_get(anum);

	if (anum > anum_alc_top)
		return NULL;

	/* It's a user-defined attribute, we need to copy data */

	va = (VATTR *) anum_get(anum);
	if (va != NULL) {
		tattr.name = va->name;
		tattr.number = va->number;
		tattr.flags = va->flags;
		tattr.check = NULL;
		return &tattr;
	}
	/* All failed, return NULL */

	return NULL;
}

/* ---------------------------------------------------------------------------
 * mkattr: Lookup attribute by name, creating if needed.
 */

int mkattr(buff)
char *buff;
{
	ATTR *ap;
	VATTR *va;
	int vflags;
	KEYLIST *kp;

	if (!(ap = atr_str(buff))) {

		/* Unknown attr, create a new one.
		 * Check if it matches any attribute type pattern that
		 * we have defined; if it does, give it those flags.
		 * Otherwise, use the default vattr flags.
		 */

		if (!mudstate.standalone) {
			vflags = mudconf.vattr_flags;
			for (kp = mudconf.vattr_flag_list; kp != NULL; kp = kp->next) {
			    if (quick_wild(kp->name, buff)) {
				vflags = kp->data;
				break;
			    }
			}
			va = vattr_alloc(buff, vflags);
		} else {
			va = vattr_alloc(buff, mudconf.vattr_flags);
		}
	
		if (!va || !(va->number))
			return -1;
		return va->number;
	}
	if (!(ap->number))
		return -1;
	return ap->number;
}

/* ---------------------------------------------------------------------------
 * al_decode: Fetch an attribute number from an alist.
 */

static int al_decode(app)
char **app;
{
	int atrnum = 0, anum, atrshft = 0;
	char *ap;

	ap = *app;

	for (;;) {
		anum = ((*ap) & 0x7f);
		if (atrshft > 0)
			atrnum += (anum << atrshft);
		else
			atrnum = anum;
		if (!(*ap++ & 0x80)) {
			*app = ap;
			return atrnum;
		}
		atrshft += 7;
	}
	/* NOTREACHED */
}

/* ---------------------------------------------------------------------------
 * al_code: Store an attribute number in an alist.
 */

static void al_code(app, atrnum)
char **app;
int atrnum;
{
	char *ap;

	ap = *app;

	for (;;) {
		*ap = atrnum & 0x7f;
		atrnum = atrnum >> 7;
		if (!atrnum) {
			*app = ++ap;
			return;
		}
		*ap++ |= 0x80;
	}
}

/* ---------------------------------------------------------------------------
 * Commer: check if an object has any $-commands in its attributes.
 */

int Commer(thing)
dbref thing;
{
	char *s, *as;
	int attr, aflags, alen;
	dbref aowner;
	ATTR *ap;

	if ((!Has_Commands(thing) && mudconf.req_cmds_flag) || Halted(thing))
		return 0;

	s = alloc_lbuf("Commer");
	atr_push();
	for (attr = atr_head(thing, &as); attr; attr = atr_next(&as)) {
		ap = atr_num(attr);
		if (!ap || (ap->flags & AF_NOPROG))
			continue;

		s = atr_get_str(s, thing, attr, &aowner, &aflags, &alen);

		if ((*s == '$') && !(aflags & AF_NOPROG)) {
			atr_pop();
			free_lbuf(s);
			return 1;
		}
	}
	atr_pop();
	free_lbuf(s);
	return 0;
}

/* routines to handle object attribute lists */

/* ---------------------------------------------------------------------------
 * al_size, al_fetch, al_store, al_add, al_delete: Manipulate attribute lists
 */

/* al_extend: Get more space for attributes, if needed */

void al_extend(buffer, bufsiz, len, copy)
char **buffer;
int *bufsiz, len, copy;
{
	char *tbuff;
	int newsize;
	
	if (len > *bufsiz) {
		newsize = len + ATR_BUF_CHUNK;
		tbuff = (char *) XMALLOC(newsize, "al_extend");
		if (*buffer) {
			if (copy)
				memcpy(tbuff, *buffer, *bufsiz);
			XFREE(*buffer, "al_extend");
		}
		*buffer = tbuff;
		*bufsiz = newsize;
	}
}

/* al_size: Return length of attribute list in chars */

int al_size(astr)
char *astr;
{
	if (!astr)
		return 0;
	return (strlen(astr) + 1);
}

/* al_store: Write modified attribute list */

void NDECL(al_store)
{
	if (mudstate.mod_al_id != NOTHING) {
		if (mudstate.mod_alist && *mudstate.mod_alist) {
			atr_add_raw(mudstate.mod_al_id, A_LIST,
				mudstate.mod_alist);
		} else {
			atr_clr(mudstate.mod_al_id, A_LIST);
		}	
	}
	mudstate.mod_al_id = NOTHING;
}

/* al_fetch: Load attribute list */

char *al_fetch(thing)
dbref thing;
{
	char *astr;
	int len;

	/* We only need fetch if we change things */

	if (mudstate.mod_al_id == thing)
		return mudstate.mod_alist;

	/* Fetch and set up the attribute list */

	al_store();
	astr = atr_get_raw(thing, A_LIST);
	if (astr) {
		len = al_size(astr);
		al_extend(&mudstate.mod_alist, &mudstate.mod_size, len, 0);
		memcpy(mudstate.mod_alist, astr, len);
	} else {
		al_extend(&mudstate.mod_alist, &mudstate.mod_size, 1, 0);
		*mudstate.mod_alist = '\0';
	}
	mudstate.mod_al_id = thing;
	return mudstate.mod_alist;
}

/* al_add: Add an attribute to an attribute list */

void al_add(thing, attrnum)
dbref thing;
int attrnum;
{
	char *abuf, *cp;
	int anum;

	/* If trying to modify List attrib, return.  Otherwise, get the
	 * attribute list. 
	 */

	if (attrnum == A_LIST)
		return;
	abuf = al_fetch(thing);

	/* See if attr is in the list.  If so, exit (need not do anything) */

	cp = abuf;
	while (*cp) {
		anum = al_decode(&cp);
		if (anum == attrnum)
			return;
	}

	/* Nope, extend it */

	al_extend(&mudstate.mod_alist, &mudstate.mod_size,
		  (cp - abuf + ATR_BUF_INCR), 1);
	if (mudstate.mod_alist != abuf) {

		/* extend returned different buffer, re-find the end */

		abuf = mudstate.mod_alist;
		for (cp = abuf; *cp; anum = al_decode(&cp)) ;
	}
	/* Add the new attribute on to the end */

	al_code(&cp, attrnum);
	*cp = '\0';

	return;
}

/* al_delete: Remove an attribute from an attribute list */

void al_delete(thing, attrnum)
dbref thing;
int attrnum;
{
	int anum;
	char *abuf, *cp, *dp;

	/* If trying to modify List attrib, return.  Otherwise, get the
	 * attribute list. 
	 */

	if (attrnum == A_LIST)
		return;
	abuf = al_fetch(thing);
	if (!abuf)
		return;

	cp = abuf;
	while (*cp) {
		dp = cp;
		anum = al_decode(&cp);
		if (anum == attrnum) {
			while (*cp) {
				anum = al_decode(&cp);
				al_code(&dp, anum);
			}
			*dp = '\0';
			return;
		}
	}
	return;
}

INLINE static void makekey(thing, atr, abuff)
dbref thing;
int atr;
Aname *abuff;
{
	abuff->object = thing;
	abuff->attrnum = atr;
	return;
}

/* ---------------------------------------------------------------------------
 * al_destroy: wipe out an object's attribute list.
 */

void al_destroy(thing)
dbref thing;
{
	if (mudstate.mod_al_id == thing)
		al_store();	/* remove from cache */
	atr_clr(thing, A_LIST);
}

/* ---------------------------------------------------------------------------
 * atr_encode: Encode an attribute string.
 */

static char *atr_encode(iattr, thing, owner, flags, atr)
char *iattr;
dbref thing, owner;
int flags, atr;
{

	/* If using the default owner and flags (almost all attributes will),
	 * just store the string. 
	 */

	if (((owner == Owner(thing)) || (owner == NOTHING)) && !flags)
		return iattr;

	/* Encode owner and flags into the attribute text */

	if (owner == NOTHING)
		owner = Owner(thing);
	return tprintf("%c%d:%d:%s", ATR_INFO_CHAR, owner, flags, iattr);
}

/* ---------------------------------------------------------------------------
 * atr_decode: Decode an attribute string.
 */

static void atr_decode(iattr, oattr, thing, owner, flags, atr, alen)
char *iattr, *oattr;
dbref thing, *owner;
int *flags, atr, *alen;
{
	char *cp;
	int neg;

	/* See if the first char of the attribute is the special character */

	if (*iattr == ATR_INFO_CHAR) {

		/* It is, crack the attr apart */

		cp = &iattr[1];

		/* Get the attribute owner */

		*owner = 0;
		neg = 0;
		if (*cp == '-') {
			neg = 1;
			cp++;
		}
		while (isdigit(*cp)) {
			*owner = (*owner * 10) + (*cp++ - '0');
		}
		if (neg)
			*owner = 0 - *owner;

		/* If delimiter is not ':', just return attribute */

		if (*cp++ != ':') {
			*owner = Owner(thing);
			*flags = 0;
			if (oattr) {
			    StrCopyLen(oattr, iattr, alen);
			}
			return;
		}
		/* Get the attribute flags */

		*flags = 0;
		while (isdigit(*cp)) {
			*flags = (*flags * 10) + (*cp++ - '0');
		}

		/* If delimiter is not ':', just return attribute */

		if (*cp++ != ':') {
			*owner = Owner(thing);
			*flags = 0;
			if (oattr) {
			    StrCopyLen(oattr, iattr, alen);
			}
		}
		/* Get the attribute text */

		if (oattr) {
			StrCopyLen(oattr, cp, alen);
		}
		if (*owner == NOTHING)
			*owner = Owner(thing);
	} else {

		/* Not the special character, return normal info */

		*owner = Owner(thing);
		*flags = 0;
		if (oattr) {
		    StrCopyLen(oattr, iattr, alen);
		}
	}
}

/* ---------------------------------------------------------------------------
 * atr_clr: clear an attribute in the list.
 */

void atr_clr(thing, atr)
dbref thing;
int atr;
{
	Aname okey;
	DBData key;
	
	makekey(thing, atr, &okey);
	
	/* Delete the entry from cache */
	
	key.dptr = &okey;
	key.dsize = sizeof(Aname);
	cache_del(key, DBTYPE_ATTRIBUTE);

	al_delete(thing, atr);

	if (!mudstate.standalone && !mudstate.loading_db)
	    s_Modified(thing);

	switch (atr) {
	case A_STARTUP:
		s_Flags(thing, Flags(thing) & ~HAS_STARTUP);
		break;
	case A_DAILY:
		s_Flags2(thing, Flags2(thing) & ~HAS_DAILY);
		if (!mudstate.standalone)
			(void) cron_clr(thing, A_DAILY);
		break;
	case A_FORWARDLIST:
		s_Flags2(thing, Flags2(thing) & ~HAS_FWDLIST);
		break;
	case A_LISTEN:
		s_Flags2(thing, Flags2(thing) & ~HAS_LISTEN);
		break;
	case A_SPEECHFMT:
		s_Flags3(thing, Flags3(thing) & ~HAS_SPEECHMOD);
		break;
	case A_TIMEOUT:
		if (!mudstate.standalone)
			desc_reload(thing);
		break;
	case A_QUEUEMAX:
		if (!mudstate.standalone)
			pcache_reload(thing);
		break;
	}
}

/* ---------------------------------------------------------------------------
 * atr_add_raw, atr_add: add attribute of type atr to list
 */

void atr_add_raw(thing, atr, buff)
dbref thing;
int atr;
char *buff;
{
	Attr *a;
	Aname okey;
	DBData key, data;
	
	makekey(thing, atr, &okey);
	
	if (!buff || !*buff) {
		/* Delete the entry from cache */

		key.dptr = &okey;
		key.dsize = sizeof(Aname);
		cache_del(key, DBTYPE_ATTRIBUTE);

		al_delete(thing, atr);
		return;
	}

	if ((a = (Attr *) XMALLOC(strlen(buff) + 1, "atr_add_raw.2")) == (char *)0) {
		return;
	}
	strcpy(a, buff);

	/* Store the value in cache */
	
	key.dptr = &okey;
	key.dsize = sizeof(Aname);
	data.dptr = a;
	data.dsize = strlen(a) + 1;
	cache_put(key, data, DBTYPE_ATTRIBUTE);
	
	al_add(thing, atr);

	if (!mudstate.standalone && !mudstate.loading_db)
	    s_Modified(thing);

	switch (atr) {
	case A_STARTUP:
		s_Flags(thing, Flags(thing) | HAS_STARTUP);
		break;
	case A_DAILY:
		s_Flags2(thing, Flags2(thing) | HAS_DAILY);
		if (!mudstate.standalone && !mudstate.loading_db) {
		    char tbuf[SBUF_SIZE];
		    (void) cron_clr(thing, A_DAILY);
		    sprintf(tbuf, "0 %d * * *", mudconf.events_daily_hour);
		    call_cron(thing, thing, A_DAILY, tbuf);
		}
		break;
	case A_FORWARDLIST:
		s_Flags2(thing, Flags2(thing) | HAS_FWDLIST);
		break;
	case A_LISTEN:
		s_Flags2(thing, Flags2(thing) | HAS_LISTEN);
		break;
	case A_SPEECHFMT:
		s_Flags3(thing, Flags3(thing) | HAS_SPEECHMOD);
		break;
	case A_TIMEOUT:
		if (!mudstate.standalone)
			desc_reload(thing);
		break;
	case A_QUEUEMAX:
		if (!mudstate.standalone)
			pcache_reload(thing);
		break;
	}
}

void atr_add(thing, atr, buff, owner, flags)
dbref thing, owner;
int atr, flags;
char *buff;
{
	char *tbuff;

	if (!buff || !*buff) {
		atr_clr(thing, atr);
	} else {
		tbuff = atr_encode(buff, thing, owner, flags, atr);
		atr_add_raw(thing, atr, tbuff);
	}
}

void atr_set_owner(thing, atr, owner)
dbref thing, owner;
int atr;
{
	dbref aowner;
	int aflags, alen;
	char *buff;

	buff = atr_get(thing, atr, &aowner, &aflags, &alen);
	atr_add(thing, atr, buff, owner, aflags);
	free_lbuf(buff);
}

void atr_set_flags(thing, atr, flags)
dbref thing, flags;
int atr;
{
	dbref aowner;
	int aflags, alen;
	char *buff;

	buff = atr_get(thing, atr, &aowner, &aflags, &alen);
	atr_add(thing, atr, buff, aowner, flags);
	free_lbuf(buff);
}

/* ---------------------------------------------------------------------------
 * atr_get_raw, atr_get_str, atr_get: Get an attribute from the database.
 */

char *atr_get_raw(thing, atr)
dbref thing;
int atr;
{
	DBData key, data;
	Aname okey;
	
	if (Typeof(thing) == TYPE_GARBAGE)
		return NULL;

	if (!mudstate.standalone && !mudstate.loading_db)
		s_Accessed(thing);
	
	makekey(thing, atr, &okey);
	
	/* Fetch the entry from cache and return it */
	
	key.dptr = &okey;
	key.dsize = sizeof(Aname);
	data = cache_get(key, DBTYPE_ATTRIBUTE);
	return data.dptr;
}

char *atr_get_str(s, thing, atr, owner, flags, alen)
char *s;
dbref thing, *owner;
int atr, *flags, *alen;
{
	char *buff;

	buff = atr_get_raw(thing, atr);
	if (!buff) {
		*owner = Owner(thing);
		*flags = 0;
		*alen = 0;
		*s = '\0';
	} else {
		atr_decode(buff, s, thing, owner, flags, atr, alen);
	}
	return s;
}

char *atr_get(thing, atr, owner, flags, alen)
dbref thing, *owner;
int atr, *flags, *alen;
{
	char *buff;

	buff = alloc_lbuf("atr_get");
	return atr_get_str(buff, thing, atr, owner, flags, alen);
}

int atr_get_info(thing, atr, owner, flags)
dbref thing, *owner;
int atr, *flags;
{
        int alen;
	char *buff;

	buff = atr_get_raw(thing, atr);
	if (!buff) {
		*owner = Owner(thing);
		*flags = 0;
		return 0;
	}
	atr_decode(buff, NULL, thing, owner, flags, atr, &alen);
	return 1;
}

char *atr_pget_str(s, thing, atr, owner, flags, alen)
char *s;
dbref thing, *owner;
int atr, *flags, *alen;
{
	char *buff;
	dbref parent;
	int lev;

	ATTR *ap;

	ITER_PARENTS(thing, parent, lev) {
		buff = atr_get_raw(parent, atr);
		if (buff && *buff) {
			atr_decode(buff, s, thing, owner, flags, atr, alen);
			if ((lev == 0) || !(*flags & AF_PRIVATE))
				return s;
		}
		if ((lev == 0) && Good_obj(Parent(parent))) {
			ap = atr_num(atr);
			if (!ap || ap->flags & AF_PRIVATE)
				break;
		}
	}
	*owner = Owner(thing);
	*flags = 0;
	*alen = 0;
	*s = '\0';
	return s;
}

char *atr_pget(thing, atr, owner, flags, alen)
dbref thing, *owner;
int atr, *flags, *alen;
{
	char *buff;

	buff = alloc_lbuf("atr_pget");
	return atr_pget_str(buff, thing, atr, owner, flags, alen);
}

int atr_pget_info(thing, atr, owner, flags)
dbref thing, *owner;
int atr, *flags;
{
	char *buff;
	dbref parent;
	int lev, alen;
	ATTR *ap;

	ITER_PARENTS(thing, parent, lev) {
		buff = atr_get_raw(parent, atr);
		if (buff && *buff) {
			atr_decode(buff, NULL, thing, owner, flags, atr, &alen);
			if ((lev == 0) || !(*flags & AF_PRIVATE))
				return 1;
		}
		if ((lev == 0) && Good_obj(Parent(parent))) {
			ap = atr_num(atr);
			if (!ap || ap->flags & AF_PRIVATE)
				break;
		}
	}
	*owner = Owner(thing);
	*flags = 0;
	return 0;
}


/* ---------------------------------------------------------------------------
 * atr_free: Return all attributes of an object.
 */

void atr_free(thing)
dbref thing;
{
	int attr;
	char *as;
	atr_push();
	for (attr = atr_head(thing, &as); attr; attr = atr_next(&as)) {
		atr_clr(thing, attr);
	}
	atr_pop();
	al_destroy(thing);	/* Just to be on the safe side */
}

/* ---------------------------------------------------------------------------
 * atr_cpy: Copy all attributes from one object to another.  Takes the
 * player argument to ensure that only attributes that COULD be set by
 * the player are copied.
 */

void atr_cpy(player, dest, source)
dbref player, dest, source;
{
	int attr, aflags, alen;
	dbref owner, aowner;
	char *as, *buf;
	ATTR *at;

	owner = Owner(dest);
        buf = alloc_lbuf("atr_cpy");
	atr_push();
	for (attr = atr_head(source, &as); attr; attr = atr_next(&as)) {
		buf = atr_get_str(buf, source, attr, &aowner, &aflags, &alen);
		if (!(aflags & AF_LOCK))
			aowner = owner;		/* change owner */
		at = atr_num(attr);
		if (attr && at) {
			if (Write_attr(owner, dest, at, aflags))
				/* Only set attrs that owner has perm to set */
				atr_add(dest, attr, buf, aowner, aflags);
		}
	}
	atr_pop();
	free_lbuf(buf);
}

/* ---------------------------------------------------------------------------
 * atr_chown: Change the ownership of the attributes of an object to the
 * current owner if they are not locked.
 */

void atr_chown(obj)
dbref obj;
{
	int attr, aflags, alen;
	dbref owner, aowner;
	char *as, *buf;

	owner = Owner(obj);
	buf = alloc_lbuf("atr_chown");
	atr_push();
	for (attr = atr_head(obj, &as); attr; attr = atr_next(&as)) {
		buf = atr_get_str(buf, obj, attr, &aowner, &aflags, &alen);
		if ((aowner != owner) && !(aflags & AF_LOCK))
			atr_add(obj, attr, buf, owner, aflags);
	}
	atr_pop();
	free_lbuf(buf);
}

/* ---------------------------------------------------------------------------
 * atr_next: Return next attribute in attribute list.
 */

int atr_next(attrp)
char **attrp;
{
	if (!*attrp || !**attrp) {
		return 0;
	} else {
		return al_decode(attrp);
	}
}

/* ---------------------------------------------------------------------------
 * atr_push, atr_pop: Push and pop attr lists.
 */

void NDECL(atr_push)
{
	ALIST *new_alist;

	new_alist = (ALIST *) alloc_sbuf("atr_push");
	new_alist->data = mudstate.iter_alist.data;
	new_alist->len = mudstate.iter_alist.len;
	new_alist->next = mudstate.iter_alist.next;

	mudstate.iter_alist.data = NULL;
	mudstate.iter_alist.len = 0;
	mudstate.iter_alist.next = new_alist;
	return;
}

void NDECL(atr_pop)
{
	ALIST *old_alist;
	char *cp;

	old_alist = mudstate.iter_alist.next;

	if (mudstate.iter_alist.data) {
		XFREE(mudstate.iter_alist.data, "al_extend");
	}
	if (old_alist) {
		mudstate.iter_alist.data = old_alist->data;
		mudstate.iter_alist.len = old_alist->len;
		mudstate.iter_alist.next = old_alist->next;
		cp = (char *)old_alist;
		free_sbuf(cp);
	} else {
		mudstate.iter_alist.data = NULL;
		mudstate.iter_alist.len = 0;
		mudstate.iter_alist.next = NULL;
	}
	return;
}

/* ---------------------------------------------------------------------------
 * atr_head: Returns the head of the attr list for object 'thing'
 */

int atr_head(thing, attrp)
dbref thing;
char **attrp;
{
	char *astr;
	int alen;

	/* Get attribute list.  Save a read if it is in the modify atr list */

	if (thing == mudstate.mod_al_id) {
		astr = mudstate.mod_alist;
	} else {
		astr = atr_get_raw(thing, A_LIST);
	}
	alen = al_size(astr);

	/* If no list, return nothing */

	if (!alen)
		return 0;

	/* Set up the list and return the first entry */

	al_extend(&mudstate.iter_alist.data, &mudstate.iter_alist.len,
		  alen, 0);
	memcpy(mudstate.iter_alist.data, astr, alen);
	*attrp = mudstate.iter_alist.data;
	return atr_next(attrp);
}


/* ---------------------------------------------------------------------------
 * db_grow: Extend the struct database.
 */

#define	SIZE_HACK	1	/* So mistaken refs to #-1 won't die. */

void initialize_objects(first, last)
dbref first, last;
{
	dbref thing;

	for (thing = first; thing < last; thing++) {
		s_Owner(thing, GOD);
		s_Flags(thing, (TYPE_GARBAGE | GOING));
		s_Powers(thing, 0);
		s_Powers2(thing, 0);
		s_Location(thing, NOTHING);
		s_Contents(thing, NOTHING);
		s_Exits(thing, NOTHING);
		s_Link(thing, NOTHING);
		s_Next(thing, NOTHING);
		s_Zone(thing, NOTHING);
		s_Parent(thing, NOTHING);
#ifdef MEMORY_BASED
		db[thing].attrtext.atrs = NULL;
		db[thing].attrtext.at_count = 0;
#endif
	}
}

void db_grow(newtop)
dbref newtop;
{
	int newsize, marksize, delta, i;
	MARKBUF *newmarkbuf;
	OBJ *newdb;
	NAME *newnames, *newpurenames;
	char *cp;

	if (!mudstate.standalone)
		delta = mudconf.init_size;
	else
		delta = 1000;

	/* Determine what to do based on requested size, current top and
	 * size.  Make sure we grow in reasonable-size chunks to prevent 
	 * frequent reallocations of the db array. 
	 */

	/* If requested size is smaller than the current db size, ignore it */

	if (newtop <= mudstate.db_top) {
		return;
	}
	/* If requested size is greater than the current db size but smaller
	 * than the amount of space we have allocated, raise the db size 
	 * and initialize the new area. 
	 */

	if (newtop <= mudstate.db_size) {
		for (i = mudstate.db_top; i < newtop; i++) {
			names[i] = NULL;
			purenames[i] = NULL;
		}
		initialize_objects(mudstate.db_top, newtop);
		mudstate.db_top = newtop;
		return;
	}
	/* Grow by a minimum of delta objects */

	if (newtop <= mudstate.db_size + delta) {
		newsize = mudstate.db_size + delta;
	} else {
		newsize = newtop;
	}

	/* Enforce minimum database size */

	if (newsize < mudstate.min_size)
		newsize = mudstate.min_size + delta;

	/* Grow the name tables */

	newnames = (NAME *) XCALLOC(newsize + SIZE_HACK, sizeof(NAME),
				    "db_grow.names");
	if (!newnames) {
	    fprintf(mainlog_fp,
		    "ABORT! db.c, could not allocate space for %d item name cache in db_grow().\n",
		    newsize);
	    abort();
	}

	if (names) {
		/* An old name cache exists.  Copy it. */

		names -= SIZE_HACK;
		memcpy((char *)newnames, (char *)names,
		       (newtop + SIZE_HACK) * sizeof(NAME));
		cp = (char *)names;
		XFREE(cp, "db_grow.name");
	} else {

		/* Creating a brand new struct database.  Fill in the
		 * 'reserved' area in case it gets referenced.  
		 */

		names = newnames;
		for (i = 0; i < SIZE_HACK; i++) {
			names[i] = NULL;
		}
	}
	names = newnames + SIZE_HACK;
	newnames = NULL;

	newpurenames = (NAME *) XCALLOC(newsize + SIZE_HACK,
					sizeof(NAME),
					"db_grow.purenames");

	if (!newpurenames) {
	    fprintf(mainlog_fp,
		    "ABORT! db.c, could not allocate space for %d item name cache in db_grow().\n", newsize);
	    abort();
	}
	memset((char *)newpurenames, 0, (newsize + SIZE_HACK) * sizeof(NAME));

	if (purenames) {

		/* An old name cache exists.  Copy it. */

		purenames -= SIZE_HACK;
		memcpy((char *)newpurenames, (char *)purenames,
		       (newtop + SIZE_HACK) * sizeof(NAME));
		cp = (char *)purenames;
		XFREE(cp, "db_grow.purename");
	} else {

		/* Creating a brand new struct database.  Fill in the
		 * 'reserved' area in case it gets referenced.  
		 */

		purenames = newpurenames;
		for (i = 0; i < SIZE_HACK; i++) {
			purenames[i] = NULL;
		}
	}
	purenames = newpurenames + SIZE_HACK;
	newpurenames = NULL;

	/* Grow the db array */

	newdb = (OBJ *) XCALLOC(newsize + SIZE_HACK, sizeof(OBJ),
				"db_grow.db");
	if (!newdb) {
	    STARTLOG(LOG_ALWAYS, "ALC", "DB")
		log_printf("Could not allocate space for %d item struct database.", newsize);
	    ENDLOG
	    abort();
	}
	if (db) {

		/* An old struct database exists.  Copy it to the new buffer */

		db -= SIZE_HACK;
		memcpy((char *)newdb, (char *)db,
		      (mudstate.db_top + SIZE_HACK) * sizeof(OBJ));
		cp = (char *)db;
		XFREE(cp, "db_grow.db");
	} else {

		/* Creating a brand new struct database.  Fill in the
		 * 'reserved' area in case it gets referenced. 
		 */

		db = newdb;
		for (i = 0; i < SIZE_HACK; i++) {
			s_Owner(i, GOD);
			s_Flags(i, (TYPE_GARBAGE | GOING));
			s_Flags2(i, 0);
			s_Flags3(i, 0);
			s_Powers(i, 0);
			s_Powers2(i, 0);
			s_Location(i, NOTHING);
			s_Contents(i, NOTHING);
			s_Exits(i, NOTHING);
			s_Link(i, NOTHING);
			s_Next(i, NOTHING);
			s_Zone(i, NOTHING);
			s_Parent(i, NOTHING);
		}
	}
	db = newdb + SIZE_HACK;
	newdb = NULL;

	/* Go do the rest of the things */

	CALL_ALL_MODULES(db_grow, (newsize, newtop));

	for (i = mudstate.db_top; i < newtop; i++) {
		names[i] = NULL;
		purenames[i] = NULL;
	}
	initialize_objects(mudstate.db_top, newtop);
	mudstate.db_top = newtop;
	mudstate.db_size = newsize;

	/* Grow the db mark buffer */

	marksize = (newsize + 7) >> 3;
	newmarkbuf = (MARKBUF *) XMALLOC(marksize, "db_grow");
	memset((char *)newmarkbuf, 0, marksize);
	if (mudstate.markbits) {
		marksize = (newtop + 7) >> 3;
		memcpy((char *)newmarkbuf, (char *)mudstate.markbits, marksize);
		cp = (char *)mudstate.markbits;
		XFREE(cp, "db_grow");
	}
	mudstate.markbits = newmarkbuf;
}

void NDECL(db_free)
{
	char *cp;

	if (db != NULL) {
		db -= SIZE_HACK;
		cp = (char *)db;
		XFREE(cp, "db_grow");
		db = NULL;
	}
	mudstate.db_top = 0;
	mudstate.db_size = 0;
	mudstate.freelist = NOTHING;
}

void NDECL(db_make_minimal)
{
	dbref obj;

	db_free();
	db_grow(1);

	s_Name(0, "Limbo");
	s_Flags(0, TYPE_ROOM);
	s_Flags2(0, 0);
	s_Flags3(0, 0);
	s_Powers(0, 0);
	s_Powers2(0, 0);
	s_Location(0, NOTHING);
	s_Exits(0, NOTHING);
	s_Link(0, NOTHING);
	s_Parent(0, NOTHING);
	s_Zone(0, NOTHING);
	s_Pennies(0, 1);
	s_Owner(0, 1);

	/* should be #1 */
	load_player_names();
	obj = create_player((char *)"Wizard", (char *)"potrzebie", NOTHING, 0, 1);
	s_Flags(obj, Flags(obj) | WIZARD);
	s_Flags2(obj, 0);
	s_Flags3(obj, 0);
	s_Powers(obj, 0);
	s_Powers2(obj, 0);
	s_Pennies(obj, 1000);

	/* Manually link to Limbo, just in case */

	s_Location(obj, 0);
	s_Next(obj, NOTHING);
	s_Contents(0, obj);
	s_Link(obj, 0);
}

dbref parse_dbref(s)
const char *s;
{
	const char *p;
	int x;

	/* Enforce completely numeric dbrefs */

	for (p = s; *p; p++) {
		if (!isdigit(*p))
			return NOTHING;
	}

	x = atoi(s);
	return ((x >= 0) ? x : NOTHING);
}

void putstring(f, s)
FILE *f;
const char *s;
{
	putc('"', f);
	
	while (s && *s) {
		switch (*s) {
		case '\\':
		case '"':
			putc('\\', f);
		default:
			putc(*s, f);
		}
		s++;
	}
	putc('"', f);
	putc('\n', f);
}

const char *getstring_noalloc(f, new_strings)
FILE *f;
int new_strings;
{
	static char buf[LBUF_SIZE];
	char *p;
	int c, lastc;

	p = buf;
	c = fgetc(f);
	if (!new_strings || (c != '"')) {
		ungetc(c, f);
		c = '\0';
		for (;;) {
			lastc = c;
			c = fgetc(f);

			/* If EOF or null, return */

			if (!c || (c == EOF)) {
				*p = '\0';
				return buf;
			}
			/* If a newline, return if prior char is not a cr.
			 * Otherwise keep on truckin' 
			 */

			if ((c == '\n') && (lastc != '\r')) {
				*p = '\0';
				return buf;
			}
			safe_chr(c, buf, &p);
		}
	} else {
		for (;;) {
			c = fgetc(f);
			if (c == '"') {
				if ((c = fgetc(f)) != '\n')
					ungetc(c, f);
				*p = '\0';
				return buf;
			} else if (c == '\\') {
				c = fgetc(f);
			}
			if ((c == '\0') || (c == EOF)) {
				*p = '\0';
				return buf;
			}
			safe_chr(c, buf, &p);
		}
	}
}

INLINE dbref getref(f)
FILE *f;
{
	static char buf[SBUF_SIZE];
	fgets(buf, sizeof(buf), f);
	return (atoi(buf));
}

INLINE long getlong(f)
FILE *f;
{
	static char buf[SBUF_SIZE];
	fgets(buf, sizeof(buf), f);
	return (atol(buf));
}

void free_boolexp(b)
BOOLEXP *b;
{
	if (b == TRUE_BOOLEXP)
		return;

	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_IS:
	case BOOLEXP_OWNER:
	case BOOLEXP_INDIR:
		free_boolexp(b->sub1);
		free_bool(b);
		break;
	case BOOLEXP_CONST:
		free_bool(b);
		break;
	case BOOLEXP_ATR:
	case BOOLEXP_EVAL:
		XFREE(b->sub1, "test_atr.sub1");
		free_bool(b);
		break;
	}
}

BOOLEXP *dup_bool(b)
BOOLEXP *b;
{
	BOOLEXP *r;

	if (b == TRUE_BOOLEXP)
		return (TRUE_BOOLEXP);

	r = alloc_bool("dup_bool");
	switch (r->type = b->type) {
	case BOOLEXP_AND:
	case BOOLEXP_OR:
		r->sub2 = dup_bool(b->sub2);
	case BOOLEXP_NOT:
	case BOOLEXP_CARRY:
	case BOOLEXP_IS:
	case BOOLEXP_OWNER:
	case BOOLEXP_INDIR:
		r->sub1 = dup_bool(b->sub1);
	case BOOLEXP_CONST:
		r->thing = b->thing;
		break;
	case BOOLEXP_EVAL:
	case BOOLEXP_ATR:
		r->thing = b->thing;
		r->sub1 = (BOOLEXP *) XSTRDUP((char *)b->sub1, "dup_bool.sub1");
		break;
	default:
		fprintf(mainlog_fp, "bad bool type!!\n");
		return (TRUE_BOOLEXP);
	}
	return (r);
}

int init_gdbm_db(gdbmfile)
char *gdbmfile;
{
	/* Calculate proper database block size */
	
	for (mudstate.db_block_size = 1; mudstate.db_block_size < (LBUF_SIZE * 4);
		mudstate.db_block_size = mudstate.db_block_size << 1) ;

	cache_init(mudconf.cache_width);
	dddb_setfile(gdbmfile);
	dddb_init();
	STARTLOG(LOG_ALWAYS, "INI", "LOAD")
		log_printf("Using gdbm file: %s", gdbmfile);
	ENDLOG
	db_free();
	return (0);
}

/* check_zone - checks back through a zone tree for control */

int check_zone(player, thing)
dbref player, thing;
{
	if (mudstate.standalone)
		return 0;

	if (!mudconf.have_zones || (Zone(thing) == NOTHING) ||
	    isPlayer(thing) ||
	    (mudstate.zone_nest_num + 1 == mudconf.zone_nest_lim)) {
		mudstate.zone_nest_num = 0;
		return 0;
	}

	/* We check Control_OK on the thing itself, not on its ZMO --
	 * that allows us to have things default into a zone without
	 * needing to be controlled by that ZMO.
	 */
	if (!Control_ok(thing)) {
		return 0;
	}
	
	mudstate.zone_nest_num++;

	/* If the zone doesn't have a ControlLock, DON'T allow control. */

	if (atr_get_raw(Zone(thing), A_LCONTROL) &&
	    could_doit(player, Zone(thing), A_LCONTROL)) {
		mudstate.zone_nest_num = 0;
		return 1;
	} else {
		return check_zone(player, Zone(thing));
	}

}

int check_zone_for_player(player, thing)
dbref player, thing;
{
	if (!Control_ok(Zone(thing))) {
		return 0;
	}

	mudstate.zone_nest_num++;

	if (!mudconf.have_zones || (Zone(thing) == NOTHING) || 
	    (mudstate.zone_nest_num == mudconf.zone_nest_lim) || !(isPlayer(thing))) {
	    	mudstate.zone_nest_num = 0;
		return 0;
	}

	if (atr_get_raw(Zone(thing), A_LCONTROL) && could_doit(player, Zone(thing), A_LCONTROL)) {
		mudstate.zone_nest_num = 0;
		return 1;
	} else {
		return check_zone(player, Zone(thing));
	}

}

/* ---------------------------------------------------------------------------
 * dump_restart_db: Writes out socket information.
 */

void dump_restart_db()
{
	FILE *f;
	DESC *d;
	int version = 0;

	/* We maintain a version number for the restart database,
	 * so we can restart even if the format of the restart db
	 * has been changed in the new executable. 
	 */
	   
#ifdef CONCENTRATE
	version |= RS_CONCENTRATE;
#endif
	version |= RS_RECORD_PLAYERS;
	version |= RS_NEW_STRINGS;
	version |= RS_COUNT_REBOOTS;
	
	f = fopen("restart.db", "w");
	fprintf(f, "+V%d\n", version);
	putref(f, sock);
	putlong(f, mudstate.start_time);
	putref(f, mudstate.reboot_nums);
	putstring(f, mudstate.doing_hdr);
#ifdef CONCENTRATE
	putref(f, conc_pid);
#endif
	putref(f, mudstate.record_players);
	DESC_ITER_ALL(d) {
		putref(f, d->descriptor);
		putref(f, d->flags);
		putlong(f, d->connected_at);
		putref(f, d->command_count);
		putref(f, d->timeout);
		putref(f, d->host_info);
		putref(f, d->player);
		putlong(f, d->last_time);
		putstring(f, d->output_prefix);
		putstring(f, d->output_suffix);
		putstring(f, d->addr);
		putstring(f, d->doing);
		putstring(f, d->username);
#ifdef CONCENTRATE
		putref(f, d->concid);
		putref(f, d->cstatus);
#endif
	}
	putref(f, 0);

	fclose(f);
}

void load_restart_db()
{
	FILE *f;
	DESC *d;
	DESC *p;

	int val, version, new_strings = 0;
	char *temp, buf[8];
	struct stat fstatbuf;

	f = fopen("restart.db", "r");
	if (!f) {
		mudstate.restarting = 0;
		return;
	}
	mudstate.restarting = 1;

	fgets(buf, 3, f);
	if (strncmp(buf, "+V", 2)) {
		abort();
	}
	version = getref(f);
	sock = getref(f);

	if (version & RS_NEW_STRINGS)
		new_strings = 1;
		
	maxd = sock + 1;
	mudstate.start_time = (time_t) getlong(f);

	if (version & RS_COUNT_REBOOTS)
	    mudstate.reboot_nums = getref(f) + 1;

	strcpy(mudstate.doing_hdr, getstring_noalloc(f, new_strings));

	if (version & RS_CONCENTRATE) {
#ifdef CONCENTRATE
		conc_pid = getref(f);
#else
		(void)getref(f);
#endif
	}
	
	if (version & RS_RECORD_PLAYERS) {
		mudstate.record_players = getref(f);
	}
	
	while ((val = getref(f)) != 0) {
		ndescriptors++;
		d = alloc_desc("restart");
		d->descriptor = val;
		d->flags = getref(f);
		d->connected_at = (time_t) getlong(f);
		d->retries_left = mudconf.retry_limit;
		d->command_count = getref(f);
		d->timeout = getref(f);
		d->host_info = getref(f);
		d->player = getref(f);
		d->last_time = (time_t) getlong(f);
		temp = (char *)getstring_noalloc(f, new_strings);
		if (*temp) {
			d->output_prefix = alloc_lbuf("set_userstring");
			strcpy(d->output_prefix, temp);
		} else {
			d->output_prefix = NULL;
		}
		temp = (char *)getstring_noalloc(f, new_strings);
		if (*temp) {
			d->output_suffix = alloc_lbuf("set_userstring");
			strcpy(d->output_suffix, temp);
		} else {
			d->output_suffix = NULL;
		}

		strcpy(d->addr, getstring_noalloc(f, new_strings));
		strcpy(d->doing, getstring_noalloc(f, new_strings));
		strcpy(d->username, getstring_noalloc(f, new_strings));

		if (version & RS_CONCENTRATE) {
#ifdef CONCENTRATE
			d->concid = getref(f);
			d->cstatus = getref(f);
#else
			(void)getref(f);
			(void)getref(f);
#endif
		}
		d->output_size = 0;
		d->output_tot = 0;
		d->output_lost = 0;
		d->output_head = NULL;
		d->output_tail = NULL;
		d->input_head = NULL;
		d->input_tail = NULL;
		d->input_size = 0;
		d->input_tot = 0;
		d->input_lost = 0;
		d->raw_input = NULL;
		d->raw_input_at = NULL;
		d->quota = mudconf.cmd_quota_max;
		d->program_data = NULL;
		d->hashnext = NULL;
		/* Note that d->address is NOT INITIALIZED, and it DOES
		 * get used later, particularly when checking logout.
		 */

		if (descriptor_list) {
			for (p = descriptor_list; p->next; p = p->next) ;
			d->prev = &p->next;
			p->next = d;
			d->next = NULL;
		} else {
			d->next = descriptor_list;
			d->prev = &descriptor_list;
			descriptor_list = d;
		}

		if (d->descriptor >= maxd)
			maxd = d->descriptor + 1;
		desc_addhash(d);
#ifdef CONCENTRATE
		if (!(d->cstatus & C_CCONTROL))
#endif
			if (isPlayer(d->player))
				s_Flags2(d->player, Flags2(d->player) | CONNECTED);
	}

	/* In case we've had anything bizarre happen... */

	DESC_ITER_ALL(d) {
	    if (fstat(d->descriptor, &fstatbuf) < 0) {
		STARTLOG(LOG_PROBLEMS, "ERR", "RESTART")
		    log_printf("Bad descriptor %d", d->descriptor);
		ENDLOG
		shutdownsock(d, R_SOCKDIED);
	    }
	}

	DESC_ITER_CONN(d) {
		if (!isPlayer(d->player)) {
			shutdownsock(d, R_QUIT);
		}
#ifdef CONCENTRATE
		if (d->cstatus & C_REMOTE) {
			DESC *k;
			DESC_ITER_ALL(k) {
				if (k->descriptor = d->descriptor)
					d->parent = k;
			}
		}
#endif

	}

	fclose(f);
	remove("restart.db");
}