/* predicates.c */

#include "copyright.h"

#include <varargs.h>
#include <ctype.h>

#ifdef WANT_ANSI
#ifdef __STDC__
#include <stddef.h>
#include <stdlib.h>
#endif /* __STDC__ */
#endif /* WANT_ANSI */

#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "match.h"
#include "command.h"

/* Wouldn't it be neat if this used varargs? */
#ifdef NEVER
char *tprintf(format, a, b, c, d, e, f)
    char *format;
    int a, b, c, d, e, f;
{
  static char buff[10000];
  sprintf(buff, format, a, b, c, d, e, f);
  buff[9999] = 0;
  return (buff);
}
#else
char *tprintf(va_alist)
va_dcl
{
  static char buff[10000];
  va_list ap;
  char *format;

  va_start(ap);
  format = va_arg(ap, char *);
  vsprintf(buff, format, ap);
  va_end(ap);
  buff[9999] = '\0';
  return buff;
}
#endif

/* ---------------------------------------------------------------------------
 * insert_first, remove_first: Insert or remove objects from lists.
 */

dbref insert_first (dbref head, dbref thing)
{
	s_Next(thing, head);
	return thing;
}

dbref remove_first (dbref head, dbref thing)
{
dbref	prev;

	if (head == thing)
		return (Next(thing));

	DOLIST(prev, head) {
		if (Next(prev) == thing) {
			s_Next(prev, Next(thing));
			return head;
		}
	}
	return head;
}

/* ---------------------------------------------------------------------------
 * reverse_list: Reverse the order of members in a list.
 */

dbref reverse_list(dbref list)
{
dbref	newlist, rest;

	newlist = NOTHING;
	while (list != NOTHING) {
		rest = Next(list);
		s_Next(list, newlist);
		newlist = list;
		list = rest;
	}
	return newlist;
}

/* ---------------------------------------------------------------------------
 * member - indicate if thing is in list
 */

int member(dbref thing, dbref list)
{
	DOLIST(list, list) {
		if (list == thing)
			return 1;
	}
	return 0;
}

/* ---------------------------------------------------------------------------
 * is_number: see if string contains just a number.
 */

int is_number (char *str)
{
	while (*str && isspace(*str)) str++;	/* Leading spaces */
	if (*str == '-') {			/* Leading minus */
		str++;
		if (!*str) return 0;		/* but not if just a minus */
	}
	while (*str && isdigit(*str)) str++;	/* The number */
	while (*str && isspace(*str)) str++;	/* Leading spaces */
	return (*str ? 0 : 1);
}

#ifndef STANDALONE

int could_doit(dbref player, dbref thing, int locknum)
{
char	*key;
dbref	aowner;
int	aflags, doit;

	/* no if puppet trys to get key */

	if (Typeof(player) != TYPE_PLAYER) {
		if (IS(thing, TYPE_THING, THING_KEY) ||
		    IS(thing, TYPE_EXIT, EXIT_KEY))
			return 0;
	      }

	if (Typeof(thing) != TYPE_ROOM && Location(thing) == NOTHING)
		return 0;
	key = atr_get(thing, locknum, &aowner, &aflags);
	doit = eval_boolexp_atr(player, thing, key);
	free_lbuf(key);
	return doit;
}

int can_see(dbref player, dbref thing, int can_see_loc)
{
	/* Sleeping players are invisible if configured */

	if ((Typeof(thing) == TYPE_PLAYER) &&
	    !(Flags(thing) & (PLAYER_CONNECT|PUPPET)) &&
	    mudconf.dark_sleepers) {
		return 0;
	}

	/* You don't see yourself or exits */

	if ((player == thing) || (Typeof(thing) == TYPE_EXIT)) {
		return 0;
	}

	/* If loc is not dark, you see it if it's not dark or you control it.
	 * If loc is dark, you see it if you control it.  Seeing your own
	 * dark objects is controlled by mudconf.see_own_dark.
	 */

	if (can_see_loc) {
		return (!Dark(thing) ||
			(mudconf.see_own_dark && Examinable(player, thing)));
	} else {
		return (mudconf.see_own_dark && Examinable(player, thing));
	}
}

static int pay_quota(dbref who, int cost)
{
  dbref aowner;
  int quota, aflags;
  char buf[20], *quota_str;

  /* determine quota */

  quota = atoi(quota_str = atr_get(Owner(who), A_RQUOTA, &aowner, &aflags));
  free_lbuf(quota_str);

  /* enough to build?  Wizards always have enough. */

  quota -= cost;
  if ((quota < 0) && !Wizard(who) && !Wizard(Owner(who)))
    return 0;

  /* dock the quota */
  sprintf(buf, "%d", quota);
  atr_add_raw(who, A_RQUOTA, buf);

  return 1;
}

int canpayfees(dbref player, dbref who, int pennies, int quota)
{
	if (!Wizard(who) && !Wizard(Owner(who)) &&
	    !Immortal(who) && !Immortal(Owner(who)) &&
	    (Pennies(Owner(who)) < pennies)) {
		if (player == who) {
			notify(player,
				tprintf("Sorry, you don't have enough %s.",
					mudconf.many_coins));
		} else {
			notify(player,
				tprintf("Sorry, that player doesn't have enough %s."));
		}
		return 0;
	}

	if(mudconf.quotas) {
		if (!pay_quota(who, quota)) {
			if (player == who) {
				notify(player,
					"Sorry, your building contract has run out.");
			} else {
				notify(player,
					"Sorry, that player's building contract has run out.");
			}
			return 0;
		}
	}
	payfor(who, pennies);
	return 1;
}

int payfor(dbref who, int cost)
{
	dbref tmp;

	if (Wizard(who) || Wizard(Owner(who)) ||
	    Immortal(who) || Immortal(Owner(who))) {
		return 1;
	}

	who = Owner(who);
	if ((tmp = Pennies(who)) >= cost) {
		s_Pennies(who, tmp - cost);
		return 1;
	}

	return 0;
}

#endif	/* STANDALONE */

void add_quota(dbref who, int payment)
{
  dbref aowner;
  int aflags;
  char buf[20], *quota;

  quota = atr_get(who, A_RQUOTA, &aowner, &aflags);
  sprintf(buf, "%d", atoi(quota) + payment);
  free_lbuf(quota);
  atr_add_raw(who, A_RQUOTA, buf);
}

void giveto(dbref who, int pennies)
{
	if (Wizard(who) || Wizard(Owner(who)) ||
	    Immortal(who) || Immortal(Owner(who))) {
		return;
	}

	who = Owner(who);
	s_Pennies(who, Pennies(who) + pennies);
}

int ok_name(const char *name)
{
const char *cp;

	for (cp=name; cp && *cp; cp++) if (!isprint(*cp)) return 0;

	return (name &&
		*name &&
		*name != LOOKUP_TOKEN &&
		*name != NUMBER_TOKEN &&
		*name != NOT_TOKEN &&
		!index(name, ARG_DELIMITER) &&
		!index(name, AND_TOKEN) &&
		!index(name, OR_TOKEN) &&
		string_compare(name, "me") &&
		string_compare(name, "home") &&
		string_compare(name, "here"));
}

int ok_player_name(const char *name)
{
const char *cp;

	if (!ok_name(name) || strlen(name) > PLAYER_NAME_LIMIT)
		return 0;

	for (cp=name; *cp; cp++) if (isspace(*cp)) return 0;
	return 1;
}

int ok_attr_name(const char *attrname)
{
const char *scan;

	if (!isalpha(*attrname)) return 0;
	for (scan=attrname; *scan; scan++) {
		if (isalnum(*scan)) continue;
		if (!(index("-_.@#$^&~=+", *scan))) return 0;
	}
	return 1;
}

int ok_password(const char *password)
{
  const char *scan;
  if (*password == '\0')
    return 0;

  for (scan = password; *scan; scan++) {
    if (!(isprint(*scan) && !isspace(*scan))) {
      return 0;
    }
  }

  /* Needed.  Change it if you like, but be sure yours is the same. */
  if ((strlen(password) == 13) &&
      (password[0] == 'X') &&
      (password[1] == 'X'))
    return 0;

  return 1;
}

#ifndef STANDALONE

/* ---------------------------------------------------------------------------
 * handle_ears: Generate the 'grows ears' and 'loses ears' messages.
 */

void handle_ears (dbref thing, int could_hear, int can_hear)
{
char	*buff, *bp;

	if (!could_hear && can_hear) {
		buff = alloc_lbuf("handle_ears.grow");
		strcpy(buff, Name(thing));
		if (Typeof(thing) == TYPE_EXIT) {
			for (bp=buff; *bp && (*bp!=';'); bp++) ;
			*bp = '\0';
		}
		if (Has_location(thing))
			notify_all(Location(thing), thing, 
				tprintf("%s grows ears and can now hear.",
					buff), 0);
		if (Has_contents(thing))
			notify_all(thing, thing,
				tprintf("%s grows ears and can now hear.",
					buff), 0);
		free_lbuf(buff);
	} else if (could_hear && !can_hear) {
		buff = alloc_lbuf("handle_ears.lose");
		strcpy(buff, Name(thing));
		if (Typeof(thing) == TYPE_EXIT) {
			for (bp=buff; *bp && (*bp!=';'); bp++) ;
			*bp = '\0';
		}
		if (Has_location(thing))
			notify_all(Location(thing), thing,
				tprintf("%s loses its ears and becomes deaf.",
					buff), 0);
		if (Has_contents(thing))
			notify_all(thing, thing,
				tprintf("%s loses its ears and becomes deaf.",
					buff), 0);
		free_lbuf(buff);
	}
}

/* for lack of better place the @switch code is here */

void do_switch (dbref player, dbref cause, int key, char *expr,
	char *args[], int nargs, char *cargs[], int ncargs)
{
int	any, a;
char	*buff;

	any = 0;
	if (!expr || (nargs <= 0))
		return;

	if (key == SWITCH_DEFAULT) {
		if (mudconf.switch_df_all)
			key = SWITCH_ANY;
		else
			key = SWITCH_ONE;
	}

	/* now try a wild card match of buff with stuff in coms */

	for (a=0; (a<(nargs-1)) && args[a] && args[a+1]; a+=2) {
		buff = exec(player, cause, EV_STRIP|EV_FCHECK, args[a],
			cargs, ncargs);
		if (wild_match(buff, expr, (char **)NULL, 0)) {
			if ((any == 0) || (key == SWITCH_ANY))
				wait_que(player, cause, RU_ARG1_COPY,
					0, NOTHING, args[a+1], cargs, ncargs);
			any = 1;
		}
		free_lbuf(buff);
	}
	if ((a < nargs) && !any && args[a])
		wait_que(player, cause, RU_ARG1_COPY, 0, NOTHING,
			args[a], cargs, ncargs);
}

/* ---------------------------------------------------------------------------
 * do_comment: Implement the @@ (comment) command. Very cpu-intensive :-)
 */

void do_comment (dbref player, dbref cause, int key)
{
}

dbref is_possess(dbref player, char *arg1)
{
dbref	result1, result;
char	*buff, *place, *temp, *s1, *d1;

	/* If no space in the string, forget it */

	place = (char *)index(arg1, ' ');
	if (place == NULL)
		return NOTHING;

	/* Look for <name>' and <name>'s.  Return failure if <name> is null. */

	temp = place--;
	temp++;
	if (place == arg1)
		return NOTHING;
	if ((*place != 's') && (*place != 'S') && (*place != '\''))
		return NOTHING;
	if (*place != '\'') {
		place--;
		if ((place == arg1) || (*place != '\''))
			return NOTHING;
	}

	/* Copy the container name to a new buffer so we can terminate it */

	buff = alloc_lbuf("is_posess");
	for (s1=arg1,d1=buff; *s1&&(s1<place); *d1++=(*s1++)) ;
	*d1 = '\0';

	/* Look for the container here and in our inventory */

	init_match(player, buff, NOTYPE);
	match_neighbor();
	match_possession();
	result1 = match_result();
	free_lbuf(buff);
	if (result1 == NOTHING || result1 == AMBIGUOUS)
		return NOTHING;

	/* If we don't control it and it is either dark or opaque, fail */

	if ((Dark(result1) || (Flags(result1) & OPAQUE)) &&
	    !Controls(player, result1))
		return NOTHING;

	/* Look for the object in the container */

	init_match(result1, temp, NOTYPE);
	match_possession();
	result = match_result();
	if (result == AMBIGUOUS)
		result = NOTHING;
	return result;
}

#endif	/* STANDALONE */

/* ---------------------------------------------------------------------------
 * where_is: Returns place where obj is linked into a list.
 * ie. location for players/things, source for exits, NOTHING for rooms.
 */

dbref where_is (dbref what)
{
dbref	loc;

	if (!Good_obj(what))
		return NOTHING;

	switch (Typeof(what)) {
	case TYPE_PLAYER:
	case TYPE_THING:
		loc = Location(what);
		break;
	case TYPE_EXIT:
		loc = Exits(what);
		break;
	default:
		loc = NOTHING;
		break;
	}
	return loc;
}

/* ---------------------------------------------------------------------------
 * nearby: Check if thing is nearby player (in inventory, in same room, or
 * IS the room.
 */

int nearby (dbref player, dbref thing)
{
int	thing_loc, player_loc;

	if (!Good_obj(player) || !Good_obj(thing))
		return 0;
	thing_loc = where_is(thing);
	if (thing_loc == player)
		return 1;
	player_loc = where_is(player);
	if ((thing_loc == player_loc) || (thing == player_loc))
		return 1;
	return 0;
}

/* ---------------------------------------------------------------------------
 * next_exit: return next exit that is ok to see.
 */

dbref next_exit(dbref player, dbref this, int exam_here)
{
	if (Typeof(this) == TYPE_ROOM)
		return NOTHING;
	if ((Typeof(this) != TYPE_EXIT) || exam_here)
		return this;

	while ((this != NOTHING) &&
	       (Flags(this) & DARK) &&
	       !Examinable(player, this))
		this = Next(this);

	return this;
}

#ifndef STANDALONE

/* ---------------------------------------------------------------------------
 * did_it: Do something with an object
 */

void did_it(dbref player, dbref thing, int what, const char *def, int owhat,
	const char *odef, int awhat, char *args[], int nargs)
{
char	*d, *buff, *act, *charges;
dbref	loc, aowner;
int	num, aflags;

	/* message to player */

	if (what) {
		if (*(d = atr_pget(thing, what, &aowner, &aflags))) {
			buff = exec(thing, player, EV_FIGNORE, d, args, nargs);
			notify(player, buff);
			free_lbuf(buff);
		} else if (def) {
			notify(player, def);
		}
		free_lbuf(d);
	}

	/* message to neighbors */

	if (owhat && Has_location(player) &&
	    Good_obj(loc = Location(player))) {
		if (*(d = atr_pget(thing, owhat, &aowner, &aflags))) {
			buff = exec(thing, player, EV_FIGNORE, d, args, nargs);
			notify_except2(loc, player, player, thing,
				tprintf("%s %s", Name(player), buff), 1);
			free_lbuf(buff);
		} else if (odef) {
			notify_except2(loc, player, player, thing,
				tprintf("%s %s", Name(player), odef), 1);
		}
		free_lbuf(d);
	}

	/* do the action attribute */

	if (awhat) {
		if (*(act = atr_pget(thing, awhat, &aowner, &aflags))) {
			charges = atr_pget(thing, A_CHARGES, &aowner, &aflags);
			if (*charges) {
				num = atoi(charges);
				if (num > 0) {
					buff = alloc_sbuf("did_it.charges");
					sprintf(buff, "%d", num - 1);
					atr_add_raw(thing, A_CHARGES, buff);
					free_sbuf(buff);
				} else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner, &aflags))) {
					free_lbuf(act);
					act = buff;
				} else {
					free_lbuf(act);
					free_lbuf(buff);
					free_lbuf(charges);
					return;
				}
			}
			free_lbuf(charges);
			wait_que(thing, player, RU_ARG1_COPY, 0, NOTHING,
				act, args, nargs);
		}
		free_lbuf(act);
	}
}

/* ---------------------------------------------------------------------------
 * do_verb: Command interface to did_it.
 */

void do_verb (dbref player, dbref cause, int key, char *actor_str,
	char *args[], int nargs)
{
dbref	actor;
int	what, owhat, awhat, nxargs;
ATTR	*ap;
const char *whatd, *owhatd;
char	*xargs[10];

	/* Look for the actor */

	if (!actor_str || !*actor_str) {
		notify(player, "Nothing to do.");
		return;
	}
	actor = match_controlled(player, actor_str);
	if (!Good_obj(actor))
		return;

	/* Get the invoker.  Default to my invoker.  Player must control. */

	if ((nargs >= 1) && args[0] && *args[0]) {
		cause = match_controlled(player, args[0]);
	} else if (!Controls(player, cause)) {
		notify(player, "Permission denied.");
		return;
	}
	if (!Good_obj(cause))
		return;

	what = 0;
	owhat = 0;
	awhat = 0;
	whatd = NULL;
	owhatd = NULL;
	nxargs = 0;

	/* Get invoker message attribute */

	if (nargs >= 2) {
		ap = atr_str(args[1]);
		if (ap)
			what = ap->number;
	}

	/* Get invoker message default */

	if ((nargs >= 3) && args[2] && *args[2]) {
		whatd = args[2];
	}

	/* Get others message attribute */

	if (nargs >= 4) {
		ap = atr_str(args[3]);
		if (ap)
			owhat = ap->number;
	}

	/* Get others message default */

	if ((nargs >= 5) && args[4] && *args[4]) {
		owhatd = args[4];
	}

	/* Get action attribute */

	if (nargs >= 6) {
		ap = atr_str(args[5]);
		if (ap)
			awhat = ap->number;
	}

	/* Get arguments */

	if (nargs >= 7) {
		parse_arglist(actor, cause, args[6], '\0',
			EV_STRIP_LS|EV_STRIP_TS, xargs, 10, (char **)NULL, 0);
		for (nxargs=0; (nxargs<10) && xargs[nxargs]; nxargs++) ;
	}

	/* Go do it */

	did_it(cause, actor, what, whatd, owhat, owhatd, awhat,
		xargs, nxargs);
}
		
/* ---------------------------------------------------------------------------
 * Buffer pool routines - faster allocate/free for fixed-size buffers
 */

#define POOL_MAGICNUM 0xdeadbeef

void pool_init(POOL *poolp, int poolsize)
{
	poolp->pool_size = poolsize;
	poolp->free_head = NULL;
	poolp->chain_head = NULL;
	poolp->tot_alloc = 0;
	poolp->num_alloc = 0;
	poolp->max_alloc = 0;
	poolp->num_lost = 0;
	return;
}

#ifdef PARANOID_ALLOC

static void pool_vfy (POOL *poolp, const char *tag)
{
POOLHDR *ph;

	for (ph=poolp->chain_head; ph; ph=ph->next) {
		if (ph->magicnum != POOL_MAGICNUM) {
			if (mudstate.logging == 0) {
				STARTLOG(LOG_ALWAYS,"BUG","ALLOC")
					sprintf(mudstate.buffer,
						"Buffer[%d] consistency check at %x, tag '%s'";
						poolp->pool_size, ph, tag);
					log_text(mudstate.buffer);
				ENDLOG
			else {
					sprintf(mudstate.buffer,
						"***< Buffer[%d] consistency check at %x, tag '%s' >***";
						poolp->pool_size, ph, tag);
					log_text(mudstate.buffer);
			}
		}
	}
}

void pool_check(const char *tag)
{
	pool_vfy(&mudstate.lbuf_pool, tag);
	pool_vfy(&mudstate.mbuf_pool, tag);
	pool_vfy(&mudstate.sbuf_pool, tag);
	pool_vfy(&mudstate.bool_pool, tag);
	pool_vfy(&mudstate.desc_pool, tag);
	pool_vfy(&mudstate.qentry_pool, tag);
}
#endif

char *pool_alloc(POOL *poolp, const char *tag)
{
int	*p;
char	*h;
POOLHDR	*ph;

#ifdef PARANOID_ALLOC
	pool_check(tag);
#endif
	do {
		if (poolp->free_head == NULL) {
			h = (char *)malloc(poolp->pool_size + sizeof(POOLHDR));
			if (h == NULL) abort();
			ph = (POOLHDR *)h;
			ph->next = poolp->chain_head;
			ph->nxtfree = NULL;
			ph->magicnum = POOL_MAGICNUM;
			poolp->chain_head = ph;
			poolp->max_alloc++;
			p = (int *)(h + sizeof(POOLHDR));
			*p = POOL_MAGICNUM;
		} else {
			ph = (POOLHDR *)(poolp->free_head);
			h = (char *)ph;
			poolp->free_head = ph->nxtfree;
			p = (int *)(h + sizeof(POOLHDR));
			if (ph->magicnum != POOL_MAGICNUM) {

				/* New buffer is corrupted.  Log it always. */

				if (!mudstate.logging) {
					STARTLOG(LOG_ALWAYS,"BUG","ALLOC")
						sprintf(mudstate.buffer,
							"Alloc[%d] (tag %s) got corrupted buffer at %x, clearing freelist.",
							poolp->pool_size,
							tag, ph);
						log_text(mudstate.buffer);
					ENDLOG
				} else {
					sprintf(mudstate.buffer,
						"***< Alloc[%d] (tag %s) got corrupted buffer at %x, clearing freelist. >***",
						poolp->pool_size, tag, ph);
					log_text(mudstate.buffer);
				}

				/* Start a new free list and record stats */

				p = NULL;
				poolp->free_head = NULL;
				poolp->num_lost +=
					(poolp->tot_alloc - poolp->num_alloc);
				poolp->tot_alloc = poolp->num_alloc;
			}
		}
	} while (p == NULL);

	ph->buf_tag = (char *)tag;
	poolp->tot_alloc++;
	poolp->num_alloc++;

	/* Log if requested.  Only try to log if we are not logging, because
	 * there are often alloc/free pairs within a STARTLOG/ENDLOG pair,
	 * and attemting to log these would generate a recursive logging
	 * error. */

	if (!mudstate.logging) {
		STARTLOG(LOG_ALLOCATE,"DBG","ALLOC")
			sprintf(mudstate.buffer, "Allocate[%d] at %x by %s",
				poolp->pool_size, ph, tag);
			log_text(mudstate.buffer);
		ENDLOG
	}

	/* If the buffer was modified after it was last freed, log it. */

	if ((*p != POOL_MAGICNUM) && (!mudstate.logging)) {
		STARTLOG(LOG_PROBLEMS,"BUG","ALLOC")
			sprintf(mudstate.buffer,
				"Alloc[%d] (tag %s) buffer at %x modified after free.",
				poolp->pool_size, tag, ph);
			log_text(mudstate.buffer);
		ENDLOG
	}

	return (char *)p;
}

void pool_free(POOL *poolp, char **buf)
{
int	*ibuf;
char	*h;
POOLHDR	*ph;

	ibuf = (int *)*buf;
	h = (char *)ibuf;
	h -= sizeof(POOLHDR);
	ph = (POOLHDR *)h;
#ifdef PARANOID_ALLOC
	pool_check(ph->buf_tag);
#endif
	*ibuf = POOL_MAGICNUM;

	/* Make sure the buffer header is good.  If it isn't, log the error and
	 * throw away the buffer. */

	if (ph->magicnum != POOL_MAGICNUM) {
		if (!mudstate.logging) {
			STARTLOG(LOG_ALWAYS,"BUG","ALLOC")
				sprintf(mudstate.buffer,
					"Free[%d] buffer header corrupted at %x.",
					poolp->pool_size, ph);
				log_text(mudstate.buffer);
			ENDLOG
		} else {
			sprintf(mudstate.buffer,
				"***< Free[%d] buffer header corrupted at %x. >***",
				poolp->pool_size, ph);
			log_text(mudstate.buffer);
		}
		poolp->num_lost++;
		poolp->num_alloc--;
		poolp->tot_alloc--;
		return;
	}

	/* Log if requested.  Only try to log if we are not logging, because
	 * there are often alloc/free pairs within a STARTLOG/ENDLOG pair,
	 * and attemting to log these would generate a recursive logging
	 * error. */

	if (!mudstate.logging) {
		STARTLOG(LOG_ALLOCATE,"DBG","ALLOC")
			sprintf(mudstate.buffer, "Free[%d] at %x (alloc by %s)",
				poolp->pool_size, ph, ph->buf_tag);
			log_text(mudstate.buffer);
		ENDLOG
	}

	/* Make sure we aren't freeing an already free buffer.  If we are,
	 * log an error, otherwise update the pool header and stats 
	 */

	if (ph->buf_tag == NULL) {
		if (!mudstate.logging) {
			STARTLOG(LOG_BUGS,"BUG","ALLOC")
				sprintf(mudstate.buffer,
					"Free[%d] of already free block at %x",
					poolp->pool_size, ph);
				log_text(mudstate.buffer);
			ENDLOG
		}
	} else {
		ph->nxtfree = poolp->free_head;
		poolp->free_head = ph;
		poolp->num_alloc--;
		ph->buf_tag = NULL;
	}
}

static char *pool_stats(POOL *poolp, const char *text)
{
char	*buf;
	buf = alloc_mbuf("pool_stats");
	sprintf(buf, "%-15s %5d%9d%9d%9d%9d", text, poolp->pool_size,
		poolp->num_alloc, poolp->max_alloc, poolp->tot_alloc,
		poolp->num_lost);
	return buf;
}

static void pool_trace(dbref player, POOL *poolp, const char *text)
{
POOLHDR	*ph;
int	numfree;

	numfree = 0;
	notify(player, tprintf("----- %s -----", text));
	for (ph=poolp->chain_head; ph!=NULL; ph=ph->next) {
		if (ph->magicnum != POOL_MAGICNUM) {
			notify(player,"*** CORRUPTED BUFFER HEADER, ABORTING SCAN ***");
			notify(player,
				tprintf("%d free %s (before corruption)",
					numfree, text));
			return;
		}
		if (ph->buf_tag != NULL)
			notify(player, ph->buf_tag);
		else
			numfree++;
	}
	notify(player, tprintf("%d free %s", numfree, text));
}

static void list_bufstat(dbref player, POOL *poolp, const char *pool_name)
{
char	*buff;

	buff = pool_stats(poolp, pool_name);
	notify(player, buff);
	free_mbuf(buff);
}

void list_bufstats(dbref player)
{
	notify(player, "Buffer Stats     Size    InUse    Total   Allocs     Lost");
	list_bufstat(player, &mudstate.lbuf_pool, "Lbufs");
	list_bufstat(player, &mudstate.mbuf_pool, "Mbufs");
	list_bufstat(player, &mudstate.sbuf_pool, "Sbufs");
	list_bufstat(player, &mudstate.bool_pool, "Bools");
	list_bufstat(player, &mudstate.desc_pool, "Descs");
	list_bufstat(player, &mudstate.qentry_pool, "Qentries");
}

void list_buftrace(dbref player)
{
	pool_trace(player,&mudstate.lbuf_pool, "Lbufs");
	pool_trace(player,&mudstate.mbuf_pool, "Mbufs");
	pool_trace(player,&mudstate.sbuf_pool, "Sbufs");
	pool_trace(player,&mudstate.bool_pool, "Bools");
	pool_trace(player,&mudstate.desc_pool, "Descs");
	pool_trace(player,&mudstate.qentry_pool, "Qentries");
}

#endif	/* STANDALONE */