fbmuck-6.05/auto/
fbmuck-6.05/contrib/jresolver/
fbmuck-6.05/contrib/jresolver/org/
fbmuck-6.05/contrib/jresolver/org/fuzzball/
fbmuck-6.05/docs/devel/
fbmuck-6.05/game/
fbmuck-6.05/game/logs/
fbmuck-6.05/game/muf/
fbmuck-6.05/scripts/
fbmuck-6.05/src_docs/
/* $Header: /cvsroot/fbmuck/fbmuck/src/property.c,v 1.26 2004/10/07 09:19:03 revar Exp $ */

#include "copyright.h"
#include "config.h"
#include "params.h"

#include "db.h"
#include "tune.h"
#include "mpi.h"
#include "props.h"
#include "externs.h"
#include "interface.h"
#include <string.h>
#include <math.h>

#define alloc_compressed(x) alloc_string(compress(x))

/* property.c
   A whole new lachesis mod.
   Adds property manipulation routines to TinyMUCK.   */

/* Completely rewritten by darkfox and Foxen, for propdirs and other things */



void
set_property_nofetch(dbref player, const char *pname, PData * dat)
{
	PropPtr p;
	char buf[BUFFER_LEN];
	char *n, *w;

	/* Make sure that we are passed a valid property name */
	if (!pname)
		return;

	while (*pname == PROPDIR_DELIMITER)
		pname++;
	if ((!(FLAGS(player) & LISTENER)) &&
		(string_prefix(pname, "_listen") ||
		 string_prefix(pname, "~listen") || string_prefix(pname, "~olisten"))) {
		FLAGS(player) |= LISTENER;
	}

	w = strcpyn(buf, sizeof(buf), pname);

	/* truncate propnames with a ':' in them at the ':' */
	n = index(buf, PROP_DELIMITER);
	if (n)
		*n = '\0';
	if (!*buf)
		return;

	p = propdir_new_elem(&(DBFETCH(player)->properties), w);

	/* free up any old values */
	clear_propnode(p);

	SetPFlagsRaw(p, dat->flags);
	if (PropFlags(p) & PROP_ISUNLOADED) {
		SetPDataUnion(p, dat->data);
		return;
	}
	switch (PropType(p)) {
	case PROP_STRTYP:
		if (!dat->data.str || !*(dat->data.str)) {
			SetPType(p, PROP_DIRTYP);
			SetPDataStr(p, NULL);
			if (!PropDir(p)) {
				remove_property_nofetch(player, pname);
			}
		} else {
			SetPDataStr(p, alloc_compressed(dat->data.str));
#ifdef COMPRESS
			SetPFlagsRaw(p, (dat->flags | PROP_COMPRESSED));
#endif
		}
		break;
	case PROP_INTTYP:
		SetPDataVal(p, dat->data.val);
		if (!dat->data.val) {
			SetPType(p, PROP_DIRTYP);
			if (!PropDir(p)) {
				remove_property_nofetch(player, pname);
			}
		}
		break;
	case PROP_FLTTYP:
		SetPDataFVal(p, dat->data.fval);
		if (dat->data.fval == 0.0) {
			SetPType(p, PROP_DIRTYP);
			if (!PropDir(p)) {
				remove_property_nofetch(player, pname);
			}
		}
		break;
	case PROP_REFTYP:
		SetPDataRef(p, dat->data.ref);
		if (dat->data.ref == NOTHING) {
			SetPType(p, PROP_DIRTYP);
			SetPDataRef(p, 0);
			if (!PropDir(p)) {
				remove_property_nofetch(player, pname);
			}
		}
		break;
	case PROP_LOKTYP:
		SetPDataLok(p, dat->data.lok);
		break;
	case PROP_DIRTYP:
		SetPDataVal(p, 0);
		if (!PropDir(p)) {
			remove_property_nofetch(player, pname);
		}
		break;
	}
}


void
set_property(dbref player, const char *name, PData * dat)
{
#ifdef DISKBASE
	fetchprops(player, propdir_name(name));
	set_property_nofetch(player, name, dat);
	dirtyprops(player);
#else
	set_property_nofetch(player, name, dat);
#endif
	DBDIRTY(player);
}

void
set_lock_property(dbref player, const char *pname, const char *lok)
{
	PData mydat;

	mydat.flags = PROP_LOKTYP;
	if (!lok || !*lok) {
		mydat.data.lok = TRUE_BOOLEXP;
	} else {
		mydat.data.lok = parse_boolexp(-1, (dbref) 1, lok, 1);
	}
	set_property(player, pname, &mydat);
}



/* adds a new property to an object */
void
add_prop_nofetch(dbref player, const char *pname, const char *strval, int value)
{
	PData mydat;

	if (strval && *strval) {
		mydat.flags = PROP_STRTYP;
		mydat.data.str = (char *) strval;
	} else if (value) {
		mydat.flags = PROP_INTTYP;
		mydat.data.val = value;
	} else {
		mydat.flags = PROP_DIRTYP;
		mydat.data.str = NULL;
	}
	set_property_nofetch(player, pname, &mydat);
}


/* adds a new property to an object */
void
add_property(dbref player, const char *pname, const char *strval, int value)
{

#ifdef DISKBASE
	fetchprops(player, propdir_name(pname));
	add_prop_nofetch(player, pname, strval, value);
	dirtyprops(player);
#else
	add_prop_nofetch(player, pname, strval, value);
#endif
	DBDIRTY(player);
}


void
remove_proplist_item(dbref player, PropPtr p, int allp)
{
	const char *ptr;

	if (!p)
		return;
	ptr = PropName(p);

	if (Prop_System(ptr))
		return;

	if (!allp) {
		if (*ptr == PROP_HIDDEN)
			return;
		if (*ptr == PROP_SEEONLY)
			return;
		if (ptr[0] == '_' && ptr[1] == '\0')
			return;
		if (PropFlags(p) & PROP_SYSPERMS)
			return;
	}
	remove_property(player, ptr);
}


/* removes property list --- if it's not there then ignore */
void
remove_property_list(dbref player, int all)
{
	PropPtr l;
	PropPtr p;
	PropPtr n;

#ifdef DISKBASE
	fetchprops(player, NULL);
#endif

	if ((l = DBFETCH(player)->properties) != NULL) {
		p = first_node(l);
		while (p) {
			n = next_node(l, PropName(p));
			remove_proplist_item(player, p, all);
			l = DBFETCH(player)->properties;
			p = n;
		}
	}

#ifdef DISKBASE
	dirtyprops(player);
#endif

	DBDIRTY(player);
}

/* removes property --- if it's not there then ignore */
void
remove_property_nofetch(dbref player, const char *pname)
{
	PropPtr l;
	char buf[BUFFER_LEN];
	char *w;

	w = strcpyn(buf, sizeof(buf), pname);

	l = DBFETCH(player)->properties;
	l = propdir_delete_elem(l, w);
	DBFETCH(player)->properties = l;
	DBDIRTY(player);
}


void
remove_property(dbref player, const char *pname)
{
#ifdef DISKBASE
	fetchprops(player, propdir_name(pname));
#endif

	remove_property_nofetch(player, pname);

#ifdef DISKBASE
	dirtyprops(player);
#endif
}


PropPtr
get_property(dbref player, const char *pname)
{
	PropPtr p;
	char buf[BUFFER_LEN];
	char *w;

#ifdef DISKBASE
	fetchprops(player, propdir_name(pname));
#endif

	w = strcpy(buf, pname);

	p = propdir_get_elem(DBFETCH(player)->properties, w);
	return (p);
}


/* checks if object has property, returning 1 if it or any of it's contents has
   the property stated                                                      */
int
has_property(int descr, dbref player, dbref what, const char *pname, const char *strval,
			 int value)
{
	dbref things;

	if (has_property_strict(descr, player, what, pname, strval, value))
		return 1;
	for (things = DBFETCH(what)->contents; things != NOTHING; things = DBFETCH(things)->next) {
		if (has_property(descr, player, things, pname, strval, value))
			return 1;
	}
	if (tp_lock_envcheck) {
		things = getparent(what);
		while (things != NOTHING) {
			if (has_property_strict(descr, player, things, pname, strval, value))
				return 1;
			things = getparent(things);
		}
	}
	return 0;
}


static int has_prop_recursion_limit = 2;
/* checks if object has property, returns 1 if it has the property */
int
has_property_strict(int descr, dbref player, dbref what, const char *pname, const char *strval,
					int value)
{
	PropPtr p;
	const char *str;
	char *ptr;
	char buf[BUFFER_LEN];

	p = get_property(what, pname);

	if (p) {
#ifdef DISKBASE
		propfetch(what, p);
#endif
		if (PropType(p) == PROP_STRTYP) {
			str = uncompress(DoNull(PropDataStr(p)));

			if (has_prop_recursion_limit-->0) {
				ptr = do_parse_mesg(descr, player, what, str, "(Lock)", buf,
									(MPI_ISPRIVATE | MPI_ISLOCK |
									((PropFlags(p) & PROP_BLESSED)? MPI_ISBLESSED : 0)));
			} else {
				strcpyn(buf, sizeof(buf), str); 
				ptr = buf;
			}
			has_prop_recursion_limit++;
			return (equalstr((char *) strval, ptr));
		} else if (PropType(p) == PROP_LOKTYP) {
			return 0;
		} else if (PropType(p) == PROP_INTTYP) {
			return (value == PropDataVal(p));
		} else {
			return (value == (int) PropDataFVal(p));
		}
	}
	return 0;
}

/* return string value of property */
const char *
get_property_class(dbref player, const char *pname)
{
	PropPtr p;

	p = get_property(player, pname);
	if (p) {
#ifdef DISKBASE
		propfetch(player, p);
#endif
		if (PropType(p) != PROP_STRTYP)
			return (char *) NULL;
		return (PropDataStr(p));
	} else {
		return (char *) NULL;
	}
}

/* return value of property */
int
get_property_value(dbref player, const char *pname)
{
	PropPtr p;

	p = get_property(player, pname);

	if (p) {
#ifdef DISKBASE
		propfetch(player, p);
#endif
		if (PropType(p) != PROP_INTTYP)
			return 0;
		return (PropDataVal(p));
	} else {
		return 0;
	}
}

/* return float value of a property */
double
get_property_fvalue(dbref player, const char *pname)
{
	PropPtr p;

	p = get_property(player, pname);
	if (p) {
#ifdef DISKBASE
		propfetch(player, p);
#endif
		if (PropType(p) != PROP_FLTTYP)
			return 0.0;
		return (PropDataFVal(p));
	} else {
		return 0.0;
	}
}

/* return boolexp lock of property */
dbref
get_property_dbref(dbref player, const char *pname)
{
	PropPtr p;

	p = get_property(player, pname);
	if (!p)
		return NOTHING;
#ifdef DISKBASE
	propfetch(player, p);
#endif
	if (PropType(p) != PROP_REFTYP)
		return NOTHING;
	return PropDataRef(p);
}


/* return boolexp lock of property */
struct boolexp *
get_property_lock(dbref player, const char *pname)
{
	PropPtr p;

	p = get_property(player, pname);
	if (!p)
		return TRUE_BOOLEXP;
#ifdef DISKBASE
	propfetch(player, p);
	if (PropFlags(p) & PROP_ISUNLOADED)
		return TRUE_BOOLEXP;
#endif
	if (PropType(p) != PROP_LOKTYP)
		return TRUE_BOOLEXP;
	return PropDataLok(p);
}


/* return flags of property */
int
get_property_flags(dbref player, const char *pname)
{
	PropPtr p;

	p = get_property(player, pname);

	if (p) {
		return (PropFlags(p));
	} else {
		return 0;
	}
}


/* return flags of property */
void
clear_property_flags(dbref player, const char *pname, int flags)
{
	PropPtr p;

	flags &= ~PROP_TYPMASK;
	p = get_property(player, pname);
	if (p) {
		SetPFlags(p, (PropFlags(p) & ~flags));
#ifdef DISKBASE
		dirtyprops(player);
#endif
	}
}


/* return flags of property */
void
set_property_flags(dbref player, const char *pname, int flags)
{
	PropPtr p;

	flags &= ~PROP_TYPMASK;
	p = get_property(player, pname);
	if (p) {
		SetPFlags(p, (PropFlags(p) | flags));
#ifdef DISKBASE
		dirtyprops(player);
#endif
	}
}


/* return type of property */
int
get_property_type(dbref player, const char *pname)
{
	PropPtr p;

	p = get_property(player, pname);

	if (p) {
		return (PropType(p));
	} else {
		return 0;
	}
}



PropPtr
copy_prop(dbref old)
{
	PropPtr p, n = NULL;

#ifdef DISKBASE
	fetchprops(old, NULL);
#endif

	p = DBFETCH(old)->properties;
	copy_proplist(old, &n, p);
	return (n);
}

/* Return a pointer to the first property in a propdir and duplicates the
   property name into 'name'.  Returns NULL if the property list is empty
   or does not exist. */
PropPtr
first_prop_nofetch(dbref player, const char *dir, PropPtr * list, char *name)
{
	char buf[BUFFER_LEN];
	PropPtr p;

	if (dir) {
		while (*dir && *dir == PROPDIR_DELIMITER) {
			dir++;
		}
	}
	if (!dir || !*dir) {
		*list = DBFETCH(player)->properties;
		p = first_node(*list);
		if (p) {
			strcpy(name, PropName(p));
		} else {
			*name = '\0';
		}
		return (p);
	}

	strcpy(buf, dir);
	*list = p = propdir_get_elem(DBFETCH(player)->properties, buf);
	if (!p) {
		*name = '\0';
		return NULL;
	}
	*list = PropDir(p);
	p = first_node(*list);
	if (p) {
		strcpy(name, PropName(p));
	} else {
		*name = '\0';
	}

	return (p);
}


/* first_prop() returns a pointer to the first property.
 * player    dbref of object that the properties are on.
 * dir       pointer to string name of the propdir
 * list      pointer to a proplist pointer.  Returns the root node.
 * name      printer to a string.  Returns the name of the first node.
 */

PropPtr
first_prop(dbref player, const char *dir, PropPtr * list, char *name)
{

#ifdef DISKBASE
	fetchprops(player, (char *) dir);
#endif

	return (first_prop_nofetch(player, dir, list, name));
}



/* next_prop() returns a pointer to the next property node.
 * list    Pointer to the root node of the list.
 * prop    Pointer to the previous prop.
 * name    Pointer to a string.  Returns the name of the next property.
 */

PropPtr
next_prop(PropPtr list, PropPtr prop, char *name)
{
	PropPtr p = prop;

	if (!p || !(p = next_node(list, PropName(p))))
		return ((PropPtr) 0);

	strcpy(name, PropName(p));
	return (p);
}



/* next_prop_name() returns a ptr to the string name of the next property.
 * player   object the properties are on.
 * outbuf   pointer to buffer to return the next prop's name in.
 * name     pointer to the name of the previous property.
 *
 * Returns null if propdir doesn't exist, or if no more properties in list.
 * Call with name set to "" to get the first property of the root propdir.
 */

char *
next_prop_name(dbref player, char *outbuf, int outbuflen, char *name)
{
	char *ptr;
	char buf[BUFFER_LEN];
	PropPtr p, l;

#ifdef DISKBASE
	fetchprops(player, propdir_name(name));
#endif

	strcpy(buf, name);
	if (!*name || name[strlen(name) - 1] == PROPDIR_DELIMITER) {
		l = DBFETCH(player)->properties;
		p = propdir_first_elem(l, buf);
		if (!p) {
			*outbuf = '\0';
			return NULL;
		}
		strcpyn(outbuf, outbuflen, name);
		strcatn(outbuf, outbuflen, PropName(p));
	} else {
		l = DBFETCH(player)->properties;
		p = propdir_next_elem(l, buf);
		if (!p) {
			*outbuf = '\0';
			return NULL;
		}
		strcpy(outbuf, name);
		ptr = rindex(outbuf, PROPDIR_DELIMITER);
		if (!ptr)
			ptr = outbuf;
		*(ptr++) = PROPDIR_DELIMITER;
		strcpy(ptr, PropName(p));
	}
	return outbuf;
}


long
size_properties(dbref player, int load)
{
#ifdef DISKBASE
	if (load) {
		fetchprops(player, NULL);
		fetch_propvals(player, "/");
	}
#endif
	return size_proplist(DBFETCH(player)->properties);
}


/* return true if a property contains a propdir */
int
is_propdir_nofetch(dbref player, const char *pname)
{
	PropPtr p;
	char w[BUFFER_LEN];

	strcpy(w, pname);
	p = propdir_get_elem(DBFETCH(player)->properties, w);
	if (!p)
		return 0;
	return (PropDir(p) != (PropPtr) NULL);
}


int
is_propdir(dbref player, const char *pname)
{

#ifdef DISKBASE
	fetchprops(player, propdir_name(pname));
#endif

	return (is_propdir_nofetch(player, pname));
}


PropPtr
envprop(dbref * where, const char *propname, int typ)
{
	PropPtr temp;

	while (*where != NOTHING) {
		temp = get_property(*where, propname);
#ifdef DISKBASE
		if (temp)
			propfetch(*where, temp);
#endif
		if (temp && (!typ || PropType(temp) == typ))
			return temp;
		*where = getparent(*where);
	}
	return NULL;
}


const char *
envpropstr(dbref * where, const char *propname)
{
	PropPtr temp;

	temp = envprop(where, propname, PROP_STRTYP);
	if (!temp)
		return NULL;
	if (PropType(temp) == PROP_STRTYP)
		return (PropDataStr(temp));
	return NULL;
}


char *
displayprop(dbref player, dbref obj, const char *name, char *buf, size_t bufsiz)
{
	char mybuf[BUFFER_LEN];
	int pdflag;
	char blesschar = '-';
	PropPtr p = get_property(obj, name);

	if (!p) {
		snprintf(buf, bufsiz, "%s: No such property.", name);
		return buf;
	}
#ifdef DISKBASE
	propfetch(obj, p);
#endif
	if (PropFlags(p) & PROP_BLESSED)
		blesschar = 'B';
	pdflag = (PropDir(p) != NULL);
	snprintf(mybuf, bufsiz, "%.*s%c", (BUFFER_LEN / 4), name, (pdflag) ? PROPDIR_DELIMITER : '\0');
	switch (PropType(p)) {
	case PROP_STRTYP:
		snprintf(buf, bufsiz, "%c str %s:%.*s", blesschar, mybuf, (BUFFER_LEN / 2), PropDataStr(p));
		break;
	case PROP_REFTYP:
		snprintf(buf, bufsiz, "%c ref %s:%s", blesschar, mybuf, unparse_object(player, PropDataRef(p)));
		break;
	case PROP_INTTYP:
		snprintf(buf, bufsiz, "%c int %s:%d", blesschar, mybuf, PropDataVal(p));
		break;
	case PROP_FLTTYP:
		snprintf(buf, bufsiz, "%c flt %s:%.17g", blesschar, mybuf, PropDataFVal(p));
		break;
	case PROP_LOKTYP:
		if (PropFlags(p) & PROP_ISUNLOADED) {
			snprintf(buf, bufsiz, "%c lok %s:*UNLOCKED*", blesschar, mybuf);
		} else {
			snprintf(buf, bufsiz, "%c lok %s:%.*s", blesschar, mybuf, (BUFFER_LEN / 2),
					unparse_boolexp(player, PropDataLok(p), 1));
		}
		break;
	case PROP_DIRTYP:
		snprintf(buf, bufsiz, "%c dir %s:(no value)", blesschar, mybuf);
		break;
	}
	return buf;
}



#define DOWNCASE(x) (tolower(x))

extern short db_conversion_flag;
extern short db_decompression_flag;


int
db_get_single_prop(FILE * f, dbref obj, long pos, PropPtr pnode, const char *pdir)
{
	char getprop_buf[BUFFER_LEN * 3];
	char *name, *flags, *value, *p;
	int flg;
	long tpos=0L;
	struct boolexp *lok;
	short do_diskbase_propvals;
	PData mydat;

#ifdef DISKBASE
	do_diskbase_propvals = tp_diskbase_propvals;
#else
	do_diskbase_propvals = 0;
#endif

	if (pos) {
		fseek(f, pos, 0);
	} else if (do_diskbase_propvals) {
		tpos = ftell(f);
	}
	name = fgets(getprop_buf, sizeof(getprop_buf), f);
	if (!name) {
		wall_wizards("## WARNING! A corrupt property was found while trying to read it from disk.");
		wall_wizards("##   This property has been skipped and will not be loaded.  See the sanity");
		wall_wizards("##   logfile for technical details.");
		log_sanity("Failed to read property from disk: Failed disk read.  obj = #%d, pos = %ld, pdir = %s", obj, pos, pdir);
		return -1;
	}
	if (*name == '*') {
		if (!strcmp(name, "*End*\n")) {
			return 0;
		}
		if (name[1] == '\n') {
			return 2;
		}
	}

	flags = index(name, PROP_DELIMITER);
	if (!flags) {
		wall_wizards("## WARNING! A corrupt property was found while trying to read it from disk.");
		wall_wizards("##   This property has been skipped and will not be loaded.  See the sanity");
		wall_wizards("##   logfile for technical details.");
		log_sanity("Failed to read property from disk: Corrupt property, flag delimiter not found.  obj = #%d, pos = %ld, pdir = %s, data = %s", obj, pos, pdir, name);
		return -1;
	}
	*flags++ = '\0';

	value = index(flags, PROP_DELIMITER);
	if (!value) {
		wall_wizards("## WARNING! A corrupt property was found while trying to read it from disk.");
		wall_wizards("##   This property has been skipped and will not be loaded.  See the sanity");
		wall_wizards("##   logfile for technical details.");
		log_sanity("Failed to read property from disk: Corrupt property, value delimiter not found.  obj = #%d, pos = %ld, pdir = %s, data = %s:%s", obj, pos, pdir, name, flags);
		return -1;
	}
	*value++ = '\0';

	p = index(value, '\n');
	if (p) {
		*p = '\0';
	}

	if (!number(flags)) {
		wall_wizards("## WARNING! A corrupt property was found while trying to read it from disk.");
		wall_wizards("##   This property has been skipped and will not be loaded.  See the sanity");
		wall_wizards("##   logfile for technical details.");
		log_sanity("Failed to read property from disk: Corrupt property flags.  obj = #%d, pos = %ld, pdir = %s, data = %s:%s:%s", obj, pos, pdir, name, flags, value);
		return -1;
	}
	flg = atoi(flags);

	switch (flg & PROP_TYPMASK) {
	case PROP_STRTYP:
		if (!do_diskbase_propvals || pos) {
			flg &= ~PROP_ISUNLOADED;
#ifdef COMPRESS
			if (!(flg & PROP_COMPRESSED)) {
				value = (char *) old_uncompress(value);
			}
#endif
			if (pnode) {
				SetPDataStr(pnode, alloc_compressed(value));
#ifdef COMPRESS
				flg |= PROP_COMPRESSED;
#endif
				SetPFlagsRaw(pnode, flg);
			} else {
				mydat.flags = flg;
				mydat.data.str = value;
				set_property_nofetch(obj, name, &mydat);
			}
		} else {
			flg |= PROP_ISUNLOADED;
			mydat.flags = flg;
			mydat.data.val = tpos;
			set_property_nofetch(obj, name, &mydat);
		}
		break;
	case PROP_LOKTYP:
		if (!do_diskbase_propvals || pos) {
			lok = parse_boolexp(-1, (dbref) 1, value, 32767);
			flg &= ~PROP_ISUNLOADED;
			if (pnode) {
				SetPDataLok(pnode, lok);
				SetPFlagsRaw(pnode, flg);
			} else {
				mydat.flags = flg;
				mydat.data.lok = lok;
				set_property_nofetch(obj, name, &mydat);
			}
		} else {
			flg |= PROP_ISUNLOADED;
			mydat.flags = flg;
			mydat.data.val = tpos;
			set_property_nofetch(obj, name, &mydat);
		}
		break;
	case PROP_INTTYP:
		if (!number(value)) {
			wall_wizards("## WARNING! A corrupt property was found while trying to read it from disk.");
			wall_wizards("##   This property has been skipped and will not be loaded.  See the sanity");
			wall_wizards("##   logfile for technical details.");
			log_sanity("Failed to read property from disk: Corrupt integer value.  obj = #%d, pos = %ld, pdir = %s, data = %s:%s:%s", obj, pos, pdir, name, flags, value);
			return -1;
		}
		mydat.flags = flg;
		mydat.data.val = atoi(value);
		set_property_nofetch(obj, name, &mydat);
		break;
	case PROP_FLTTYP:
		mydat.flags = flg;
		if (!number(value) && !ifloat(value)) {
			char *tpnt = value;
			int dtemp = 0;

			if ((*tpnt == '+') || (*tpnt == '-')) {
				if (*tpnt == '+') {
					dtemp = 0;
				} else {
					dtemp = 1;
				}
				tpnt++;
			}
			tpnt[0] = toupper(tpnt[0]);
			tpnt[1] = toupper(tpnt[1]);
			tpnt[2] = toupper(tpnt[2]);
			if (!strncmp(tpnt, "INF", 3)) {
				if (!dtemp) {
					mydat.data.fval = INF;
				} else {
					mydat.data.fval = NINF;
				}
			} else {
				if (!strncmp(tpnt, "NAN", 3)) {
					/* FIXME: This should be NaN. */
					mydat.data.fval = INF;
				}
			}
		} else {
			sscanf(value, "%lg", &mydat.data.fval);
		}
		set_property_nofetch(obj, name, &mydat);
		break;
	case PROP_REFTYP:
		if (!number(value)) {
			wall_wizards("## WARNING! A corrupt property was found while trying to read it from disk.");
			wall_wizards("##   This property has been skipped and will not be loaded.  See the sanity");
			wall_wizards("##   logfile for technical details.");
			log_sanity("Failed to read property from disk: Corrupt dbref value.  obj = #%d, pos = %ld, pdir = %s, data = %s:%s:%s", obj, pos, pdir, name, flags, value);
			return -1;
		}
		mydat.flags = flg;
		mydat.data.ref = atoi(value);
		set_property_nofetch(obj, name, &mydat);
		break;
	case PROP_DIRTYP:
		break;
	}
	return 1;
}

void
db_getprops(FILE * f, dbref obj, const char *pdir)
{
	while (db_get_single_prop(f, obj, 0L, (PropPtr) NULL, pdir)) ;
}


void
db_putprop(FILE * f, const char *dir, PropPtr p)
{
	char buf[BUFFER_LEN * 2];
	const char *ptr2;
	char tbuf[50];
	int outflags = (PropFlagsRaw(p) & ~(PROP_TOUCHED | PROP_ISUNLOADED | PROP_DIRUNLOADED));

	if (PropType(p) == PROP_DIRTYP)
		return;

	ptr2 = "";
	switch (PropType(p)) {
	case PROP_INTTYP:
		if (!PropDataVal(p))
			return;
		ptr2 = intostr(PropDataVal(p));
		break;
	case PROP_FLTTYP:
		if (!PropDataFVal(p))
			return;
		snprintf(tbuf, sizeof(tbuf), "%.17g", PropDataFVal(p));
		ptr2 = tbuf;
		break;
	case PROP_REFTYP:
		if (PropDataRef(p) == NOTHING)
			return;
		ptr2 = intostr(PropDataRef(p));
		break;
	case PROP_STRTYP:
		if (!*PropDataStr(p))
			return;
		if (db_decompression_flag) {
			ptr2 = uncompress(PropDataStr(p));
		} else {
			ptr2 = PropDataStr(p);
		}
		break;
	case PROP_LOKTYP:
		if (PropFlags(p) & PROP_ISUNLOADED)
			return;
		if (PropDataLok(p) == TRUE_BOOLEXP)
			return;
		ptr2 = unparse_boolexp((dbref) 1, PropDataLok(p), 0);
		break;
	}

	snprintf(buf, sizeof(buf), "%s%s%c%d%c%s\n",
			dir+1, PropName(p), PROP_DELIMITER,
			outflags, PROP_DELIMITER, ptr2);

	if (fputs(buf, f) == EOF) {
		log_sanity("Failed to write out property db_putprop(dir = %s)", dir);
		abort();
	}
}


int
db_dump_props_rec(dbref obj, FILE * f, const char *dir, PropPtr p)
{
	char buf[BUFFER_LEN];
#ifdef DISKBASE
	long tpos=0L;
	int flg;
	short wastouched = 0;
#endif
	int count = 0;
	int pdcount;

	if (!p)
		return 0;

	count += db_dump_props_rec(obj, f, dir, AVL_LF(p));

#ifdef DISKBASE
	wastouched = (PropFlags(p) & PROP_TOUCHED);
	if (tp_diskbase_propvals) {
		tpos = ftell(f);
	}
	if (wastouched) {
		count++;
	}
	if (propfetch(obj, p)) {
		fseek(f, 0L, 2);
	}
#endif

	db_putprop(f, dir, p);

#ifdef DISKBASE
	if (tp_diskbase_propvals && !wastouched) {
		if (PropType(p) == PROP_STRTYP || PropType(p) == PROP_LOKTYP) {
			flg = PropFlagsRaw(p) | PROP_ISUNLOADED;
			clear_propnode(p);
			SetPFlagsRaw(p, flg);
			SetPDataVal(p, tpos);
		}
	}
#endif

	if (PropDir(p)) {
		const char *iptr;
		char *optr;

		for (iptr = dir, optr = buf; *iptr;)
			*optr++ = *iptr++;
		for (iptr = PropName(p); *iptr;)
			*optr++ = *iptr++;
		*optr++ = PROPDIR_DELIMITER;
		*optr++ = '\0';

		pdcount = db_dump_props_rec(obj, f, buf, PropDir(p));
		count += pdcount;
	}

	count += db_dump_props_rec(obj, f, dir, AVL_RT(p));

	return count;
}


void
db_dump_props(FILE * f, dbref obj)
{
	db_dump_props_rec(obj, f, "/", DBFETCH(obj)->properties);
}


void
untouchprop_rec(PropPtr p)
{
	if (!p)
		return;
	SetPFlags(p, (PropFlags(p) & ~PROP_TOUCHED));
	untouchprop_rec(AVL_LF(p));
	untouchprop_rec(AVL_RT(p));
	untouchprop_rec(PropDir(p));
}

static dbref untouch_lastdone = 0;
void
untouchprops_incremental(int limit)
{
	PropPtr p;

	while (untouch_lastdone < db_top) {
		/* clear the touch flags */
		p = DBFETCH(untouch_lastdone)->properties;
		if (p) {
			if (!limit--)
				return;
			untouchprop_rec(p);
		}
		untouch_lastdone++;
	}
	untouch_lastdone = 0;
}


void
reflist_add(dbref obj, const char* propname, dbref toadd)
{
	PropPtr ptr;
	const char *temp;
	const char *list;
	int count = 0;
	int charcount = 0;
	char buf[BUFFER_LEN];
	char outbuf[BUFFER_LEN];

	ptr = get_property(obj, propname);
	if (ptr) {
		const char *pat = NULL;

#ifdef DISKBASE
		propfetch(obj, ptr);
#endif
		switch (PropType(ptr)) {
		case PROP_STRTYP:
			*outbuf = '\0';
			list = temp = uncompress(PropDataStr(ptr));
			snprintf(buf, sizeof(buf), "%d", toadd);
			while (*temp) {
				if (*temp == '#') {
					pat = buf;
					count++;
					charcount = temp - list;
				} else if (pat) {
					if (!*pat) {
						if (!*temp || *temp == ' ') {
							break;
						}
						pat = NULL;
					} else if (*pat != *temp) {
						pat = NULL;
					} else {
						pat++;
					}
				}
				temp++;
			}
			if (pat && !*pat) {
				if (charcount > 0) {
					strcpyn(outbuf, charcount, list);
				}
				strcatn(outbuf, sizeof(outbuf), temp);
			} else {
				strcpyn(outbuf, sizeof(outbuf), list);
			}
			snprintf(buf, sizeof(buf), " #%d", toadd);
			if (strlen(outbuf) + strlen(buf) < BUFFER_LEN) {
				strcatn(outbuf, sizeof(outbuf), buf);
				for (temp = outbuf; isspace(*temp); temp++);
				add_property(obj, propname, temp, 0);
			}
			break;
		case PROP_REFTYP:
			if (PropDataRef(ptr) != toadd) {
				snprintf(outbuf, sizeof(outbuf), "#%d #%d", PropDataRef(ptr), toadd);
				add_property(obj, propname, outbuf, 0);
			}
			break;
		default:
			snprintf(outbuf, sizeof(outbuf), "#%d", toadd);
			add_property(obj, propname, outbuf, 0);
			break;
		}
	} else {
		snprintf(outbuf, sizeof(outbuf), "#%d", toadd);
		add_property(obj, propname, outbuf, 0);
	}
}


void
reflist_del(dbref obj, const char* propname, dbref todel)
{
	PropPtr ptr;
	const char *temp;
	const char *list;
	int count = 0;
	int charcount = 0;
	char buf[BUFFER_LEN];
	char outbuf[BUFFER_LEN];

	ptr = get_property(obj, propname);
	if (ptr) {
		const char *pat = NULL;

#ifdef DISKBASE
		propfetch(obj, ptr);
#endif
		switch (PropType(ptr)) {
		case PROP_STRTYP:
			*outbuf = '\0';
			list = temp = uncompress(PropDataStr(ptr));
			snprintf(buf, sizeof(buf), "%d", todel);
			while (*temp) {
				if (*temp == '#') {
					pat = buf;
					count++;
					charcount = temp - list;
				} else if (pat) {
					if (!*pat) {
						if (!*temp || *temp == ' ') {
							break;
						}
						pat = NULL;
					} else if (*pat != *temp) {
						pat = NULL;
					} else {
						pat++;
					}
				}
				temp++;
			}
			if (pat && !*pat) {
				if (charcount > 0) {
					strcpyn(outbuf, charcount, list);
				}
				strcatn(outbuf, sizeof(outbuf), temp);
				for (temp = outbuf; isspace(*temp); temp++);
				add_property(obj, propname, temp, 0);
			}
			break;
		case PROP_REFTYP:
			if (PropDataRef(ptr) == todel) {
				add_property(obj, propname, "", 0);
			}
			break;
		default:
			break;
		}
	}
}


int
reflist_find(dbref obj, const char* propname, dbref tofind)
{
	PropPtr ptr;
	const char *temp;
	int pos = 0;
	int count = 0;
	char buf[BUFFER_LEN];

	ptr = get_property(obj, propname);
	if (ptr) {
		const char *pat = NULL;

#ifdef DISKBASE
		propfetch(obj, ptr);
#endif
		switch (PropType(ptr)) {
		case PROP_STRTYP:
			temp = uncompress(PropDataStr(ptr));
			snprintf(buf, sizeof(buf), "%d", tofind);
			while (*temp) {
				if (*temp == '#') {
					pat = buf;
					count++;
				} else if (pat) {
					if (!*pat) {
						if (!*temp || *temp == ' ') {
							break;
						}
						pat = NULL;
					} else if (*pat != *temp) {
						pat = NULL;
					} else {
						pat++;
					}
				}
				temp++;
			}
			if (pat && !*pat) {
				pos = count;
			}
			break;
		case PROP_REFTYP:
			if (PropDataRef(ptr) == tofind)
				pos = 1;
			break;
		default:
			break;
		}
	}
	return pos;
}