btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
/*
 * db_rw.c 
 */

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

#include <sys/file.h>

#include "mudconf.h"
#include "config.h"
#include "externs.h"
#include "db.h"
#include "vattr.h"
#include "attrs.h"
#include "alloc.h"
#include "powers.h"

extern const char *getstring_noalloc(FILE *, int);
extern void putstring(FILE *, const char *);
extern void db_grow(dbref);

extern struct object *db;

static int g_version;
static int g_format;
static int g_flags;

/*
 * ---------------------------------------------------------------------------
 * * getboolexp1: Get boolean subexpression from file.
 */

static BOOLEXP *getboolexp1(FILE * f)
{
	BOOLEXP *b;
	char *buff, *s;
	int c, d, anum;

	c = getc(f);
	switch (c) {
	case '\n':
		ungetc(c, f);
		return TRUE_BOOLEXP;
		/*
		 * break; 
		 */
	case EOF:
		abort();				/*
								 * unexpected EOF in boolexp 
								 */
		break;
	case '(':
		b = alloc_bool("getboolexp1.openparen");
		switch (c = getc(f)) {
		case NOT_TOKEN:
			b->type = BOOLEXP_NOT;
			b->sub1 = getboolexp1(f);
			if((d = getc(f)) == '\n')
				d = getc(f);
			if(d != ')')
				goto error;
			return b;
		case INDIR_TOKEN:
			b->type = BOOLEXP_INDIR;
			b->sub1 = getboolexp1(f);
			if((d = getc(f)) == '\n')
				d = getc(f);
			if(d != ')')
				goto error;
			return b;
		case IS_TOKEN:
			b->type = BOOLEXP_IS;
			b->sub1 = getboolexp1(f);
			if((d = getc(f)) == '\n')
				d = getc(f);
			if(d != ')')
				goto error;
			return b;
		case CARRY_TOKEN:
			b->type = BOOLEXP_CARRY;
			b->sub1 = getboolexp1(f);
			if((d = getc(f)) == '\n')
				d = getc(f);
			if(d != ')')
				goto error;
			return b;
		case OWNER_TOKEN:
			b->type = BOOLEXP_OWNER;
			b->sub1 = getboolexp1(f);
			if((d = getc(f)) == '\n')
				d = getc(f);
			if(d != ')')
				goto error;
			return b;
		default:
			ungetc(c, f);
			b->sub1 = getboolexp1(f);
			if((c = getc(f)) == '\n')
				c = getc(f);
			switch (c) {
			case AND_TOKEN:
				b->type = BOOLEXP_AND;
				break;
			case OR_TOKEN:
				b->type = BOOLEXP_OR;
				break;
			default:
				goto error;
			}
			b->sub2 = getboolexp1(f);
			if((d = getc(f)) == '\n')
				d = getc(f);
			if(d != ')')
				goto error;
			return b;
		}
	case '-':					/*
								 * obsolete NOTHING key, eat it 
								 */
		while ((c = getc(f)) != '\n')
			if(c == EOF)
				abort();		/*
								 * unexp EOF 
								 */
		ungetc(c, f);
		return TRUE_BOOLEXP;
		break;
	case '"':
		ungetc(c, f);
		buff = alloc_lbuf("getboolexp_quoted");
		StringCopy(buff, getstring_noalloc(f, 1));
		c = fgetc(f);
		if(c == EOF) {
			free_lbuf(buff);
			return TRUE_BOOLEXP;
		}

		b = alloc_bool("getboolexp1_quoted");
		anum = mkattr(buff);
		if(anum <= 0) {
			free_bool(b);
			free_lbuf(buff);
			goto error;
		}
		free_lbuf(buff);
		b->thing = anum;

		/*
		 * if last character is : then this is an attribute lock. A 
		 * last character of / means an eval lock 
		 */

		if((c == ':') || (c == '/')) {
			if(c == '/')
				b->type = BOOLEXP_EVAL;
			else
				b->type = BOOLEXP_ATR;
			buff = alloc_lbuf("getboolexp1.attr_lock");
			StringCopy(buff, getstring_noalloc(f, 1));
			b->sub1 = (BOOLEXP *) strsave(buff);
			free_lbuf(buff);
		}
		return b;
	default:					/*
								 * dbref or attribute 
								 */
		ungetc(c, f);
		b = alloc_bool("getboolexp1.default");
		b->type = BOOLEXP_CONST;
		b->thing = 0;

		/*
		 * This is either an attribute, eval, or constant lock.
		 * Constant locks are of the form <num>, while
		 * attribute * and * * * * eval locks are of the form
		 * <anam-or-anum>:<string> or
		 * <aname-or-anum>/<string> respectively. The
		 * characters <nl>, |, and & terminate the string. 
		 */

		if(isdigit(c)) {
			while (isdigit(c = getc(f))) {
				b->thing = b->thing * 10 + c - '0';
			}
		} else if(isalpha(c)) {
			buff = alloc_lbuf("getboolexp1.atr_name");
			for(s = buff;
				((c = getc(f)) != EOF) && (c != '\n') && (c != ':') &&
				(c != '/'); *s++ = c);
			if(c == EOF) {
				free_lbuf(buff);
				free_bool(b);
				goto error;
			}
			*s = '\0';

			/*
			 * Look the name up as an attribute.  If not found,
			 * create a new attribute. 
			 */

			anum = mkattr(buff);
			if(anum <= 0) {
				free_bool(b);
				free_lbuf(buff);
				goto error;
			}
			free_lbuf(buff);
			b->thing = anum;
		} else {
			free_bool(b);
			goto error;
		}

		/*
		 * if last character is : then this is an attribute lock. A 
		 * last character of / means an eval lock 
		 */

		if((c == ':') || (c == '/')) {
			if(c == '/')
				b->type = BOOLEXP_EVAL;
			else
				b->type = BOOLEXP_ATR;
			buff = alloc_lbuf("getboolexp1.attr_lock");
			for(s = buff;
				((c = getc(f)) != EOF) && (c != '\n') && (c != ')') &&
				(c != OR_TOKEN) && (c != AND_TOKEN); *s++ = c);
			if(c == EOF)
				goto error;
			*s++ = 0;
			b->sub1 = (BOOLEXP *) strsave(buff);
			free_lbuf(buff);
		}
		ungetc(c, f);
		return b;
	}

  error:
	abort();					/*
								 * bomb out 
								 */
	return TRUE_BOOLEXP;
}

/*
 * ---------------------------------------------------------------------------
 * * getboolexp: Read a boolean expression from the flat file.
 */

static BOOLEXP *getboolexp(FILE * f)
{
	BOOLEXP *b;
	char c;

	b = getboolexp1(f);
	if(getc(f) != '\n')
		abort();				/*
								 * parse error, we lose 
								 */

	/*
	 * MUSH (except for PernMUSH) and MUSE can have an extra CR, * MUD *
	 * * * * * does not. 
	 */

	if(((g_format == F_MUSH) && (g_version != 2)) || (g_format == F_MUSE)
	   || (g_format == F_MUX)) {
		if((c = getc(f)) != '\n')
			ungetc(c, f);
	}
	return b;
}

/*
 * ---------------------------------------------------------------------------
 * * get_list: Read attribute list from flat file.
 */

static int get_list(FILE * f, dbref i, int new_strings)
{
	dbref atr;
	int c;
	char *buff;

	buff = alloc_lbuf("get_list");
	while (1) {
		switch (c = getc(f)) {
		case '>':				/*
								 * read # then string 
								 */
			atr = getref(f);
			if(atr > 0) {
				/*
				 * Store the attr 
				 */

				atr_add_raw(i, atr, (char *) getstring_noalloc(f,
															   new_strings));
			} else {
				/*
				 * Silently discard 
				 */

				getstring_noalloc(f, new_strings);
			}
			break;
		case '\n':				/*
								 * ignore newlines. They're due to v(r). 
								 */
			break;
		case '<':				/*
								 * end of list 
								 */
			free_lbuf(buff);
			c = getc(f);
			if(c != '\n') {
				ungetc(c, f);
				fprintf(stderr, "No line feed on object %d\n", i);
				return 1;
			}
			return 1;
		default:
			fprintf(stderr,
					"Bad character '%c' when getting attributes on object %d\n",
					c, i);
			/*
			 * We've found a bad spot.  I hope things aren't * *
			 * * * * * too bad. 
			 */

			(void) getstring_noalloc(f, new_strings);
		}
	}
}

/*
 * ---------------------------------------------------------------------------
 * * putbool_subexp: Write a boolean sub-expression to the flat file.
 */
static void putbool_subexp(FILE * f, BOOLEXP * b)
{
	ATTR *va;

	switch (b->type) {
	case BOOLEXP_IS:
		putc('(', f);
		putc(IS_TOKEN, f);
		putbool_subexp(f, b->sub1);
		putc(')', f);
		break;
	case BOOLEXP_CARRY:
		putc('(', f);
		putc(CARRY_TOKEN, f);
		putbool_subexp(f, b->sub1);
		putc(')', f);
		break;
	case BOOLEXP_INDIR:
		putc('(', f);
		putc(INDIR_TOKEN, f);
		putbool_subexp(f, b->sub1);
		putc(')', f);
		break;
	case BOOLEXP_OWNER:
		putc('(', f);
		putc(OWNER_TOKEN, f);
		putbool_subexp(f, b->sub1);
		putc(')', f);
		break;
	case BOOLEXP_AND:
		putc('(', f);
		putbool_subexp(f, b->sub1);
		putc(AND_TOKEN, f);
		putbool_subexp(f, b->sub2);
		putc(')', f);
		break;
	case BOOLEXP_OR:
		putc('(', f);
		putbool_subexp(f, b->sub1);
		putc(OR_TOKEN, f);
		putbool_subexp(f, b->sub2);
		putc(')', f);
		break;
	case BOOLEXP_NOT:
		putc('(', f);
		putc(NOT_TOKEN, f);
		putbool_subexp(f, b->sub1);
		putc(')', f);
		break;
	case BOOLEXP_CONST:
		fprintf(f, "%d", b->thing);
		break;
	case BOOLEXP_ATR:
		va = atr_num(b->thing);
		if(va) {
			fprintf(f, "%s:%s", va->name, (char *) b->sub1);
		} else {
			fprintf(f, "%d:%s\n", b->thing, (char *) b->sub1);
		}
		break;
	case BOOLEXP_EVAL:
		va = atr_num(b->thing);
		if(va) {
			fprintf(f, "%s/%s\n", va->name, (char *) b->sub1);
		} else {
			fprintf(f, "%d/%s\n", b->thing, (char *) b->sub1);
		}
		break;
	default:
		fprintf(stderr, "Unknown boolean type in putbool_subexp: %d\n",
				b->type);
	}
}

/*
 * ---------------------------------------------------------------------------
 * * putboolexp: Write boolean expression to the flat file.
 */

static void putboolexp(FILE * f, BOOLEXP * b)
{
	if(b != TRUE_BOOLEXP) {
		putbool_subexp(f, b);
	}
	putc('\n', f);
}

dbref db_read(FILE * f, int *db_format, int *db_version, int *db_flags)
{
	dbref i, anum;
	char ch;
	const char *tstr;
	int header_gotten, size_gotten, nextattr_gotten;
	int read_attribs, read_name, read_zone, read_link, read_key, read_parent;
	int read_extflags, read_3flags, read_money, read_timestamps,
		read_new_strings;
	int read_powers, read_powers_player, read_powers_any;
	int deduce_version, deduce_name, deduce_zone, deduce_timestamps;
	int aflags, f1, f2, f3;
	BOOLEXP *tempbool;

	header_gotten = 0;
	size_gotten = 0;
	nextattr_gotten = 0;
	g_format = F_UNKNOWN;
	g_version = 0;
	g_flags = 0;
	read_attribs = 1;
	read_name = 1;
	read_zone = 0;
	read_link = 0;
	read_key = 1;
	read_parent = 0;
	read_money = 1;
	read_extflags = 0;
	read_3flags = 0;
	read_timestamps = 0;
	read_new_strings = 0;
	read_powers = 0;
	read_powers_player = 0;
	read_powers_any = 0;
	deduce_version = 1;
	deduce_zone = 1;
	deduce_name = 1;
	deduce_timestamps = 1;
	db_free();
	for(i = 0;; i++) {

		switch (ch = getc(f)) {
		case '-':				/* Misc tag */
			switch (ch = getc(f)) {
			case 'R':			/* Record number of players */
				mudstate.record_players = getref(f);
				break;
			default:
				(void) getstring_noalloc(f, 0);
			}
			break;
		case '+':				/*
								 * MUX and MUSH header 
								 */
			switch (ch = getc(f)) {	/*
									 * 2nd char selects 
									 * type 
									 */
			case 'X':			/*
								 * MUX VERSION 
								 */
				if(header_gotten) {
					fprintf(stderr,
							"\nDuplicate MUX version header entry at object %d, ignored.\n",
							i);
					tstr = getstring_noalloc(f, 0);
					break;
				}
				header_gotten = 1;
				deduce_version = 0;
				g_format = F_MUX;
				g_version = getref(f);

				/*
				 * Otherwise extract feature flags 
				 */

				if(g_version & V_GDBM) {
					read_attribs = 0;
					read_name = !(g_version & V_ATRNAME);
				}
				read_zone = (g_version & V_ZONE);
				read_link = (g_version & V_LINK);
				read_key = !(g_version & V_ATRKEY);
				read_parent = (g_version & V_PARENT);
				read_money = !(g_version & V_ATRMONEY);
				read_extflags = (g_version & V_XFLAGS);
				read_3flags = (g_version & V_3FLAGS);
				read_powers = (g_version & V_POWERS);
				read_new_strings = (g_version & V_QUOTED);
				g_flags = g_version & ~V_MASK;

				g_version &= V_MASK;
				deduce_name = 0;
				deduce_version = 0;
				deduce_zone = 0;
				break;
			case 'S':			/*
								 * SIZE 
								 */
				if(size_gotten) {
					fprintf(stderr,
							"\nDuplicate size entry at object %d, ignored.\n",
							i);
					tstr = getstring_noalloc(f, 0);
				} else {
					mudstate.min_size = getref(f);
				}
				size_gotten = 1;
				break;
			case 'A':			/*
								 * USER-NAMED ATTRIBUTE 
								 */
				anum = getref(f);
				tstr = getstring_noalloc(f, read_new_strings);
				if(isdigit(*tstr)) {
					aflags = 0;
					while (isdigit(*tstr))
						aflags = (aflags * 10) + (*tstr++ - '0');
					tstr++;		/*
								 * skip ':' 
								 */
				} else {
					aflags = mudconf.vattr_flags;
				}
				vattr_define((char *) tstr, anum, aflags);
				break;
			case 'F':			/*
								 * OPEN USER ATTRIBUTE SLOT 
								 */
				anum = getref(f);
				break;
			case 'N':			/*
								 * NEXT ATTR TO ALLOC WHEN NO
								 * FREELIST 
								 */
				if(nextattr_gotten) {
					fprintf(stderr,
							"\nDuplicate next free vattr entry at object %d, ignored.\n",
							i);
					tstr = getstring_noalloc(f, 0);
				} else {
					mudstate.attr_next = getref(f);
					nextattr_gotten = 1;
				}
				break;
			default:
				fprintf(stderr,
						"\nUnexpected character '%c' in MUX header near object #%d, ignored.\n",
						ch, i);
				tstr = getstring_noalloc(f, 0);
			}
			break;
		case '!':				/*
								 * MUX entry/MUSH entry/MUSE non-zoned entry 
								 */
			if(deduce_version) {
				g_format = F_MUX;
				g_version = 1;
				deduce_name = 0;
				deduce_zone = 0;
				deduce_version = 0;
			} else if(deduce_zone) {
				deduce_zone = 0;
				read_zone = 0;
			}
			i = getref(f);
			db_grow(i + 1);

			if(read_name) {
				tstr = getstring_noalloc(f, read_new_strings);
				if(deduce_name) {
					if(isdigit(*tstr)) {
						read_name = 0;
						s_Location(i, atoi(tstr));
					} else {
						s_Name(i, (char *) tstr);
						s_Location(i, getref(f));
					}
					deduce_name = 0;
				} else {
					s_Name(i, (char *) tstr);
					s_Location(i, getref(f));
				}
			} else {
				s_Location(i, getref(f));
			}

			/*
			 * ZONE on MUSE databases and some others 
			 */

			if(read_zone)
				s_Zone(i, getref(f));

			/*
			 * else
			 * * s_Zone(i, NOTHING); 
			 */

			/*
			 * CONTENTS and EXITS 
			 */

			s_Contents(i, getref(f));
			s_Exits(i, getref(f));

			/*
			 * LINK 
			 */

			if(read_link)
				s_Link(i, getref(f));
			else
				s_Link(i, NOTHING);

			/*
			 * NEXT 
			 */

			s_Next(i, getref(f));

			/*
			 * LOCK
			 */

			if(read_key) {
				tempbool = getboolexp(f);
				atr_add_raw(i, A_LOCK, unparse_boolexp_quiet(1, tempbool));
				free_boolexp(tempbool);
			}
			/*
			 * OWNER 
			 */

			s_Owner(i, getref(f));

			/*
			 * PARENT: PennMUSH uses this field for ZONE
			 * (which we  use as PARENT if we
			 * didn't already read in a  
			 * non-NOTHING parent. 
			 */

			if(read_parent) {
				s_Parent(i, getref(f));
			} else {
				s_Parent(i, NOTHING);
			}

			/*
			 * PENNIES 
			 */

			if(read_money)		/*
								 *  if not fix in
								 * unscraw_foreign  
								 */
				s_Pennies(i, getref(f));

			/*
			 * FLAGS 
			 */

			f1 = getref(f);
			if(read_extflags)
				f2 = getref(f);
			else
				f2 = 0;

			if(read_3flags)
				f3 = getref(f);
			else
				f3 = 0;

			s_Flags(i, f1);
			s_Flags2(i, f2);
			s_Flags3(i, f3);

			if(read_powers) {
				f1 = getref(f);
				f2 = getref(f);
				s_Powers(i, f1);
				s_Powers2(i, f2);
			}

			/*
			 * ATTRIBUTES 
			 */

			if(read_attribs) {
				if(!get_list(f, i, read_new_strings)) {
					fprintf(stderr,
							"\nError reading attrs for object #%d\n", i);
					return -1;
				}
			}
			/*
			 * check to see if it's a player 
			 */

			if(Typeof(i) == TYPE_PLAYER) {
				c_Connected(i);
			}
			break;
		case '*':				/*
								 * EOF marker 
								 */
			tstr = getstring_noalloc(f, 0);
			if(strcmp(tstr, "**END OF DUMP***")) {
				fprintf(stderr, "\nBad EOF marker at object #%d\n", i);
				return -1;
			} else {
				/*
				 * Fix up bizarro foreign DBs 
				 */

				*db_version = g_version;
				*db_format = g_format;
				*db_flags = g_flags;
				load_player_names();
				return mudstate.db_top;
			}
		default:
			fprintf(stderr, "\nIllegal character '%c' near object #%d\n",
					ch, i);
			return -1;
		}

	}

}

static int db_write_object(FILE * f, dbref i, int db_format, int flags)
{
	ATTR *a;
	char *got, *as;
	dbref aowner;
	int ca, aflags, save, j;
	BOOLEXP *tempbool;

	if(!(flags & V_ATRNAME))
		putstring(f, Name(i));
	putref(f, Location(i));
	if(flags & V_ZONE)
		putref(f, Zone(i));
	putref(f, Contents(i));
	putref(f, Exits(i));
	if(flags & V_LINK)
		putref(f, Link(i));
	putref(f, Next(i));
	if(!(flags & V_ATRKEY)) {
		got = atr_get(i, A_LOCK, &aowner, &aflags);
		tempbool = parse_boolexp(GOD, got, 1);
		free_lbuf(got);
		putboolexp(f, tempbool);
		if(tempbool)
			free_bool(tempbool);
	}
	putref(f, Owner(i));
	if(flags & V_PARENT)
		putref(f, Parent(i));
	if(!(flags & V_ATRMONEY))
		putref(f, Pennies(i));
	putref(f, Flags(i));
	if(flags & V_XFLAGS)
		putref(f, Flags2(i));
	if(flags & V_3FLAGS)
		putref(f, Flags3(i));
	if(flags & V_POWERS) {
		putref(f, Powers(i));
		putref(f, Powers2(i));
	}
	/*
	 * write the attribute list 
	 */

	if((!(flags & V_GDBM)) || (mudstate.panicking == 1)) {
		for(ca = atr_head(i, &as); ca; ca = atr_next(&as)) {
			save = 0;
			a = atr_num(ca);
			if(a)
				j = a->number;
			else
				j = -1;

			if(j > 0) {
				switch (j) {
				case A_NAME:
					if(flags & V_ATRNAME)
						save = 1;
					break;
				case A_LOCK:
					if(flags & V_ATRKEY)
						save = 1;
					break;
				case A_LIST:
				case A_MONEY:
					break;
				default:
					save = 1;
				}
			}
			if(save) {
				got = atr_get_raw(i, j);
				fprintf(f, ">%d\n", j);
				putstring(f, got);
			}
		}
		fprintf(f, "<\n");
	}
	return 0;
}

dbref db_write(FILE * f, int format, int version)
{
	dbref i;
	int flags;
	VATTR *vp;

	switch (format) {
	case F_MUX:
		flags = version;
		break;
	default:
		fprintf(stderr, "Can only write MUX format.\n");
		return -1;
	}
	i = mudstate.attr_next;
	fprintf(f, "+X%d\n+S%d\n+N%d\n", flags, mudstate.db_top, i);
	fprintf(f, "-R%d\n", mudstate.record_players);

	/*
	 * Dump user-named attribute info 
	 */

	vp = vattr_first();
	while (vp != NULL) {
		if(!(vp->flags & AF_DELETED))
			fprintf(f, "+A%d\n\"%d:%s\"\n", vp->number, vp->flags, vp->name);
		vp = vattr_next(vp);
	}

	DO_WHOLE_DB(i) {

		if(!(Going(i))) {
			fprintf(f, "!%d\n", i);
			db_write_object(f, i, format, flags);
		}
	}
	fputs("***END OF DUMP***\n", f);
	fflush(f);
	return (mudstate.db_top);
}