ds2.9a12/bin/
ds2.9a12/extra/
ds2.9a12/extra/crat/
ds2.9a12/extra/creremote/
ds2.9a12/extra/mingw/
ds2.9a12/extra/wolfpaw/
ds2.9a12/fluffos-2.14-ds13/
ds2.9a12/fluffos-2.14-ds13/Win32/
ds2.9a12/fluffos-2.14-ds13/compat/
ds2.9a12/fluffos-2.14-ds13/compat/simuls/
ds2.9a12/fluffos-2.14-ds13/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/
ds2.9a12/fluffos-2.14-ds13/testsuite/clone/
ds2.9a12/fluffos-2.14-ds13/testsuite/command/
ds2.9a12/fluffos-2.14-ds13/testsuite/data/
ds2.9a12/fluffos-2.14-ds13/testsuite/etc/
ds2.9a12/fluffos-2.14-ds13/testsuite/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/master/
ds2.9a12/fluffos-2.14-ds13/testsuite/log/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/compiler/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/efuns/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/operators/
ds2.9a12/fluffos-2.14-ds13/testsuite/u/
ds2.9a12/lib/cmds/admins/
ds2.9a12/lib/cmds/common/
ds2.9a12/lib/cmds/creators/include/
ds2.9a12/lib/daemon/services/
ds2.9a12/lib/daemon/tmp/
ds2.9a12/lib/doc/
ds2.9a12/lib/doc/bguide/
ds2.9a12/lib/doc/efun/all/
ds2.9a12/lib/doc/efun/arrays/
ds2.9a12/lib/doc/efun/buffers/
ds2.9a12/lib/doc/efun/compile/
ds2.9a12/lib/doc/efun/floats/
ds2.9a12/lib/doc/efun/functions/
ds2.9a12/lib/doc/efun/general/
ds2.9a12/lib/doc/efun/mixed/
ds2.9a12/lib/doc/efun/numbers/
ds2.9a12/lib/doc/efun/parsing/
ds2.9a12/lib/doc/hbook/
ds2.9a12/lib/doc/help/classes/
ds2.9a12/lib/doc/help/races/
ds2.9a12/lib/doc/lfun/
ds2.9a12/lib/doc/lfun/all/
ds2.9a12/lib/doc/lfun/lib/abilities/
ds2.9a12/lib/doc/lfun/lib/armor/
ds2.9a12/lib/doc/lfun/lib/bank/
ds2.9a12/lib/doc/lfun/lib/bot/
ds2.9a12/lib/doc/lfun/lib/clay/
ds2.9a12/lib/doc/lfun/lib/clean/
ds2.9a12/lib/doc/lfun/lib/clerk/
ds2.9a12/lib/doc/lfun/lib/client/
ds2.9a12/lib/doc/lfun/lib/combat/
ds2.9a12/lib/doc/lfun/lib/connect/
ds2.9a12/lib/doc/lfun/lib/container/
ds2.9a12/lib/doc/lfun/lib/corpse/
ds2.9a12/lib/doc/lfun/lib/creator/
ds2.9a12/lib/doc/lfun/lib/daemon/
ds2.9a12/lib/doc/lfun/lib/damage/
ds2.9a12/lib/doc/lfun/lib/deterioration/
ds2.9a12/lib/doc/lfun/lib/donate/
ds2.9a12/lib/doc/lfun/lib/door/
ds2.9a12/lib/doc/lfun/lib/equip/
ds2.9a12/lib/doc/lfun/lib/file/
ds2.9a12/lib/doc/lfun/lib/fish/
ds2.9a12/lib/doc/lfun/lib/fishing/
ds2.9a12/lib/doc/lfun/lib/flashlight/
ds2.9a12/lib/doc/lfun/lib/follow/
ds2.9a12/lib/doc/lfun/lib/ftp_client/
ds2.9a12/lib/doc/lfun/lib/ftp_data_connection/
ds2.9a12/lib/doc/lfun/lib/fuel/
ds2.9a12/lib/doc/lfun/lib/furnace/
ds2.9a12/lib/doc/lfun/lib/genetics/
ds2.9a12/lib/doc/lfun/lib/holder/
ds2.9a12/lib/doc/lfun/lib/id/
ds2.9a12/lib/doc/lfun/lib/interactive/
ds2.9a12/lib/doc/lfun/lib/lamp/
ds2.9a12/lib/doc/lfun/lib/leader/
ds2.9a12/lib/doc/lfun/lib/light/
ds2.9a12/lib/doc/lfun/lib/limb/
ds2.9a12/lib/doc/lfun/lib/living/
ds2.9a12/lib/doc/lfun/lib/load/
ds2.9a12/lib/doc/lfun/lib/look/
ds2.9a12/lib/doc/lfun/lib/manipulate/
ds2.9a12/lib/doc/lfun/lib/meal/
ds2.9a12/lib/doc/lfun/lib/messages/
ds2.9a12/lib/doc/lfun/lib/player/
ds2.9a12/lib/doc/lfun/lib/poison/
ds2.9a12/lib/doc/lfun/lib/position/
ds2.9a12/lib/doc/lfun/lib/post_office/
ds2.9a12/lib/doc/lfun/lib/potion/
ds2.9a12/lib/doc/lfun/lib/room/
ds2.9a12/lib/doc/lfun/lib/server/
ds2.9a12/lib/doc/lfun/lib/spell/
ds2.9a12/lib/doc/lfun/lib/torch/
ds2.9a12/lib/doc/lfun/lib/vendor/
ds2.9a12/lib/doc/lfun/lib/virt_sky/
ds2.9a12/lib/doc/lfun/lib/weapon/
ds2.9a12/lib/doc/lfun/lib/worn_storage/
ds2.9a12/lib/doc/lpc/basic/
ds2.9a12/lib/doc/lpc/concepts/
ds2.9a12/lib/doc/lpc/constructs/
ds2.9a12/lib/doc/lpc/etc/
ds2.9a12/lib/doc/lpc/intermediate/
ds2.9a12/lib/doc/lpc/types/
ds2.9a12/lib/doc/misc/
ds2.9a12/lib/doc/old/
ds2.9a12/lib/domains/
ds2.9a12/lib/domains/Praxis/adm/
ds2.9a12/lib/domains/Praxis/attic/
ds2.9a12/lib/domains/Praxis/cemetery/mon/
ds2.9a12/lib/domains/Praxis/data/
ds2.9a12/lib/domains/Praxis/death/
ds2.9a12/lib/domains/Praxis/mountains/
ds2.9a12/lib/domains/Praxis/obj/armour/
ds2.9a12/lib/domains/Praxis/obj/magic/
ds2.9a12/lib/domains/Praxis/obj/weapon/
ds2.9a12/lib/domains/Praxis/orc_valley/
ds2.9a12/lib/domains/Ylsrim/
ds2.9a12/lib/domains/Ylsrim/adm/
ds2.9a12/lib/domains/Ylsrim/armor/
ds2.9a12/lib/domains/Ylsrim/broken/
ds2.9a12/lib/domains/Ylsrim/fish/
ds2.9a12/lib/domains/Ylsrim/meal/
ds2.9a12/lib/domains/Ylsrim/npc/
ds2.9a12/lib/domains/Ylsrim/obj/
ds2.9a12/lib/domains/Ylsrim/virtual/
ds2.9a12/lib/domains/Ylsrim/weapon/
ds2.9a12/lib/domains/campus/adm/
ds2.9a12/lib/domains/campus/etc/
ds2.9a12/lib/domains/campus/meals/
ds2.9a12/lib/domains/campus/save/
ds2.9a12/lib/domains/campus/txt/ai/charles/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds2.9a12/lib/domains/campus/txt/ai/charly/
ds2.9a12/lib/domains/campus/txt/ai/charly/bak/
ds2.9a12/lib/domains/campus/txt/jenny/
ds2.9a12/lib/domains/cave/doors/
ds2.9a12/lib/domains/cave/etc/
ds2.9a12/lib/domains/cave/meals/
ds2.9a12/lib/domains/cave/weap/
ds2.9a12/lib/domains/default/creator/
ds2.9a12/lib/domains/default/doors/
ds2.9a12/lib/domains/default/etc/
ds2.9a12/lib/domains/default/vehicles/
ds2.9a12/lib/domains/default/virtual/
ds2.9a12/lib/domains/default/weap/
ds2.9a12/lib/domains/town/txt/shame/
ds2.9a12/lib/domains/town/virtual/
ds2.9a12/lib/domains/town/virtual/bottom/
ds2.9a12/lib/domains/town/virtual/space/
ds2.9a12/lib/estates/
ds2.9a12/lib/ftp/
ds2.9a12/lib/lib/comp/
ds2.9a12/lib/lib/daemons/
ds2.9a12/lib/lib/daemons/include/
ds2.9a12/lib/lib/lvs/
ds2.9a12/lib/lib/user/
ds2.9a12/lib/lib/virtual/
ds2.9a12/lib/log/
ds2.9a12/lib/log/adm/
ds2.9a12/lib/log/archive/
ds2.9a12/lib/log/chan/
ds2.9a12/lib/log/errors/
ds2.9a12/lib/log/law/adm/
ds2.9a12/lib/log/law/email/
ds2.9a12/lib/log/law/names/
ds2.9a12/lib/log/law/sites-misc/
ds2.9a12/lib/log/law/sites-register/
ds2.9a12/lib/log/law/sites-tempban/
ds2.9a12/lib/log/law/sites-watch/
ds2.9a12/lib/log/open/
ds2.9a12/lib/log/reports/
ds2.9a12/lib/log/router/
ds2.9a12/lib/log/secure/
ds2.9a12/lib/log/watch/
ds2.9a12/lib/obj/book_source/
ds2.9a12/lib/obj/include/
ds2.9a12/lib/powers/prayers/
ds2.9a12/lib/powers/spells/
ds2.9a12/lib/realms/template/adm/
ds2.9a12/lib/realms/template/area/armor/
ds2.9a12/lib/realms/template/area/npc/
ds2.9a12/lib/realms/template/area/obj/
ds2.9a12/lib/realms/template/area/room/
ds2.9a12/lib/realms/template/area/weap/
ds2.9a12/lib/realms/template/bak/
ds2.9a12/lib/realms/template/cmds/
ds2.9a12/lib/save/kills/o/
ds2.9a12/lib/secure/cfg/classes/
ds2.9a12/lib/secure/cmds/builders/
ds2.9a12/lib/secure/cmds/creators/include/
ds2.9a12/lib/secure/cmds/players/
ds2.9a12/lib/secure/cmds/players/include/
ds2.9a12/lib/secure/daemon/imc2server/
ds2.9a12/lib/secure/daemon/include/
ds2.9a12/lib/secure/lib/
ds2.9a12/lib/secure/lib/include/
ds2.9a12/lib/secure/lib/net/include/
ds2.9a12/lib/secure/lib/std/
ds2.9a12/lib/secure/log/adm/
ds2.9a12/lib/secure/log/bak/
ds2.9a12/lib/secure/log/intermud/
ds2.9a12/lib/secure/log/network/
ds2.9a12/lib/secure/modules/
ds2.9a12/lib/secure/npc/
ds2.9a12/lib/secure/obj/include/
ds2.9a12/lib/secure/room/
ds2.9a12/lib/secure/save/
ds2.9a12/lib/secure/save/backup/
ds2.9a12/lib/secure/save/boards/
ds2.9a12/lib/secure/tmp/
ds2.9a12/lib/secure/upgrades/files/
ds2.9a12/lib/secure/verbs/creators/
ds2.9a12/lib/std/board/
ds2.9a12/lib/std/lib/
ds2.9a12/lib/tmp/
ds2.9a12/lib/verbs/admins/include/
ds2.9a12/lib/verbs/builders/
ds2.9a12/lib/verbs/common/
ds2.9a12/lib/verbs/common/include/
ds2.9a12/lib/verbs/creators/
ds2.9a12/lib/verbs/creators/include/
ds2.9a12/lib/verbs/rooms/
ds2.9a12/lib/verbs/rooms/include/
ds2.9a12/lib/www/client/
ds2.9a12/lib/www/errors/
ds2.9a12/lib/www/images/
ds2.9a12/lib/www/lpmuds/downloads_files/
ds2.9a12/lib/www/lpmuds/intermud_files/
ds2.9a12/lib/www/lpmuds/links_files/
ds2.9a12/win32/
/*
 * mudlib_stats.c
 * created by: Erik Kay
 * last modified: 11/1/92
 * this file is a replacement for wiz_list.c and all of its purposes
 * the idea is that it will be more domain based, rather than user based
 * and will be a little more general purpose than wiz_list was
 */

#ifdef LATTICE
#include "/lpc_incl.h"
#include "/backend.h"
#include "/md.h"
#include "/master.h"
#else
#include "../lpc_incl.h"
#include "../backend.h"
#include "../md.h"
#include "../master.h"
#include "../efun_protos.h"
#endif

#include "mudlib_stats.h"

#ifdef F_DOMAIN_STATS
void
f_domain_stats (void)
{
    mapping_t *m;

    if (st_num_arg) {
        m = get_domain_stats(sp->u.string);
        free_string_svalue(sp--);
    } else {
        m = get_domain_stats(0);
    }
    if (!m) {
        push_number(0);
    } else {
        /* ref count is properly decremented by get_domain_stats */
        push_mapping(m);
    }
}
#endif

#ifdef F_SET_AUTHOR
void
f_set_author (void)
{
    set_author(sp->u.string);
    free_string_svalue(sp--);
}
#endif

#ifdef F_AUTHOR_STATS
void
f_author_stats (void)
{
    mapping_t *m;

    if (st_num_arg) {
        m = get_author_stats(sp->u.string);
        free_string_svalue(sp--);
    } else {
        m = get_author_stats(0);
    }
    if (!m) {
        push_number(0);
    } else {
        /* ref count is properly decremented by get_author_stats */
        push_mapping(m);
    }
}
#endif

/* Support functions */
static mudlib_stats_t *domains = 0;
static mudlib_stats_t *backbone_domain = 0;
static mudlib_stats_t *authors = 0;
static mudlib_stats_t *master_author = 0;

static mudlib_stats_t *find_stat_entry (const char *, mudlib_stats_t *);
static mudlib_stats_t *add_stat_entry (const char *, mudlib_stats_t **);
static void init_author_for_ob (object_t *);
static const char *author_for_file (const char *);
static void init_domain_for_ob (object_t *);
static const char *domain_for_file (const char *);
static void save_stat_list (const char *, mudlib_stats_t *);
static void restore_stat_list (const char *, mudlib_stats_t **);
static mapping_t *get_info (mudlib_stats_t *);
static mapping_t *get_stats (const char *, mudlib_stats_t *);
static mudlib_stats_t *insert_stat_entry (mudlib_stats_t *, mudlib_stats_t **);

#ifdef DEBUGMALLOC_EXTENSIONS
/* debugging */
int check_valid_stat_entry (mudlib_stats_t * se) {
    mudlib_stats_t *tmp;

    tmp = domains;
    while (tmp) {
	if (tmp == se) return 1;
	tmp = tmp->next;
    }
    tmp = authors;
    while (tmp) {
	if (tmp == se) return 1;
	tmp = tmp->next;
    }
    return 0;
}
#endif

/**************************
 * stat list manipulation
 **************************/

static mudlib_stats_t *insert_stat_entry (mudlib_stats_t * entry, mudlib_stats_t ** list)
{
    entry->next = *list;
    *list = entry;
    return *list;
}

/*
 * Return the data for an individual domain, if it exists.
 * this uses a simple linear search.  it's a good thing that most muds
 * will have a relatively small number of domains
 */
static mudlib_stats_t *find_stat_entry (const char * name, mudlib_stats_t * list)
{
    int length;

    length = strlen(name);
    for (; list; list = list->next)
	if (list->length == length && strcmp(list->name, name) == 0)
	    return list;
    return 0;
}

/*
 * add a new domain to the domain list.  If it exists, do nothing.
 */
static mudlib_stats_t *add_stat_entry (const char * str, mudlib_stats_t ** list)
{
    mudlib_stats_t *entry;

    if ((entry = find_stat_entry(str, *list)))
	return entry;
    entry = ALLOCATE(mudlib_stats_t, TAG_MUDLIB_STATS, "add_stat_entry");
    entry->name = make_shared_string(str);
    entry->length = strlen(str);
    entry->moves = 0;
    entry->heart_beats = 0;
    entry->errors = 0;
    entry->objects = 0;
    entry->next = NULL;
    entry->size_array = 0;
    insert_stat_entry(entry, list);
    return entry;
}


/*************************************
 * general stat modifying accessor functions
 **************************************/


void assign_stats (statgroup_t * st, object_t * ob)
{
    st->domain = ob->stats.domain;
    st->author = ob->stats.author;
}

void null_stats (statgroup_t * st)
{
    if (st) {
	st->domain = NULL;
	st->author = NULL;
    }
}

void init_stats_for_object (object_t * ob)
{
    init_domain_for_ob(ob);
    init_author_for_ob(ob);
}


/*
 * Add moves to an existing domain.
 */
void add_moves (statgroup_t * st, int moves)
{
    if (st) {
	if (st->domain)
	    st->domain->moves += moves;
	if (st->author)
	    st->author->moves += moves;
    }
}

INLINE void add_heart_beats (statgroup_t * st, int hbs)
{
    if (st) {
	if (st->domain)
	    st->domain->heart_beats += hbs;
	if (st->author)
	    st->author->heart_beats += hbs;
    }
}

void add_array_size (statgroup_t * st, int size)
{
    if (st) {
	if (st->domain)
	    st->domain->size_array += size;
	if (st->author)
	    st->author->size_array += size;
    }
}

void add_errors (statgroup_t * st, int errors)
{
    if (st) {
	if (st->domain)
	    st->domain->errors += errors;
	if (st->author)
	    st->author->errors += errors;
    }
}

void add_errors_for_file (const char * file, int errors)
{
    mudlib_stats_t *entry;
    const char *name;

    name = domain_for_file(file);
    if (name && domains) {
	entry = find_stat_entry(name, domains);
	if (entry)
	    entry->errors += errors;
    }
    name = author_for_file(file);
    if (name && authors) {
	entry = find_stat_entry(name, authors);
	if (entry)
	    entry->errors += errors;
    }
}

void add_objects (statgroup_t * st, int objects)
{
    if (st) {
	if (st->domain)
	    st->domain->objects += objects;
	if (st->author)
	    st->author->objects += objects;
    }
}

/*
 * Basically the "scores" are averaged over time by having them decay
 * gradually at each reset.
 * Here's how the decay breaks down:
 *    moves -= 1%
 *    heart_beats -= 10%
 */
void mudlib_stats_decay()
{
    mudlib_stats_t *dl;
    static int next_time;

    /* Perform this once every hour. */
    if (next_time > current_time)
	return;
    next_time = current_time + 60 * 60;
    for (dl = domains; dl; dl = dl->next) {
	dl->moves = dl->moves * 99 / 100;
	dl->heart_beats = dl->heart_beats * 9 / 10;
    }
    for (dl = authors; dl; dl = dl->next) {
	dl->moves = dl->moves * 99 / 100;
	dl->heart_beats = dl->heart_beats * 9 / 10;
    }
}

#ifdef DEBUGMALLOC_EXTENSIONS
void mark_mudlib_stats() {
    mudlib_stats_t *dl;

    for (dl = domains; dl; dl = dl->next) {
	DO_MARK(dl, TAG_MUDLIB_STATS);
	EXTRA_REF(BLOCK(dl->name))++;
    }
    for (dl = authors; dl; dl = dl->next) {
	DO_MARK(dl, TAG_MUDLIB_STATS);
	EXTRA_REF(BLOCK(dl->name))++;
    }
}
#endif

/*************************
 Author specific functions
 *************************/

static void init_author_for_ob (object_t * ob)
{
    svalue_t *ret;

    push_malloced_string(add_slash(ob->obname));
    ret = apply_master_ob(APPLY_AUTHOR_FILE, 1);
    if (ret == (svalue_t *)-1) {
	ob->stats.author = master_author;
    } else if (!ret || ret->type != T_STRING) {
	ob->stats.author = NULL;
    } else {
	ob->stats.author = add_stat_entry(ret->u.string, &authors);
    }
}

void set_author (const char *name)
{
    object_t *ob;

    if (!current_object)
	return;
    ob = current_object;
    if (master_ob == (object_t *)-1) {
	ob->stats.author = NULL;
	return;
    }
    if (ob->stats.author) {
	ob->stats.author->objects--;
    }
    ob->stats.author = add_stat_entry(name, &authors);
    if (ob->stats.author) {
	ob->stats.author->objects++;
    }
}

mudlib_stats_t *set_master_author (const char * str)
{
    mudlib_stats_t *author;

    author = add_stat_entry(str, &authors);
    if (author)
	master_author = author;
    return author;
}

static const char *author_for_file (const char * file)
{
    svalue_t *ret;
    static char buff[50];

    copy_and_push_string(file);
    ret = apply_master_ob(APPLY_AUTHOR_FILE, 1);
    if (ret == 0 || ret == (svalue_t*)-1 || ret->type != T_STRING)
	return 0;
    strcpy(buff, ret->u.string);
    return buff;
}


/*************************
 Domain specific functions
 *************************/

static void init_domain_for_ob (object_t * ob)
{
    svalue_t *ret;
    const char *domain_name;

    if (!current_object
#ifdef PACKAGE_UIDS
	|| !current_object->uid
#endif
	) {
	/*
	 * Only for the master and void object. Note that you can't ask for
	 * the backbone or root domain here since we're in the process of
	 * loading the master object.
	 */
	ob->stats.domain = add_stat_entry("NONAME", &domains);
	return;
    }
    /*
     * Ask master object who the creator of this object is.
     */
    push_malloced_string(add_slash(ob->obname));

    if (master_ob)
	ret = apply_master_ob(APPLY_DOMAIN_FILE, 1);
    else
	ret = apply(applies_table[APPLY_DOMAIN_FILE], ob, 1, ORIGIN_DRIVER);

    if (IS_ZERO(ret)) {
	ob->stats.domain = current_object->stats.domain;
	return;
    }
    if (ret->type != T_STRING)
	error("'domain_file' in the master object must return a string!\n");
    domain_name = ret->u.string;
    if (strcmp(current_object->stats.domain->name, domain_name) == 0) {
	ob->stats.domain = current_object->stats.domain;
	return;
    }
    if (strcmp(backbone_domain->name, domain_name) == 0) {
	/*
	 * The object is loaded from backbone. We give domain ownership to
	 * the creator rather than backbone.
	 */
	ob->stats.domain = current_object->stats.domain;
	return;
    }
    /*
     * The object isn't loaded from backbone or from the same domain as the
     * creator, so we need to lookup the domain, and add it if it isnt
     * present.
     */
    ob->stats.domain = add_stat_entry(domain_name, &domains);
    return;
}

mudlib_stats_t *set_backbone_domain (const char * str)
{
    mudlib_stats_t *dom;

    dom = add_stat_entry(str, &domains);
    if (dom)
	backbone_domain = dom;
    return dom;
}


/*
 * Argument is a file name, which we want to get the domain of.
 * Ask the master object.
 */
static const char *domain_for_file (const char * file)
{
    svalue_t *ret;
    static char buff[512];

    share_and_push_string(file);
    ret = apply_master_ob(APPLY_DOMAIN_FILE, 1);
    if (ret == 0 || ret == (svalue_t*)-1 || ret->type != T_STRING)
	return 0;
    strcpy(buff, ret->u.string);
    return buff;
}


/************************************
 * save and restore stats to a file *
 ************************************/

static void save_stat_list (const char * file, mudlib_stats_t * list)
{
    FILE *f;
    char fname_buf[MAXPATHLEN];
    char *fname = fname_buf;

    if (file) {
	if (strchr(file, '/')) {
	    if (file[0] == '/')
		file++;
	    f = fopen(file, "w");
	} else {
	    sprintf(fname, "%s/%s", LOG_DIR, file);
	    if (fname[0] == '/')
		fname++;
	    f = fopen(fname, "w");
	}
    } else {
	debug_message("*Warning: call to save_stat_list with null filename\n");
	return;
    }
    if (!f) {
	debug_message("*Error: unable to open stat file %s for writing.\n",
		file);
	return;
    }
    while (list) {
	fprintf(f, "%s %d %d\n", list->name,
		list->moves, list->heart_beats);
	list = list->next;
    }
    fclose(f);
}

static void restore_stat_list (const char * file, mudlib_stats_t ** list)
{
    FILE *f;
    char fname_buf[MAXPATHLEN];
    char *fname = fname_buf;
    mudlib_stats_t *entry;

    if (file) {
	if (strchr(file, '/')) {
	    if (file[0] == '/')
		file++;
	    f = fopen(file, "r");
	} else {
	    sprintf(fname, "%s/%s", LOG_DIR, file);
	    if (fname[0] == '/')
		fname++;
	    f = fopen(fname, "r");
	}
    } else {
	debug_message("*Warning: call to save_stat_list with null filename\n");
	return;
    }
    if (!f) {
	debug_message("*Warning: unable to open stat file %s for reading.\n",
		file);
	return;
    }
    while (fscanf(f, "%s", fname) != EOF) {
	entry = add_stat_entry(fname, list);
	fscanf(f, "%d %d\n", &entry->moves, &entry->heart_beats);
    }
    fclose(f);
}


void save_stat_files()
{
    save_stat_list(DOMAIN_STATS_FILE_NAME, domains);
    save_stat_list(AUTHOR_STATS_FILE_NAME, authors);
}

void restore_stat_files()
{
    restore_stat_list(DOMAIN_STATS_FILE_NAME, &domains);
    restore_stat_list(AUTHOR_STATS_FILE_NAME, &authors);
}


/*************************************
 * The following functions are the interface for efuns to get mappings
 * that describe the statistics for authors and domains.
 **************************************/

static mapping_t *
        get_info (mudlib_stats_t * dl)
{
    mapping_t *ret;

    ret = allocate_mapping(8);
    add_mapping_pair(ret, "moves", dl->moves);
    add_mapping_pair(ret, "errors", dl->errors);
    add_mapping_pair(ret, "heart_beats", dl->heart_beats);
    add_mapping_pair(ret, "array_size", dl->size_array);
    add_mapping_pair(ret, "objects", dl->objects);
    return ret;
}

static mapping_t *
        get_stats (const char * str, mudlib_stats_t * list)
{
    mudlib_stats_t *dl;
    mapping_t *m;
    svalue_t lv, *s;

    if (str) {
	for (dl = list; dl; dl = dl->next) {
	    if (!strcmp(str, dl->name))	/* are these both shared strings? */
		break;
	}
	if (dl) {
	    mapping_t *tmp;

	    tmp = get_info(dl);
	    tmp->ref--;
	    return tmp;
	} else {
	    return 0;
	}
    }
    m = allocate_mapping(8);
    for (dl = list; dl; dl = dl->next) {
	lv.type = T_STRING;
	lv.subtype = STRING_SHARED;
	lv.u.string = dl->name;  /* find_for_insert() adds a ref */
	s = find_for_insert(m, &lv, 1);
	s->type = T_MAPPING;
	s->subtype = 0;
	s->u.map = get_info(dl);
    }
    m->ref--;
    return m;
}

mapping_t *get_domain_stats (const char * str){
    return get_stats(str, domains);
}

mapping_t *get_author_stats (const char * str){
    return get_stats(str, authors);
}