/* db.c */

#include "copyright.h"

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/file.h>

#include <sys/types.h>

#define __DB_C
#include "mudconf.h"
#include "config.h"
#include "externs.h"
#include "db.h"

#ifdef __GNUC__
#define INLINE inline
#else
#define INLINE
#endif /* __GNUC__ */

DB *dbp;
struct object *db = NULL;

#ifdef TEST_MALLOC
int malloc_count = 0;
#endif				/* TEST_MALLOC */

/* #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. */
extern int ck_fwdlist(int key, dbref player, dbref thing, int anum,
	char *atext);

/*
 * If you would like to add some attributes of your own please write to me
 * for available attribute #'s. This way some degree of compatibility can be
 * maintained between different variation of TinyMUSH Try:
 * larry@pleiades.ecs.umass.edu larry@belch.berkeley.edu
 * larry@sigh.berkeley.edu larry@roberts.wpi.edu or shiva on the MUDS
 */

/* list of attributes */
ATTR attr[] = {
{"Aahear",	A_AAHEAR,	AF_ODARK,			NULL},
{"Aclone",	A_ACLONE,	AF_ODARK,			NULL},
{"Aconnect",	A_ACONNECT,	AF_ODARK,			NULL},
{"Adesc",	A_ADESC,	AF_ODARK,			NULL},
{"Adisconnect",	A_ADISCONNECT,	AF_ODARK,			NULL},
{"Adrop",	A_ADROP,	AF_ODARK,			NULL},
{"Aefail",	A_AEFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Aenter",	A_AENTER,	AF_ODARK,			NULL},
{"Afail",	A_AFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Ahear",	A_AHEAR,	AF_ODARK,			NULL},
{"Akill",	A_AKILL,	AF_ODARK,			NULL},
{"Aleave",	A_ALEAVE,	AF_ODARK,			NULL},
{"Alfail",	A_ALFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Alias",	A_ALIAS,	AF_NOPROG|AF_NOCMD|AF_GOD,	NULL},
{"Allowance",	A_ALLOWANCE,	AF_MDARK|AF_NOPROG|AF_WIZARD,	NULL},
{"Amhear",	A_AMHEAR,	AF_ODARK,			NULL},
{"Amove",	A_AMOVE,	AF_ODARK,			NULL},
{"Apay",	A_APAY,		AF_ODARK,			NULL},
{"Asucc",	A_ASUCC,	AF_ODARK,			NULL},
{"Atport",	A_ATPORT,	AF_ODARK|AF_NOPROG,		NULL},
{"Aufail",	A_AUFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Ause",	A_AUSE,		AF_ODARK,			NULL},
{"Away",	A_AWAY,		AF_ODARK|AF_NOPROG,		NULL},
{"Charges",	A_CHARGES,	AF_ODARK|AF_NOPROG,		NULL},
{"Comment",	A_COMMENT,	AF_MDARK|AF_WIZARD,		NULL},
{"Cost",	A_COST,		AF_ODARK,			NULL},
{"Desc",	A_DESC,		AF_NOPROG,			NULL},
{"DefaultLock",	A_LOCK,		AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Drop",	A_DROP,		AF_ODARK|AF_NOPROG,		NULL},
{"DropLock",	A_LDROP,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Ealias",	A_EALIAS,	AF_ODARK|AF_NOPROG,		NULL},
{"Efail",	A_EFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Enter",	A_ENTER,	AF_ODARK,			NULL},
{"EnterLock",	A_LENTER,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Fail",	A_FAIL,		AF_ODARK|AF_NOPROG,		NULL},
{"Filter",	A_FILTER,	AF_ODARK|AF_NOPROG,		NULL},
{"Forwardlist",	A_FORWARDLIST,	AF_ODARK|AF_NOPROG,		ck_fwdlist},
{"GiveLock",	A_LGIVE,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Idesc",	A_IDESC,	AF_ODARK|AF_NOPROG,		NULL},
{"Idle",	A_IDLE,		AF_ODARK|AF_NOPROG,		NULL},
{"Infilter",	A_INFILTER,	AF_ODARK|AF_NOPROG,		NULL},
{"Inprefix",	A_INPREFIX,	AF_ODARK|AF_NOPROG,		NULL},
{"Kill",	A_KILL,		AF_ODARK,			NULL},
{"Lalias",	A_LALIAS,	AF_ODARK|AF_NOPROG,		NULL},
{"Last",	A_LAST,		AF_WIZARD|AF_NOCMD|AF_NOPROG,	NULL},
{"Lastsite",	A_LASTSITE,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_GOD,
								NULL},
{"Leave",	A_LEAVE,	AF_ODARK,			NULL},
{"LeaveLock",	A_LLEAVE,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Lfail",	A_LFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"LinkLock",	A_LLINK,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Listen",	A_LISTEN,	AF_ODARK,			NULL},
{"Logindata",	A_LOGINDATA,	AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{"Move",	A_MOVE,		AF_ODARK,			NULL},
#ifdef ATR_NAME
{"Name",	A_NAME,		AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
#endif
{"Odesc",	A_ODESC,	AF_ODARK|AF_NOPROG,		NULL},
{"Odrop",	A_ODROP,	AF_ODARK|AF_NOPROG,		NULL},
{"Oefail",	A_OEFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Oenter",	A_OENTER,	AF_ODARK,			NULL},
{"Ofail",	A_OFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Okill",	A_OKILL,	AF_ODARK,			NULL},
{"Oleave",	A_OLEAVE,	AF_ODARK,			NULL},
{"Olfail",	A_OLFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Omove",	A_OMOVE,	AF_ODARK,			NULL},
{"Opay",	A_OPAY,		AF_ODARK,			NULL},
{"Osucc",	A_OSUCC,	AF_ODARK|AF_NOPROG,		NULL},
{"Otport",	A_OTPORT,	AF_ODARK|AF_NOPROG,		NULL},
{"Oufail",	A_OUFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Ouse",	A_OUSE,		AF_ODARK,			NULL},
{"Oxenter",	A_OXENTER,	AF_ODARK,			NULL},
{"Oxleave",	A_OXLEAVE,	AF_ODARK,			NULL},
{"Oxtport",	A_OXTPORT,	AF_ODARK|AF_NOPROG,		NULL},
{"PageLock",	A_LPAGE,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Pay",		A_PAY,		AF_ODARK,			NULL},
{"Prefix",	A_PREFIX,	AF_ODARK|AF_NOPROG,		NULL},
{"Queue",	A_QUEUE,	AF_ODARK|AF_NOPROG|AF_WIZARD|AF_NOCMD,	NULL},
{"Quota",	A_QUOTA,	AF_DARK|AF_NOPROG|AF_WIZARD|AF_NOCMD,	NULL},
{"ReceiveLock",	A_LRECEIVE,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Reject",	A_REJECT,	AF_ODARK|AF_NOPROG,		NULL},
{"Rquota",	A_RQUOTA,	AF_DARK|AF_NOPROG|AF_WIZARD|AF_NOCMD,	NULL},
{"Runout",	A_RUNOUT,	AF_ODARK,			NULL},
{"Semaphore",	A_SEMAPHORE,	AF_ODARK|AF_NOPROG|AF_WIZARD|AF_NOCMD,	NULL},
{"Sex",		A_SEX,		AF_NOPROG,			NULL},
{"Startup",	A_STARTUP,	AF_ODARK,			NULL},
{"Succ",	A_SUCC,		AF_ODARK|AF_NOPROG,		NULL},
{"TeloutLock",	A_LTELOUT,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Timeout",	A_TIMEOUT,	AF_MDARK|AF_NOPROG|AF_WIZARD,	NULL},
{"Tport",	A_TPORT,	AF_ODARK|AF_NOPROG,		NULL},
{"TportLock",	A_LTPORT,	AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"Ufail",	A_UFAIL,	AF_ODARK|AF_NOPROG,		NULL},
{"Use",		A_USE,		AF_ODARK,			NULL},
{"UseLock",	A_LUSE,		AF_ODARK|AF_NOPROG|AF_NOCMD|AF_IS_LOCK,
								NULL},
{"VA",		A_VA,		AF_ODARK,			NULL},
{"VB",		A_VA+1,		AF_ODARK,			NULL},
{"VC",		A_VA+2,		AF_ODARK,			NULL},
{"VD",		A_VA+3,		AF_ODARK,			NULL},
{"VE",		A_VA+4,		AF_ODARK,			NULL},
{"VF",		A_VA+5,		AF_ODARK,			NULL},
{"VG",		A_VA+6,		AF_ODARK,			NULL},
{"VH",		A_VA+7,		AF_ODARK,			NULL},
{"VI",		A_VA+8,		AF_ODARK,			NULL},
{"VJ",		A_VA+9,		AF_ODARK,			NULL},
{"VK",		A_VA+10,	AF_ODARK,			NULL},
{"VL",		A_VA+11,	AF_ODARK,			NULL},
{"VM",		A_VA+12,	AF_ODARK,			NULL},
{"VN",		A_VA+13,	AF_ODARK,			NULL},
{"VO",		A_VA+14,	AF_ODARK,			NULL},
{"VP",		A_VA+15,	AF_ODARK,			NULL},
{"VQ",		A_VA+16,	AF_ODARK,			NULL},
{"VR",		A_VA+17,	AF_ODARK,			NULL},
{"VS",		A_VA+18,	AF_ODARK,			NULL},
{"VT",		A_VA+19,	AF_ODARK,			NULL},
{"VU",		A_VA+20,	AF_ODARK,			NULL},
{"VV",		A_VA+21,	AF_ODARK,			NULL},
{"VW",		A_VA+22,	AF_ODARK,			NULL},
{"VX",		A_VA+23,	AF_ODARK,			NULL},
{"VY",		A_VA+24,	AF_ODARK,			NULL},
{"VZ",		A_VA+25,	AF_ODARK,			NULL},
{"Xyxxy",	A_PASS,		AF_DARK|AF_NOPROG|AF_NOCMD|AF_INTERNAL,
								NULL},
{NULL,		0,		0,				NULL}};

#define DB_GET(x)	return (db[thing].x)
#define DB_SET(x,new)	db[thing].x = (new)

INLINE void s_Location(dbref thing, dbref new)	{ DB_SET(location, new); }
INLINE void s_Zone(dbref thing, dbref new)	{ }
INLINE void s_Contents(dbref thing, dbref new)	{ DB_SET(contents, new); }
INLINE void s_Exits(dbref thing, dbref new)	{ DB_SET(exits, new); }
INLINE void s_Next(dbref thing, dbref new)	{ DB_SET(next, new); }
INLINE void s_Link(dbref thing, dbref new)	{ DB_SET(link, new); }
INLINE void s_Owner(dbref thing, dbref new)	{ DB_SET(owner, new); }
INLINE void s_Parent(dbref thing, dbref new)	{ DB_SET(parent, new); }
INLINE void s_Pennies(dbref thing, int p)	{ DB_SET(penn, p); }
INLINE void s_Flags(dbref thing, FLAG f)	{ DB_SET(flags, f); }

INLINE dbref Location(dbref thing)		{ DB_GET(location); }
INLINE dbref Zone(dbref thing)			{ return NOTHING; }
INLINE dbref Contents(dbref thing)		{ DB_GET(contents); }
INLINE dbref Exits(dbref thing)			{ DB_GET(exits); }
INLINE dbref Next(dbref thing)			{ DB_GET(next); }
INLINE dbref Link(dbref thing)			{ DB_GET(link); }
INLINE dbref Owner(dbref thing)			{ DB_GET(owner); }
INLINE dbref Parent(dbref thing)		{ DB_GET(parent); }
INLINE int Pennies(dbref thing)			{ DB_GET(penn); }
INLINE FLAG Flags(dbref thing)			{ DB_GET(flags); }

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

int ck_fwdlist(int key, dbref player, dbref thing, int anum, char *atext)
{
#ifndef STANDALONE
dbref	target;
char	*tp, *bp, *dp;

	if (atext && *atext) {
		tp = bp = alloc_lbuf("ck_fwdlist");
		strcpy(tp, atext);
		do {
			dp = parse_to(&bp, ' ', 0);
			if ((*dp++ == '#') && isdigit(*dp)) {
				target = atoi(dp);
				if (!Good_obj(target) ||
				    (!controls(player, target) &&
				     !((Flags(target) & LINK_OK) &&
				       (could_doit(player, target, A_LLINK))))) {
					notify(player,
						tprintf("Cannot forward to #%d: Permission denied.",
							target));
					free_lbuf(tp);
					return 0;
				}
			}
		} while (bp);
		free_lbuf(tp);
	}
#endif
	return 1;
}


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

INLINE char *Name(dbref thing)
{
dbref	aowner;
int	aflags;

#ifndef ATR_NAME
	return (db[thing].name);
#else
static char temp[LBUF_SIZE];

	return (atr_get_str(temp, thing, A_NAME, &aowner, &aflags));
#endif				/* ATR_NAME */
}

INLINE void s_Name(dbref thing, const char *s)
{
#ifndef ATR_NAME
	set_string(&db[thing].name, s);
#else
	atr_add_raw(thing, A_NAME, (char *)s);
#endif				/* ATR_NAME */
}

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

#ifndef STANDALONE

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

extern NAMETAB attraccess_nametab[];

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

	/* Look up the user-named attribute we want to play with */

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

	va = (VATTR *)hashfind(buff, &mudstate.vattr_name_htab);
	if (!va) {
		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=ToLower(*sp);
		sp = strtok(value, " ");
		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 != -1) {
				success = 1;
				if (negate)
					va->flags &= ~f;
				else
					va->flags |= f;
			} else {
				notify(player,
					tprintf("Unknown permission: %s.", sp));
			}

			/* Get the next token */

			sp = strtok(NULL, " ");
		}
		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.");
			return;
		}

		/* Get rid of the old entry in the name table */

		for (p=buff,q=va->name; *q&&((p-buff)<(SBUF_SIZE-1)); p++,q++)
			*p=ToLower(*q);
		*p = '\0';
		hashdelete(buff, &mudstate.vattr_name_htab);

		/* Add the new entry to the name table */

		for (p=buff,q=value; *q&&((p-buff)<(SBUF_SIZE-1)); p++,q++)
			*p=ToLower(*q);
		*p = '\0';
		hashadd(buff, (int *)va, &mudstate.vattr_name_htab);

		/* Replace the name in the entry itself */

		XFREE (va->name, "do_attribute.rename");
		va->name = strsave(value);
		if (strlen(va->name) >= SBUF_SIZE)
			va->name[SBUF_SIZE-1] = '\0';
		notify(player, "Attribute renamed.");
		break;

	case ATTRIB_DELETE:

		/* Remove the attribute */

		hashdelete (buff, &mudstate.vattr_name_htab);
		nhashdelete (va->number, &mudstate.vattr_num_htab);
		free_vattr (va->number);
		XFREE (va->name, "do_attribute.delete");
		va->name = NULL;
		va->flags = AF_DELETED;
		notify(player, "Attribute deleted.");
		break;
	}
	free_sbuf(buff);
	return;
}

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

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

	init_match(player, arg1, NOTYPE);
	match_everything();
	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();
		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_text((char *)" renamed to ");
				log_text(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 init_attrtab()
{
ATTR	*a;
char	*buff, *p, *q;

	nhashinit(&mudstate.attr_num_htab, 47);
	hashinit(&mudstate.attr_name_htab, 47);
	nhashinit(&mudstate.vattr_num_htab, 1009);
	hashinit(&mudstate.vattr_name_htab, 1009);
	buff = alloc_sbuf("init_attrtab");
	for (a=attr; a->number; a++) {
		nhashadd(a->number, (int *)a, &mudstate.attr_num_htab);
		for (p=buff,q=(char *)a->name; *q; p++,q++) *p=ToLower(*q);
		*p = '\0';
		hashadd(buff, (int *)a, &mudstate.attr_name_htab);
	}
	free_sbuf(buff);
}

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

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

	/* Convert the buffer name to lowercase */

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

	/* Look for a predefined attribute */

	a = (ATTR *)hashfind(buff, &mudstate.attr_name_htab);
	if (a != NULL) {
		free_sbuf(buff);
		return a;
	}

	/* Nope, look for a user attribute */

	va = (VATTR *)hashfind(buff, &mudstate.vattr_name_htab);
	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;
	}

	/* All failed, return NULL */

	return NULL;
}

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

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

	/* Look for a predefined attribute */

	a = (ATTR *)nhashfind(anum, &mudstate.attr_num_htab);
	if (a != NULL) return a;

	/* Nope, look for a user-defined attribute */

	va = (VATTR *)nhashfind(anum, &mudstate.vattr_num_htab);
	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;
}

#else				/* STANDALONE */

/* We don't have the hash tables, do it by hand */

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

ATTR *atr_str(char *s)
{
ATTR	*ap;
VATTR	*va;
static ATTR tattr;

	/* Check for an exact match on a predefined attribute */

	for (ap=attr; ap->name; ap++) {
		if (!string_compare(ap->name, s)) return ap;
	}

	/* Check for an exact match on a user-named attribute */

	for (va=mudstate.user_attrs; va; va=va->next) {
		if (string_compare(va->name, s)) continue;

		/* Got it */

		tattr.name = va->name;
		tattr.number = va->number;
		tattr.flags = AF_ODARK;
		tattr.check = NULL;
		return &tattr;
	}

	/* No exact match, try for a prefix match on predefined attribs.
	 * Check for both longer versions and shorter versions.
	 */

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

	return NULL;
}

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

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

	for (ap=attr; ap->name; ap++)
		if (ap->number == anum) return ap;
	for (va=mudstate.user_attrs; va; va=va->next)
		if (va->number == anum) {
		tattr.name = va->name;
		tattr.number = va->number;
		tattr.flags = AF_ODARK;
		tattr.check = NULL;
		return &tattr;
	}

	return NULL;
}
#endif				/* STANDALONE */

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

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

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

		/* Unknown attr, create a new one */

		va = alloc_vattr(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 (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;
	}		
	return 0;
}

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

static void al_code (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(dbref thing)
{
char	*s, *as, c;
int	attr, aflags;
dbref	aowner;
ATTR	*ap;

	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(thing, attr, &aowner, &aflags);
		c = *s;
		free_lbuf(s);
		if ((c == '$') && !(aflags & AF_NOPROG)) {
			atr_pop();
			return 1;
		}
	}
	atr_pop();
	return 0;
}

#ifdef ATR_NAME
char *set_string(char **ptr, char *new)
{
  /* if pointer not null unalloc it */
  if (*ptr)
    XFREE(*ptr, "set_string");
  if (!new)
    return (*ptr = NULL);	/* Check with GAC about this */
  /* if (!new || !*new) return(*ptr=NULL); */
  *ptr = (char *) XMALLOC(strlen(new) + 1, "set_string");
  strcpy(*ptr, new);
  return (*ptr);
}
#endif				/* ATR_NAME */

/* 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 (char **buffer, int *bufsiz, int len, int copy)
{
char	*tbuff;

	if (len > *bufsiz) {
		*bufsiz = len + ATR_BUF_CHUNK;
		tbuff = XMALLOC(*bufsiz, "al_extend");
		if (*buffer) {
			if (copy) bcopy(*buffer, tbuff, len);
			XFREE(*buffer, "al_extend");
		}
		*buffer = tbuff;
	}
}

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

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

/* al_store: Write modified attribute list */

void al_store (void)
{
	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 (dbref thing)
{
char	*astr;
int	len;

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

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

	/* Save old list, then 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);
		bcopy(astr, mudstate.mod_alist, 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 (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 (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 (dbref thing, dbref atr, char *buff)
{
char	*bp;
int	i;

	bp = buff;
	for (i=0; thing && i<6; i++) {
		*bp++ = (thing & 0x3f) + '0';
		thing = thing >> 6;
	}
	*bp++ = '-';
	for (i=0; atr && i<6; i++) {
		*bp++ = (atr & 0x3f) + '0';
		atr = atr >> 6;
	}
	*bp = '\0';
}

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

void atr_clr(dbref thing, int atr)
{
DBT	key;
char	okey[KEY_SIZE];

	makekey(thing, atr, okey);
	key.data = okey;
	key.size = strlen(okey) + 1;
	DELETE(key);
	al_delete(thing, atr);
}

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

void al_destroy (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 (char *iattr, dbref thing, dbref owner, int flags)
{
	/* Compress the sttribute string */

	iattr = (char *) compress(iattr);

	/* 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 (char *iattr, char *oattr, dbref thing,
	dbref *owner, int *flags)
{
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)
				uncompress_str(oattr, iattr);
			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)
				uncompress_str(oattr, iattr);
		}

		/* Get the attribute text */

		if (oattr)
			uncompress_str(oattr, cp);

		if (*owner == NOTHING) *owner = Owner(thing);
	} else {

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

		*owner = Owner(thing);
		*flags = 0;
		if (oattr)
			uncompress_str(oattr, iattr);
	}
}

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

void atr_add_raw(dbref thing, int atr, char *buff)
{
DBT	key, con;
char	okey[KEY_SIZE];

	if (!buff || !*buff) {
		atr_clr(thing, atr);
		return;
	}
	makekey(thing, atr, okey);
	key.data = okey;
	key.size = strlen(okey) + 1;
	con.data = buff;
	con.size = strlen(buff) + 1;
	STORE(key, con);
	al_add(thing, atr);
	if (atr == A_STARTUP)	/* Set STARTUP flag */
		s_Flags(thing, Flags(thing) | STARTUP);
}

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

	if (!buff || !*buff) {
		atr_clr(thing, atr);
		if (atr == A_STARTUP)	/* Reset STARTUP flag */
			s_Flags(thing, Flags(thing) & ~STARTUP);
	} else {
		tbuff = atr_encode(buff, thing, owner, flags);
		atr_add_raw(thing, atr, tbuff);
	}
}

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

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

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

	buff = atr_get(thing, atr, &aowner, &aflags);
	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(dbref thing, int atr)
{
DBT	key, con;
char	okey[KEY_SIZE];

	makekey(thing, atr, okey);
	key.data = okey;
	key.size = strlen(okey) + 1;
	if (!FETCH(key, con)) {
		return con.data;
	} else {
		return NULL;
	}
}

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

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

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

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

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

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

#ifndef STANDALONE

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

	buff = atr_get_raw(thing, atr);
	if (!buff) {
		for (lev=0, parent=Parent(thing);
		     (Good_obj(parent) &&
		      (lev < mudconf.parent_nest_lim) && !buff);
		      parent=Parent(parent), lev++) {
			buff = atr_get_raw(parent, atr);
		}
	}
	if (!buff) {
		*owner = Owner(thing);
		*flags = 0;
		*s = '\0';
	} else {
		atr_decode(buff, s, thing, owner, flags);
	}
	return s;
}

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

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

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

	buff = atr_get_raw(thing, atr);
	if (!buff) {
		for (lev=0, parent=Parent(thing);
		     (Good_obj(parent) &&
		      (lev < mudconf.parent_nest_lim) && !buff);
		      parent=Parent(parent), lev++) {
			buff = atr_get_raw(parent, atr);
		}
	}
	if (!buff) {
		*owner = Owner(thing);
		*flags = 0;
		return 0;
	}
	atr_decode(buff, NULL, thing, owner, flags);
	return 1;
}

#endif STANDALONE

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

void atr_free(dbref thing)
{
int	attr;
char	*as;

	for (attr=atr_head(thing,&as); attr; attr=atr_next(&as)) {
		atr_clr(thing, attr);
	}
	al_destroy(thing);	/* Just to be on the safe side */
}

/* garbage collect an attribute list */

void atr_collect(dbref thing)
{
  /* Nada.  gdbm takes care of us.  I hope ;-) */
}

/* ---------------------------------------------------------------------------
 * atr_cpy: Copy all attributes from one object to another.
 */

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

	owner = Owner(dest);
	for (attr=atr_head(source,&as); attr; attr=atr_next(&as)) {
		buf = atr_get(source, attr, &aowner, &aflags);
		if (!(aflags & AF_LOCK)) aowner = owner;	/* chg owner */
		atr_add(dest, attr, buf, aowner, aflags);
		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(dbref obj)
{
int	attr, aflags;
dbref	owner, aowner;
char	*as, *buf;

	owner = Owner(obj);
	for (attr=atr_head(obj,&as); attr; attr=atr_next(&as)) {
		buf = atr_get(obj, attr, &aowner, &aflags);
		if ((aowner != owner) && !(aflags & AF_LOCK))
			atr_add(obj, attr, buf, owner, aflags);
		free_lbuf(buf);
	}
}

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

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

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

void atr_push(void)
{
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 atr_pop(void)
{
ALIST	*old_alist;
char	*cp;

	old_alist = mudstate.iter_alist.next;

	if (mudstate.iter_alist.data) {
		free(mudstate.iter_alist.data);
	}

	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(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);
	bcopy(astr, mudstate.iter_alist.data, alen);
	*attrp = mudstate.iter_alist.data;
	return atr_next(attrp);
}

/* ---------------------------------------------------------------------------
 * define_vattr: Define a new user-named attribute.
 */

VATTR *define_vattr (char *aname, int anum, int aflags)
{
VATTR	*va;
char	*p, *q, *buf;
#ifdef STANDALONE
char	*s1, *n1;
#endif

	if (!ok_attr_name(aname))
		return NULL;

	buf = alloc_sbuf("define_vattr");
	for (p=buf,q=aname; *q&&(((p-buf))<(SBUF_SIZE-1)); p++,q++)
		*p=ToLower(*q);
	*p = '\0';

	/* If already in the hash table, disregard */

#ifndef STANDALONE
	if (hashfind(buf, &mudstate.vattr_name_htab)) {
		free_sbuf(buf);
		return NULL;
	}
#else
	for (va=mudstate.user_attrs; va; va=va->next) {
		for (s1=buf,n1=va->name; *s1&&*n1; s1++,n1++) {
			if (ToLower(*s1) != ToLower(*n1)) break;
		}
		if (*s1 || *n1) continue;
		free_sbuf(buf);
		return NULL;
	}
#endif

	/* Set up a vattr structure and link it in */

	va = (VATTR *)XMALLOC(sizeof(VATTR), "define_vattr");
	va->name = strsave(aname);
	va->number = anum;
	va->flags = aflags;
	va->refcount = 0;
	if (strlen(va->name) >= SBUF_SIZE)
		va->name[SBUF_SIZE-1] = '\0';
	va->next = mudstate.user_attrs;
	mudstate.user_attrs = va;
#ifndef STANDALONE
	hashadd(buf, (int *)va, &mudstate.vattr_name_htab);
	nhashadd(anum, (int *)va, &mudstate.vattr_num_htab);
#endif
	free_sbuf(buf);
	return va;
}

/* ---------------------------------------------------------------------------
 * alloc_vattr: Allocate a new user-defined attribute.
 */

VATTR *alloc_vattr (char *aname, int aflags)
{
VATTR	*va;
int	anum;

	/* Make sure the attribute name is valid, reject it if not. */

	if (!ok_attr_name(aname))
		return NULL;

	/* Get attr numbers until we get one such that the low-order 7 bits are
	 * not zero.  If we ever had such an attribute it would truncate the
	 * attribute list at that point (because strlen would see it as the
	 * end-of-string marker (7 zero bits + a zero 'more data' bit)
	 */

	do {
		if (mudstate.user_attr_free == NULL) {
			anum = mudstate.attr_next++;
		} else {
			va = mudstate.user_attr_free;
			mudstate.user_attr_free = va->next;
			anum = va->number;
			XFREE(va, "alloc_vattr");
		}
	} while (!(anum & 0x7f));
	va = define_vattr(aname, anum, aflags);
	if (!va)
		free_vattr(anum);
	return va;
}

/* ---------------------------------------------------------------------------
 * free_vattr: Add an attribute number to the attr freelist.
 */

void free_vattr (int anum)
{
VATTR	*va;

	if (anum == (mudstate.attr_next - 1)) {
		mudstate.attr_next--;
	} else {
		va = (VATTR *)XMALLOC(sizeof(VATTR), "free_vattr");
		va->name = NULL;
		va->number = anum;
		va->next = mudstate.user_attr_free;
		mudstate.user_attr_free = va;
		va->refcount = 0;
	}
}

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

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

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

#ifndef STANDALONE
	delta = mudconf.init_size;
#else
	delta = 1000;
#endif

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

	if (newtop <= mudstate.db_top) {
		return;
	} else if (newtop <= mudstate.db_size) {
		for (i=mudstate.db_top; i<newtop; i++)
			s_Flags(i, TYPE_THING|GOING);
		mudstate.db_top = newtop;
		return;
	} else if (newtop <= mudstate.db_size + delta) {
		newsize = mudstate.db_size + delta;
	} else {
		newsize = newtop;
	}
	if (newsize < mudstate.min_size)
		newsize = mudstate.min_size + delta;;

	/* Allocate space for the new db array */

	newdb = (OBJ *)XMALLOC((newsize + SIZE_HACK) * sizeof(OBJ), "db_grow");
	if (!newdb) {

		LOG_SIMPLE(LOG_ALWAYS, "ALC", "DB",
			tprintf("Could not allocate space for %d item struct database.",
				newsize));
		abort();
	}

	if (db) {

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

		db -= SIZE_HACK;
		memcpy(newdb, db, (mudstate.db_top + SIZE_HACK) * sizeof(OBJ));
		cp = (char *)db;
		XFREE(cp, "db_grow");
	} 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_THING | GOING));
			s_Location(i, NOTHING);
			s_Contents(i, NOTHING);
			s_Exits(i, NOTHING);
			s_Link(i, NOTHING);
			s_Next(i, NOTHING);
			s_Pennies(i, NOTHING);
			s_Zone(i, NOTHING);
			s_Parent(i, NOTHING);
			s_Pennies(i, 0);
		}
	}
	db = newdb + SIZE_HACK;
	newdb = NULL;
	for (i=mudstate.db_top; i<newtop; i++)
		s_Flags(i, TYPE_THING|GOING);
	mudstate.db_top = newtop;
	mudstate.db_size = newsize;

	/* Grow the db mark buffer */

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

void db_free(void)
{
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;
}

#ifndef STANDALONE
void db_make_minimal(void)
{
dbref	obj;

	db_free();
	db_grow(1);
	s_Name(0, "Limbo");
	s_Flags(0, TYPE_ROOM);
	s_Location(0, NOTHING);
	s_Exits(0, NOTHING);
	s_Link(0, NOTHING);
	s_Parent(0, NOTHING);
	s_Zone(0, NOTHING);
	s_Pennies(0, 1);

	/* should be #1 */
	load_player_names();
	obj = create_player((char *)"Wizard", (char *)"potrzebie", NOTHING, 0);
	s_Flags(obj, Flags(obj) | WIZARD);
	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);
}
#endif

dbref parse_dbref(const char *s)
{
  const char *p;
  long x;
  x = atol(s);
  if (x > 0) {
    return x;
  } else if (x == 0) {
    /* check for 0 */
    for (p = s; *p; p++) {
      if (*p == '0')
	return 0;
      if (!isspace(*p))
	break;
    }
  }
  /* else x < 0 or s != 0 */
  return NOTHING;
}

void putref(FILE * f, dbref ref)
{
  fprintf(f, "%d\n", ref);
}

void putstring(FILE * f, const char *s)
{
  if (s) {
    fputs(s, f);
  }
  putc('\n', f);
}

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

	p = buf;
	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);
	}
}

dbref getref(FILE * f)
{
  return(atol(getstring_noalloc(f)));
}

void free_boolexp(struct boolexp * b)
{
  if (b != TRUE_BOOLEXP) {
    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:
	free((char *)b->sub1);
	free_bool(b);
	break;
    }
  }
}

struct boolexp *dup_bool(struct boolexp * b)
{
  struct 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_ATR:
      r->thing = b->thing;
      r->sub1 = (struct boolexp *)strsave((char *)b->sub1);
      break;
    default:
      fprintf(stderr, "bad bool type!!\n");
      return (TRUE_BOOLEXP);
  }
  return (r);
}

void clone_object(dbref a, dbref b)
{
	memcpy(&db[a], &db[b], sizeof(struct object));
}

int init_gdbm_db(char *gdbmfile)
{
  HASHINFO info;
  info.bsize = 4096;  /* Setting this to your blocksize is pretty good. */
#ifndef STANDALONE
  info.cachesize = mudconf.gdbm_cache_size * 1024;
#else
  info.cachesize = 1024 * 1024 * 4;	/* 4 megabytes */
#endif
  info.ffactor = 64;
  info.hash = NULL;		/* use default hash function. */
  info.lorder = 0;		/* use system byteorder. */
  info.nelem = 40000;
#ifdef STANDALONE
  fprintf(stderr, "Opening %s\n", gdbmfile);
#endif
  dbp = hash_open(gdbmfile, O_CREAT | O_RDWR, 0600, &info);
#ifdef STANDALONE
  fprintf(stderr, "Done opening %s.\n", gdbmfile);
#endif
  if (!dbp)
    return (-1);
#ifdef STANDALONE
  fprintf(stderr, "Done initializing.\n");
#else
	STARTLOG(LOG_ALWAYS,"INI","LOAD")
		log_text((char *)"Using gdbm file: ");
		log_text(gdbmfile);
	ENDLOG
#endif
  db_free();
  return (0);
}