ds3.0/bin/
ds3.0/extra/
ds3.0/extra/crat/
ds3.0/extra/creremote/
ds3.0/extra/mingw/
ds3.0/extra/wolfpaw/
ds3.0/fluffos-2.18-ds07/
ds3.0/fluffos-2.18-ds07/Win32/
ds3.0/fluffos-2.18-ds07/compat/
ds3.0/fluffos-2.18-ds07/compat/simuls/
ds3.0/fluffos-2.18-ds07/testsuite/
ds3.0/fluffos-2.18-ds07/testsuite/clone/
ds3.0/fluffos-2.18-ds07/testsuite/command/
ds3.0/fluffos-2.18-ds07/testsuite/data/
ds3.0/fluffos-2.18-ds07/testsuite/etc/
ds3.0/fluffos-2.18-ds07/testsuite/include/
ds3.0/fluffos-2.18-ds07/testsuite/inherit/
ds3.0/fluffos-2.18-ds07/testsuite/inherit/master/
ds3.0/fluffos-2.18-ds07/testsuite/log/
ds3.0/fluffos-2.18-ds07/testsuite/single/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/compiler/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/efuns/
ds3.0/fluffos-2.18-ds07/testsuite/single/tests/operators/
ds3.0/fluffos-2.18-ds07/testsuite/u/
ds3.0/fluffos-2.18-ds07/tmp/
ds3.0/lib/cmds/admins/
ds3.0/lib/cmds/common/
ds3.0/lib/cmds/creators/include/
ds3.0/lib/daemon/services/
ds3.0/lib/daemon/tmp/
ds3.0/lib/doc/
ds3.0/lib/doc/bguide/
ds3.0/lib/doc/efun/all/
ds3.0/lib/doc/efun/arrays/
ds3.0/lib/doc/efun/buffers/
ds3.0/lib/doc/efun/compile/
ds3.0/lib/doc/efun/floats/
ds3.0/lib/doc/efun/functions/
ds3.0/lib/doc/efun/mixed/
ds3.0/lib/doc/efun/numbers/
ds3.0/lib/doc/efun/parsing/
ds3.0/lib/doc/help/classes/
ds3.0/lib/doc/help/races/
ds3.0/lib/doc/lfun/
ds3.0/lib/doc/lfun/all/
ds3.0/lib/doc/lfun/lib/abilities/
ds3.0/lib/doc/lfun/lib/armor/
ds3.0/lib/doc/lfun/lib/bank/
ds3.0/lib/doc/lfun/lib/bot/
ds3.0/lib/doc/lfun/lib/clay/
ds3.0/lib/doc/lfun/lib/clean/
ds3.0/lib/doc/lfun/lib/clerk/
ds3.0/lib/doc/lfun/lib/client/
ds3.0/lib/doc/lfun/lib/combat/
ds3.0/lib/doc/lfun/lib/connect/
ds3.0/lib/doc/lfun/lib/container/
ds3.0/lib/doc/lfun/lib/corpse/
ds3.0/lib/doc/lfun/lib/creator/
ds3.0/lib/doc/lfun/lib/daemon/
ds3.0/lib/doc/lfun/lib/damage/
ds3.0/lib/doc/lfun/lib/deterioration/
ds3.0/lib/doc/lfun/lib/donate/
ds3.0/lib/doc/lfun/lib/door/
ds3.0/lib/doc/lfun/lib/equip/
ds3.0/lib/doc/lfun/lib/file/
ds3.0/lib/doc/lfun/lib/fish/
ds3.0/lib/doc/lfun/lib/fishing/
ds3.0/lib/doc/lfun/lib/flashlight/
ds3.0/lib/doc/lfun/lib/follow/
ds3.0/lib/doc/lfun/lib/ftp_client/
ds3.0/lib/doc/lfun/lib/ftp_data_connection/
ds3.0/lib/doc/lfun/lib/fuel/
ds3.0/lib/doc/lfun/lib/furnace/
ds3.0/lib/doc/lfun/lib/genetics/
ds3.0/lib/doc/lfun/lib/holder/
ds3.0/lib/doc/lfun/lib/id/
ds3.0/lib/doc/lfun/lib/interactive/
ds3.0/lib/doc/lfun/lib/lamp/
ds3.0/lib/doc/lfun/lib/leader/
ds3.0/lib/doc/lfun/lib/light/
ds3.0/lib/doc/lfun/lib/limb/
ds3.0/lib/doc/lfun/lib/living/
ds3.0/lib/doc/lfun/lib/load/
ds3.0/lib/doc/lfun/lib/look/
ds3.0/lib/doc/lfun/lib/manipulate/
ds3.0/lib/doc/lfun/lib/meal/
ds3.0/lib/doc/lfun/lib/messages/
ds3.0/lib/doc/lfun/lib/player/
ds3.0/lib/doc/lfun/lib/poison/
ds3.0/lib/doc/lfun/lib/position/
ds3.0/lib/doc/lfun/lib/post_office/
ds3.0/lib/doc/lfun/lib/potion/
ds3.0/lib/doc/lfun/lib/room/
ds3.0/lib/doc/lfun/lib/server/
ds3.0/lib/doc/lfun/lib/spell/
ds3.0/lib/doc/lfun/lib/torch/
ds3.0/lib/doc/lfun/lib/vendor/
ds3.0/lib/doc/lfun/lib/virt_sky/
ds3.0/lib/doc/lfun/lib/weapon/
ds3.0/lib/doc/lfun/lib/worn_storage/
ds3.0/lib/doc/lpc/constructs/
ds3.0/lib/doc/lpc/etc/
ds3.0/lib/doc/lpc/intermediate/
ds3.0/lib/doc/lpc/types/
ds3.0/lib/doc/misc/
ds3.0/lib/doc/old/
ds3.0/lib/doc/phints/
ds3.0/lib/domains/
ds3.0/lib/domains/Praxis/adm/
ds3.0/lib/domains/Praxis/attic/
ds3.0/lib/domains/Praxis/cemetery/mon/
ds3.0/lib/domains/Praxis/data/
ds3.0/lib/domains/Praxis/death/
ds3.0/lib/domains/Praxis/mountains/
ds3.0/lib/domains/Praxis/obj/armour/
ds3.0/lib/domains/Praxis/obj/magic/
ds3.0/lib/domains/Praxis/obj/weapon/
ds3.0/lib/domains/Praxis/orc_valley/
ds3.0/lib/domains/Ylsrim/
ds3.0/lib/domains/Ylsrim/adm/
ds3.0/lib/domains/Ylsrim/armor/
ds3.0/lib/domains/Ylsrim/broken/
ds3.0/lib/domains/Ylsrim/fish/
ds3.0/lib/domains/Ylsrim/meal/
ds3.0/lib/domains/Ylsrim/npc/
ds3.0/lib/domains/Ylsrim/obj/
ds3.0/lib/domains/Ylsrim/virtual/
ds3.0/lib/domains/Ylsrim/weapon/
ds3.0/lib/domains/campus/adm/
ds3.0/lib/domains/campus/chamber/
ds3.0/lib/domains/campus/etc/
ds3.0/lib/domains/campus/meals/
ds3.0/lib/domains/campus/txt/ai/charles/
ds3.0/lib/domains/campus/txt/ai/charles/bak2/
ds3.0/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds3.0/lib/domains/campus/txt/ai/charly/
ds3.0/lib/domains/campus/txt/ai/charly/bak/
ds3.0/lib/domains/campus/txt/jenny/
ds3.0/lib/domains/cave/doors/
ds3.0/lib/domains/cave/etc/
ds3.0/lib/domains/cave/meals/
ds3.0/lib/domains/cave/weap/
ds3.0/lib/domains/default/chamber/
ds3.0/lib/domains/default/creator/
ds3.0/lib/domains/default/doors/
ds3.0/lib/domains/default/etc/
ds3.0/lib/domains/default/vehicle/
ds3.0/lib/domains/default/virtual/
ds3.0/lib/domains/town/save/
ds3.0/lib/domains/town/txt/shame/
ds3.0/lib/domains/town/virtual/
ds3.0/lib/domains/town/virtual/bottom/
ds3.0/lib/domains/town/virtual/space/
ds3.0/lib/estates/
ds3.0/lib/ftp/
ds3.0/lib/lib/comp/
ds3.0/lib/lib/daemons/
ds3.0/lib/lib/daemons/include/
ds3.0/lib/lib/lvs/
ds3.0/lib/lib/user/
ds3.0/lib/lib/virtual/
ds3.0/lib/log/
ds3.0/lib/log/adm/
ds3.0/lib/log/archive/
ds3.0/lib/log/chan/
ds3.0/lib/log/errors/
ds3.0/lib/log/law/adm/
ds3.0/lib/log/law/email/
ds3.0/lib/log/law/names/
ds3.0/lib/log/law/sites-misc/
ds3.0/lib/log/law/sites-register/
ds3.0/lib/log/law/sites-tempban/
ds3.0/lib/log/law/sites-watch/
ds3.0/lib/log/open/
ds3.0/lib/log/reports/
ds3.0/lib/log/router/
ds3.0/lib/log/secure/
ds3.0/lib/log/watch/
ds3.0/lib/obj/book_source/
ds3.0/lib/obj/include/
ds3.0/lib/powers/prayers/
ds3.0/lib/powers/spells/
ds3.0/lib/realms/template/
ds3.0/lib/realms/template/adm/
ds3.0/lib/realms/template/area/
ds3.0/lib/realms/template/area/armor/
ds3.0/lib/realms/template/area/npc/
ds3.0/lib/realms/template/area/obj/
ds3.0/lib/realms/template/area/room/
ds3.0/lib/realms/template/area/weap/
ds3.0/lib/realms/template/bak/
ds3.0/lib/realms/template/cmds/
ds3.0/lib/save/kills/o/
ds3.0/lib/secure/cfg/classes/
ds3.0/lib/secure/cmds/builders/
ds3.0/lib/secure/cmds/creators/include/
ds3.0/lib/secure/cmds/players/
ds3.0/lib/secure/cmds/players/include/
ds3.0/lib/secure/daemon/imc2server/
ds3.0/lib/secure/daemon/include/
ds3.0/lib/secure/lib/
ds3.0/lib/secure/lib/include/
ds3.0/lib/secure/lib/net/include/
ds3.0/lib/secure/lib/std/
ds3.0/lib/secure/log/adm/
ds3.0/lib/secure/log/bak/
ds3.0/lib/secure/log/intermud/
ds3.0/lib/secure/log/network/
ds3.0/lib/secure/modules/
ds3.0/lib/secure/npc/
ds3.0/lib/secure/obj/include/
ds3.0/lib/secure/room/
ds3.0/lib/secure/save/
ds3.0/lib/secure/save/backup/
ds3.0/lib/secure/save/boards/
ds3.0/lib/secure/save/players/g/
ds3.0/lib/secure/tmp/
ds3.0/lib/secure/upgrades/files/
ds3.0/lib/secure/verbs/creators/
ds3.0/lib/std/board/
ds3.0/lib/std/lib/
ds3.0/lib/verbs/admins/include/
ds3.0/lib/verbs/builders/
ds3.0/lib/verbs/common/
ds3.0/lib/verbs/common/include/
ds3.0/lib/verbs/creators/
ds3.0/lib/verbs/creators/include/
ds3.0/lib/verbs/rooms/
ds3.0/lib/verbs/rooms/include/
ds3.0/lib/www/client/
ds3.0/lib/www/errors/
ds3.0/lib/www/images/
ds3.0/win32/
#include "std.h"
#include "lpc_incl.h"
#include "file.h"
#include "md.h"
#ifdef DEBUGMALLOC_EXTENSIONS
#include "comm.h"
#include "lex.h"
#include "simul_efun.h"
#include "call_out.h"
#include "mapping.h"
#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)
#include "socket_efuns.h"
#endif
#endif
#include "master.h"

#ifdef PACKAGE_PARSER
#include "packages/parser.h"
#endif

/*
note: do not use MALLOC() etc. in this module.  Unbridled recursion
will occur.  (use malloc() etc. instead)

This module introduces quite a lot of overhead but it can be useful
for tracking down memory leaks or for catching the freeing on non-malloc'd
data.  This module could easily be extended to allow the malloced memory
chunks to be tagged with a string label.
 */

#ifdef DEBUGMALLOC

#define LEFT_MAGIC(node) ((node)->magic)
#define RIGHT_MAGIC_ADDR(node) ((unsigned char *)(node) + sizeof(md_node_t) + (node)->size)
#define STORE_RIGHT_MAGIC(node) \
    *(RIGHT_MAGIC_ADDR(node)) = (char)(MD_MAGIC >> 24) & 0xff; \
*(RIGHT_MAGIC_ADDR(node)+1) = (char)(MD_MAGIC >> 16) & 0xff; \
*(RIGHT_MAGIC_ADDR(node)+2) = (char)(MD_MAGIC >> 8) & 0xff; \
*(RIGHT_MAGIC_ADDR(node)+3) = (char)MD_MAGIC & 0xff
#define FETCH_RIGHT_MAGIC(l, node) \
    l = (*(RIGHT_MAGIC_ADDR(node)) << 24) + \
(*(RIGHT_MAGIC_ADDR(node) + 1) << 16) + \
(*(RIGHT_MAGIC_ADDR(node) + 2) << 8) + \
(*(RIGHT_MAGIC_ADDR(node) + 3))

int totals[MAX_CATEGORY];
int blocks[MAX_CATEGORY];

static char *sources[] = {
    "*", "temporary blocks", "permanent blocks", "compiler blocks",
    "data blocks", "miscellaneous blocks", "<#6>", "<#7>", "<#8>", "<#9>",
    "<#10>", "program blocks", "call_out blocks", "interactives", "ed blocks",
    "<#15>", "include list", "permanent identifiers",
    "identifier hash table", "reserved block", "mudlib stats", "objects",
    "object table", "config table", "simul_efuns", "sentences", "string table",
    "free swap blocks", "uids", "object names", "predefines", "line numbers",
    "compiler local blocks", "compiled program", "users", "debugmalloc overhead",
    "heart_beat list", "parser", "input_to", "sockets",
    "strings", "malloc strings", "shared strings", "function pointers", "arrays",
    "mappings", "mapping nodes", "mapping tables", "buffers", "classes"
};

int malloc_mask = 121;

static md_node_t **table;
unsigned int total_malloced = 0L;
unsigned int hiwater = 0L;

#ifdef DEBUGMALLOC_EXTENSIONS
void check_all_blocks (int);

outbuffer_t out;

void MDmemcheck();

void MDmemcheck() {
    check_all_blocks(2);
    if (out.buffer)
        fatal("Internal memory check failed: %s\n", out.buffer);
}
#endif

void MDinit()
{
    int j;

    table = (md_node_t **) calloc(MD_TABLE_SIZE, sizeof(md_node_t *));
    for (j = 0; j < MAX_CATEGORY; j++) {
        totals[j] = 0;
    }
}

    void
MDmalloc (md_node_t * node, int size, int tag, char * desc)
{
    unsigned long h;
    static int count = 0;

    if (!size) {
        debug_message("md: debugmalloc: attempted to allocate zero bytes\n");
#ifdef DEBUG
        abort();
#endif
        return;
    }
    total_malloced += size;
    if (total_malloced > hiwater) {
        hiwater = total_malloced;
    }
    h = MD_HASH(node);
    node->size = size;
    node->next = table[h];
#ifdef CHECK_MEMORY
    LEFT_MAGIC(node) = MD_MAGIC;
    STORE_RIGHT_MAGIC(node);
#endif
#ifdef DEBUGMALLOC_EXTENSIONS
    if ((tag & 0xff) < MAX_CATEGORY) {
        totals[tag & 0xff] += size;
        blocks[tag & 0xff]++;
    }
    if (((tag >> 8) & 0xff) < MAX_CATEGORY) {
        totals[(tag >> 8) & 0xff] += size;
        blocks[(tag >> 8) & 0xff]++;
    }
    node->tag = tag;
    node->id = count++;
    node->desc = desc ? desc : "default";
    if (malloc_mask == node->tag) {
        debug_message("MDmalloc: %5d, [%-25s], %8lx:(%d)\n",
                node->tag, node->desc, PTR(node), node->size);
    }
#endif
    table[h] = node;
}

#ifdef DEBUGMALLOC_EXTENSIONS
void set_tag (const void * ptr, int tag) {
    md_node_t *node = PTR_TO_NODET(ptr);

    if ((node->tag & 0xff) < MAX_CATEGORY) {
        totals[node->tag & 0xff] -= node->size;
        blocks[node->tag & 0xff]--;
    }
    if (((node->tag >> 8) & 0xff) < MAX_CATEGORY) {
        totals[(node->tag >> 8) & 0xff] -= node->size;
        blocks[(node->tag >> 8) & 0xff]--;
    }
    node->tag = tag;
    if ((node->tag & 0xff) < MAX_CATEGORY) {
        totals[node->tag & 0xff] += node->size;
        blocks[node->tag & 0xff]++;
    }
    if (((node->tag >> 8) & 0xff) < MAX_CATEGORY) {
        totals[(node->tag >> 8) & 0xff] += node->size;
        blocks[(node->tag >> 8) & 0xff]++;
    }
}
#endif

    int
MDfree (void * ptr)
{
    unsigned long h;
    int tmp;
    md_node_t *entry, **oentry;

    h = MD_HASH(ptr);
    oentry = &table[h];
    for (entry = *oentry; entry; oentry = &entry->next, entry = *oentry) {
        if (entry == ptr) {
            *oentry = entry->next;
            total_malloced -= entry->size;
            break;
        }
    }
    if (entry) {
#ifdef CHECK_MEMORY
        if (LEFT_MAGIC(entry) != MD_MAGIC) {
            debug_message("MDfree: left side of entry corrupt: %s %04x at %lx\n", entry->desc, entry->tag, entry);
        }
        FETCH_RIGHT_MAGIC(tmp, entry);
        if (tmp != MD_MAGIC) {
            debug_message("MDfree: right side of entry corrupt: %s %04x at %lx\n", entry->desc, entry->tag, entry);
        }
#endif
#ifdef DEBUGMALLOC_EXTENSIONS
        if ((entry->tag & 0xff) < MAX_CATEGORY) {
            totals[entry->tag & 0xff] -= entry->size;
            blocks[entry->tag & 0xff]--;
        }
        if (((entry->tag >> 8) & 0xff) < MAX_CATEGORY) {
            totals[(entry->tag >> 8) & 0xff] -= entry->size;
            blocks[(entry->tag >> 8) & 0xff]--;
        }
        if (malloc_mask == entry->tag) {
            debug_message("MDfree: %5d, [%-25s], %8lx:(%d)\n",
                    entry->tag, entry->desc, (unsigned long) PTR(entry), entry->size);
        }
#endif
    } else {
        debug_message("md: debugmalloc: attempted to free non-malloc'd pointer %08lx\n",
                (unsigned long) ptr);
#ifdef DEBUG
        abort();
#endif
        return 0;
    }
    return 1;
}

#ifdef DEBUGMALLOC_EXTENSIONS
char *dump_debugmalloc (char * tfn, int mask)
{
    int j, total = 0, chunks = 0, total2 = 0;
    const char *fn;
    md_node_t *entry;
    FILE *fp;

    outbuf_zero(&out);
    fn = check_valid_path(tfn, current_object, "debugmalloc", 1);
    if (!fn) {
        error("Invalid path '%s' for writing.\n", tfn);
    }
    fp = fopen(fn, "w");
    if (!fp) {
        error("Unable to open %s for writing.\n", fn);
    }
    for (j = 0; j < MD_TABLE_SIZE; j++) {
        for (entry = table[j]; entry; entry = entry->next) {
            if (!mask || (entry->tag == mask)) {
                fprintf(fp, "%-30s: sz %7d: id %6d: tag %08x, a %8lx\n",
                        entry->desc, entry->size, entry->id, entry->tag,
                        (unsigned long) PTR(entry));
                total += entry->size;
                chunks++;
            }
        }
    }
    fprintf(fp, "total =    %8d\n", total);
    fprintf(fp, "# chunks = %8d\n", chunks);
    fprintf(fp, "ave. bytes per chunk = %7.2f\n\n", (float) total / chunks);
    fprintf(fp, "categories:\n\n");
    for (j = 0; j < MAX_CATEGORY; j++) {
        fprintf(fp, "%4d: %10d\n", j, totals[j]);
        total2 += totals[j];
    }
    fprintf(fp, "\ntotal = %11d\n", total2);
    fclose(fp);
    outbuf_addv(&out, "total =    %8d\n", total);
    outbuf_addv(&out, "# chunks = %8d\n", chunks);
    if (chunks) {
        outbuf_addv(&out, "ave. bytes per chunk = %7.2f\n", (float) total / chunks);
    }
    outbuf_fix(&out);
    return out.buffer;
}
#endif                          /* DEBUGMALLOC_EXTENSIONS */

void set_malloc_mask (int mask)
{
    malloc_mask = mask;
}

#ifdef DEBUGMALLOC_EXTENSIONS
static void mark_object (object_t * ob) {
#ifndef NO_ADD_ACTION
    sentence_t *sent;
#endif
    int i;

    if (ob->prog)
        ob->prog->extra_ref++;

    if (ob->obname) {
        DO_MARK(ob->obname, TAG_OBJ_NAME);
    }

    if (ob->replaced_program)
        EXTRA_REF(BLOCK(ob->replaced_program))++;

#ifdef PRIVS
    if (ob->privs)
        EXTRA_REF(BLOCK(ob->privs))++;
#endif

#ifndef NO_ADD_ACTION
    if (ob->living_name)
        EXTRA_REF(BLOCK(ob->living_name))++;

    sent = ob->sent;

    while (sent) {
        DO_MARK(sent, TAG_SENTENCE);
        if (sent->flags & V_FUNCTION)
            sent->function.f->hdr.extra_ref++;
        else {
            if (sent->function.s)
                EXTRA_REF(BLOCK(sent->function.s))++;
        }
        if (sent->verb)
            EXTRA_REF(BLOCK(sent->verb))++;
        sent = sent->next;
    }
#endif

#ifdef PACKAGE_PARSER
    if (ob->pinfo)
        parser_mark(ob->pinfo);
#endif

    if (ob->prog)
        for (i = 0; i < ob->prog->num_variables_total; i++)
            mark_svalue(&ob->variables[i]);
    else
        outbuf_addv(&out, "can't mark variables; %s is swapped.\n",
                ob->obname);
}

void mark_svalue (svalue_t * sv) {
    switch (sv->type) {
        case T_OBJECT:
            sv->u.ob->extra_ref++;
            break;
        case T_ARRAY:
            sv->u.arr->extra_ref++;
            break;
        case T_CLASS:
            sv->u.arr->extra_ref++;
            break;
        case T_MAPPING:
            sv->u.map->extra_ref++;
            break;
        case T_FUNCTION:
            sv->u.fp->hdr.extra_ref++;
            break;
#ifndef NO_BUFFER_TYPE
        case T_BUFFER:
            sv->u.buf->extra_ref++;
            break;
#endif
        case T_STRING:
            switch (sv->subtype) {
                case STRING_MALLOC:
                    MSTR_EXTRA_REF(sv->u.string)++;
                    break;
                case STRING_SHARED:
                    EXTRA_REF(BLOCK(sv->u.string))++;
                    break;
            }
    }
}

    static void mark_funp (funptr_t* fp) {
        if (fp->hdr.args)
            fp->hdr.args->extra_ref++;

        if (fp->hdr.owner)
            fp->hdr.owner->extra_ref++;
        switch (fp->hdr.type) {
            case FP_LOCAL | FP_NOT_BINDABLE:
                if (fp->hdr.owner)
                    fp->hdr.owner->prog->extra_func_ref++;
                break;
            case FP_FUNCTIONAL:
            case FP_FUNCTIONAL | FP_NOT_BINDABLE:
                fp->f.functional.prog->extra_func_ref++;
                break;
        }
    }

static void mark_sentence (sentence_t * sent) {
    if (sent->flags & V_FUNCTION) {
        if (sent->function.f)
            sent->function.f->hdr.extra_ref++;
    } else {
        if (sent->function.s)
            EXTRA_REF(BLOCK(sent->function.s))++;
    }
#ifndef NO_ADD_ACTION
    if (sent->verb)
        EXTRA_REF(BLOCK(sent->verb))++;
#endif
}

static int print_depth = 0;

static void md_print_array  (array_t * vec) {
    int i;

    outbuf_add(&out, "({ ");
            for (i=0; i < vec->size; i++) {
            switch (vec->item[i].type) {
            case T_INVALID:
            outbuf_add(&out, "INVALID");
            break;
            case T_NUMBER:
            outbuf_addv(&out, "%ld", vec->item[i].u.number);
            break;
            case T_REAL:
            outbuf_addv(&out, "%f", vec->item[i].u.real);
            break;
            case T_STRING:
            outbuf_addv(&out, "\"%s\"", vec->item[i].u.string);
            break;
            case T_ARRAY:
            if (print_depth < 2) {
            print_depth++;
            md_print_array(vec->item[i].u.arr);
            } else {
            outbuf_add(&out, "({ ... })");
            }
            break;
            case T_CLASS:
            outbuf_add(&out, "<class>");
            break;
#ifndef NO_BUFFER_TYPE
            case T_BUFFER:
            outbuf_add(&out, "<buffer>");
            break;
#endif
            case T_FUNCTION:
            outbuf_add(&out, "<function>");
            break;
            case T_MAPPING:
            outbuf_add(&out, "<mapping>");
            break;
            case T_OBJECT:
            outbuf_addv(&out, "OBJ(%s)", vec->item[i].u.ob->obname);
            break;
            }
            if (i != vec->size - 1) outbuf_add(&out, ", ");
            }
            outbuf_add(&out, " })\n");
            print_depth--;
}

static void mark_config (void) {
    int i;

    for (i = 0; i < NUM_CONFIG_STRS; i++) {
        DO_MARK(config_str[i], TAG_STRING);
    }
}

#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)
void mark_sockets (void) {
    int i;
    char *s;

    for (i = 0; i < max_lpc_socks; i++) {
        if (lpc_socks[i].flags & S_READ_FP) {
            lpc_socks[i].read_callback.f->hdr.extra_ref++;
        } else
            if ((s = lpc_socks[i].read_callback.s)) {
                EXTRA_REF(BLOCK(s))++;
            }
        if (lpc_socks[i].flags & S_WRITE_FP) {
            lpc_socks[i].write_callback.f->hdr.extra_ref++;
        } else
            if ((s = lpc_socks[i].write_callback.s)) {
                EXTRA_REF(BLOCK(s))++;
            }
        if (lpc_socks[i].flags & S_CLOSE_FP) {
            lpc_socks[i].close_callback.f->hdr.extra_ref++;
        } else
            if ((s = lpc_socks[i].close_callback.s)) {
                EXTRA_REF(BLOCK(s))++;
            }
    }
}
#endif

#ifdef STRING_STATS
static int base_overhead = 0;

/* Compute the correct values of allocd_strings, allocd_bytes, and
 * bytes_distinct_strings based on blocks that are actually allocated.
 */
void compute_string_totals (int * asp, int * abp, int * bp) {
    int hsh;
    md_node_t *entry;
    malloc_block_t *msbl;
    block_t *ssbl;

    *asp = 0;
    *abp = 0;
    *bp = 0;

    for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) {
        for (entry = table[hsh]; entry; entry = entry->next) {
            if (entry->tag == TAG_MALLOC_STRING) {
                msbl = NODET_TO_PTR(entry, malloc_block_t*);
                *bp += msbl->size + 1;
                *asp += msbl->ref;
                *abp += msbl->ref * (msbl->size + 1);
            }
            if (entry->tag == TAG_SHARED_STRING) {
                ssbl = NODET_TO_PTR(entry, block_t*);
                *bp += ssbl->size + 1;
                *asp += ssbl->refs;
                *abp += ssbl->refs * (ssbl->size + 1);
            }
        }
    }
}

/*
 * Verify string statistics.  out can be zero, in which case any errors
 * are printed to stdout and abort() is called.  Otherwise the error messages
 * are added to the outbuffer.
 */
    void check_string_stats (outbuffer_t * out) {
        int overhead = blocks[TAG_SHARED_STRING & 0xff] * sizeof(block_t)
            + blocks[TAG_MALLOC_STRING & 0xff] * sizeof(malloc_block_t);
        int num = blocks[TAG_SHARED_STRING & 0xff] + blocks[TAG_MALLOC_STRING & 0xff];
        int bytes, as, ab;

        compute_string_totals(&as, &ab, &bytes);

        if (!base_overhead)
            base_overhead = overhead_bytes - overhead;
        overhead += base_overhead;

        if (num != num_distinct_strings) {
            if (out) {
                outbuf_addv(out, "WARNING: num_distinct_strings is: %i should be: %i\n",
                        num_distinct_strings, num);
            } else {
                printf("WARNING: num_distinct_strings is: %i should be: %i\n",
                        num_distinct_strings, num);
                abort();
            }
        }
        if (overhead != overhead_bytes) {
            if (out) {
                outbuf_addv(out, "WARNING: overhead_bytes is: %i should be: %i\n",
                        overhead_bytes, overhead);
            } else {
                printf("WARNING: overhead_bytes is: %i should be: %i\n",
                        overhead_bytes, overhead);
                abort();
            }
        }
        if (bytes != bytes_distinct_strings) {
            if (out) {
                outbuf_addv(out, "WARNING: bytes_distinct_strings is: %i should be: %i\n",
                        bytes_distinct_strings, bytes - (overhead - base_overhead));
            } else {
                printf("WARNING: bytes_distinct_strings is: %i should be: %i\n",
                        bytes_distinct_strings, bytes - (overhead - base_overhead));
                abort();
            }
        }
        if (allocd_strings != as) {
            if (out) {
                outbuf_addv(out, "WARNING: allocd_strings is: %i should be: %i\n",
                        allocd_strings, as);
            } else {
                printf("WARNING: allocd_strings is: %i should be: %i\n",
                        allocd_strings, as);
                abort();
            }
        }
        if (allocd_bytes != ab) {
            if (out) {
                outbuf_addv(out, "WARNING: allocd_bytes is: %i should be: %i\n",
                        allocd_bytes, ab);
            } else {
                printf("WARNING: allocd_bytes is: %i should be: %i\n",
                        allocd_bytes, ab);
                abort();
            }
        }
    }
#endif

/* currently: 1 - debug, 2 - suppress leak checks */
void check_all_blocks (int flag) {
    int i, j, hsh;
    int tmp;
    md_node_t *entry;
    object_t *ob;
    array_t *vec;
    mapping_t *map;
#ifndef NO_BUFFER_TYPE
    buffer_t *buf;
#endif
    funptr_t *fp;
    mapping_node_t *node;
    program_t *prog;
    sentence_t *sent;
    char *ptr;
    block_t *ssbl;
    malloc_block_t *msbl;
    extern svalue_t apply_ret_value;

#if 0
    int num = 0, total = 0;
#endif

    outbuf_zero(&out);
    if (!(flag & 2))
        outbuf_add(&out, "Performing memory tests ...\n");

    for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) {
        for (entry = table[hsh]; entry; entry = entry->next) {
            entry->tag &= ~TAG_MARKED;
#ifdef CHECK_MEMORY
            if (LEFT_MAGIC(entry) != MD_MAGIC) {
                outbuf_addv(&out, "WARNING: left side of entry corrupt: %s %08lx at %lx\n", entry->desc, entry->tag, entry);
            }
            FETCH_RIGHT_MAGIC(tmp, entry);
            if (tmp != MD_MAGIC) {
                outbuf_addv(&out, "WARNING: right side of entry corrupt: %s %08lx at %lx\n", entry->desc, entry->tag, entry);
            }
#endif
            switch (entry->tag & 0xff00) {
                case TAG_TEMPORARY:
                    if (!(flag & 2))
                        outbuf_addv(&out, "WARNING: Found temporary block: %s %04x\n", entry->desc, entry->tag);
                    break;
                case TAG_COMPILER:
                    if (!(flag & 2))
                        outbuf_addv(&out, "Found compiler block: %s %04x\n", entry->desc, entry->tag);
                    break;
                case TAG_MISC:
                    outbuf_addv(&out, "Found miscellaneous block: %s %04x\n", entry->desc, entry->tag);
                    break;
            }
            switch (entry->tag) {
                case TAG_OBJECT:
                    ob = NODET_TO_PTR(entry, object_t *);
                    ob->extra_ref = 0;
                    break;
                case TAG_PROGRAM:
                    prog = NODET_TO_PTR(entry, program_t *);
                    prog->extra_ref = 0;
                    prog->extra_func_ref = 0;
                    break;
                case TAG_MALLOC_STRING:
                    {
                        char *str;

                        msbl = NODET_TO_PTR(entry, malloc_block_t *);
                        /* don't give an error for the return value we are
constructing :) */
                        if (msbl == MSTR_BLOCK(out.buffer))
                            break;

                        str = (char *)(msbl + 1);
                        msbl->extra_ref = 0;
                        if (msbl->size != USHRT_MAX && msbl->size != strlen(str)) {
                            outbuf_addv(&out, "Malloc'ed string length is incorrect: %s %04x '%s': is: %i should be: %i\n", entry->desc, entry->tag, str, msbl->size, strlen(str));
                        }
                        break;
                    }
                case TAG_SHARED_STRING:
                    ssbl = NODET_TO_PTR(entry, block_t *);
                    EXTRA_REF(ssbl) = 0;
                    break;
                case TAG_ARRAY:
                    vec = NODET_TO_PTR(entry, array_t *);
                    vec->extra_ref = 0;
                    break;
                case TAG_CLASS:
                    vec = NODET_TO_PTR(entry, array_t *);
                    vec->extra_ref = 0;
                    break;
                case TAG_MAPPING:
                    map = NODET_TO_PTR(entry, mapping_t *);
                    map->extra_ref = 0;
                    break;
                case TAG_FUNP:
                    fp = NODET_TO_PTR(entry, funptr_t *);
                    fp->hdr.extra_ref = 0;
                    break;
#ifndef NO_BUFFER_TYPE
                case TAG_BUFFER:
                    buf = NODET_TO_PTR(entry, buffer_t *);
                    buf->extra_ref = 0;
                    break;
#endif
            }
        }
    }

    if (!(flag & 2)) {
        /* the easy ones to find */
        if (blocks[TAG_SIMULS & 0xff] > 3)
            outbuf_add(&out, "WARNING: more than three simul_efun tables allocated.\n");
        if (blocks[TAG_INC_LIST & 0xff] > 1)
            outbuf_add(&out, "WARNING: more than one include list allocated.\n");
        if (blocks[TAG_IDENT_TABLE & 0xff] > 1)
            outbuf_add(&out, "WARNING: more than one identifier hash table allocated.\n");
        if (blocks[TAG_RESERVED & 0xff] > 1)
            outbuf_add(&out, "WARNING: more than one reserved block allocated.\n");
        if (blocks[TAG_OBJ_TBL & 0xff] > 1)
            outbuf_add(&out, "WARNING: more than object table allocated.\n");
        if (blocks[TAG_CONFIG & 0xff] > 1)
            outbuf_add(&out, "WARNING: more than config file table allocated.\n");
        if (blocks[TAG_STR_TBL & 0xff] > 1)
            outbuf_add(&out, "WARNING: more than string table allocated.\n");
        if (totals[TAG_CALL_OUT & 0xff] != print_call_out_usage(&out, -1))
            outbuf_add(&out, "WARNING: wrong number of call_out blocks allocated.\n");
        if (blocks[TAG_LOCALS & 0xff] > 3)
            outbuf_add(&out, "WARNING: more than 3 local blocks allocated.\n");

        if (blocks[TAG_SENTENCE & 0xff] != tot_alloc_sentence)
            outbuf_addv(&out, "WARNING: tot_alloc_sentence is: %i should be: %i\n",
                    tot_alloc_sentence, blocks[TAG_SENTENCE & 0xff]);
        if (blocks[TAG_OBJECT & 0xff] != tot_alloc_object)
            outbuf_addv(&out, "WARNING: tot_alloc_object is: %i should be: %i\n",
                    tot_alloc_object, blocks[TAG_OBJECT & 0xff]);
        if (blocks[TAG_PROGRAM & 0xff] != total_num_prog_blocks)
            outbuf_addv(&out, "WARNING: total_num_prog_blocks is: %i should be: %i\n",
                    total_num_prog_blocks, blocks[TAG_PROGRAM & 0xff]);
#ifdef ARRAY_STATS
        if (blocks[TAG_ARRAY & 0xff] != num_arrays)
            outbuf_addv(&out, "WARNING: num_arrays is: %i should be: %i\n",
                    num_arrays, blocks[TAG_ARRAY & 0xff]);
        if (totals[TAG_ARRAY & 0xff] != total_array_size)
            outbuf_addv(&out, "WARNING: total_array_size is: %i should be: %i\n",
                    total_array_size, totals[TAG_ARRAY & 0xff]);
#endif
#ifdef CLASS_STATS
        if (blocks[TAG_CLASS & 0xff] != num_classes)
            outbuf_addv(&out, "WARNING: num_classes is: %i should be: %i\n",
                    num_classes, blocks[TAG_CLASS & 0xff]);
        if (totals[TAG_CLASS & 0xff] != total_class_size)
            outbuf_addv(&out, "WARNING: total_class_size is: %i should be: %i\n",
                    total_class_size, totals[TAG_CLASS & 0xff]);
#endif

        if (blocks[TAG_MAPPING & 0xff] != num_mappings)
            outbuf_addv(&out, "WARNING: num_mappings is: %i should be: %i\n",
                    num_mappings, blocks[TAG_MAPPING & 0xff]);
        if (blocks[TAG_MAP_TBL & 0xff] != num_mappings)
            outbuf_addv(&out, "WARNING: %i tables for %i mappings\n",
                    blocks[TAG_MAP_TBL & 0xff], num_mappings);
        if (blocks[TAG_INTERACTIVE & 0xff] != num_user)
            outbuf_addv(&out, "WATNING: num_user is: %i should be: %i\n",
                    num_user, blocks[TAG_INTERACTIVE & 0xff]);
#ifdef STRING_STATS
        check_string_stats(&out);
#endif

#ifdef PACKAGE_EXTERNAL
        for (i = 0; i < NUM_EXTERNAL_CMDS; i++) {
            if (external_cmd[i]) {
                DO_MARK(external_cmd[i], TAG_STRING);
            }
        }
#endif

        /* now do a mark and sweep check to see what should be alloc'd */
        for (i = 0; i < max_users; i++)
            if (all_users[i]) {
                DO_MARK(all_users[i], TAG_INTERACTIVE);
                all_users[i]->ob->extra_ref++;
                if (all_users[i]->input_to) {
                    all_users[i]->input_to->ob->extra_ref++;
                    DO_MARK(all_users[i]->input_to, TAG_SENTENCE);
                    mark_sentence(all_users[i]->input_to);
                    if (all_users[i]->num_carry) {
                        for (j = 0; j < all_users[i]->num_carry; j++)
                            mark_svalue(all_users[i]->carryover + j);
                    }
                }
#ifdef PACKAGE_COMPRESS
                if(all_users[i]->compressed_stream)
                    DO_MARK(all_users[i]->compressed_stream, TAG_INTERACTIVE);
#endif

#ifndef NO_ADD_ACTION
                if (all_users[i]->iflags & NOTIFY_FAIL_FUNC)
                    all_users[i]->default_err_message.f->hdr.extra_ref++;
                else if (all_users[i]->default_err_message.s)
                    EXTRA_REF(BLOCK(all_users[i]->default_err_message.s))++;
#endif
            }

        if (*(DEFAULT_FAIL_MESSAGE)) {
            char buf[8192];

            strcpy(buf, DEFAULT_FAIL_MESSAGE);
            strcat(buf, "\n");
            EXTRA_REF(BLOCK(findstring(buf)))++;
        }

#ifdef PACKAGE_UIDS
        mark_all_uid_nodes();
#endif
#ifdef PACKAGE_MUDLIB_STATS
        mark_mudlib_stats();
#endif
#if defined(PACKAGE_SOCKETS) || defined(PACKAGE_EXTERNAL)
        mark_sockets();
#endif
#ifdef PACKAGE_PARSER
        parser_mark_verbs();
#endif
        mark_file_sv();
        mark_all_defines();
        mark_free_sentences();
        mark_iptable();
        mark_stack();
        mark_command_giver_stack();
        mark_call_outs();
        mark_simuls();
        mark_apply_low_cache();
        mark_mapping_node_blocks();
        mark_config();

        mark_svalue(&apply_ret_value);

        if (master_ob)
            master_ob->extra_ref++;
        if (simul_efun_ob)
            simul_efun_ob->extra_ref++;
        for (ob = obj_list; ob; ob = ob->next_all) {
            ob->extra_ref++;
        }
        /* objects on obj_list_destruct still have a ref too */
        for (ob = obj_list_destruct; ob; ob = ob->next_all) {
            ob->extra_ref++;
        }

        for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) {
            for (entry = table[hsh]; entry; entry = entry->next) {
                switch (entry->tag & ~TAG_MARKED) {
                    case TAG_IDENT_TABLE: {
                                              ident_hash_elem_t *hptr, *first;
                                              ident_hash_elem_t **table;
                                              int size;

                                              table = NODET_TO_PTR(entry, ident_hash_elem_t **);
                                              size = (entry->size / 3) / sizeof(ident_hash_elem_t *);
                                              for (i = 0; i < size; i++) {
                                                  first = table[i];
                                                  if (first) {
                                                      hptr = first;
                                                      do {
                                                          if (hptr->token & (IHE_SIMUL | IHE_EFUN)) {
                                                              DO_MARK(hptr, TAG_PERM_IDENT);
                                                          }
                                                          hptr = hptr->next;
                                                      } while (hptr != first);
                                                  }
                                              }
                                              break;
                                          }
                    case TAG_FUNP:
                                          fp = NODET_TO_PTR(entry, funptr_t *);
                                          mark_funp(fp);
                                          break;
                    case TAG_ARRAY:
                                          vec = NODET_TO_PTR(entry, array_t *);
                                          if (entry->size != sizeof(array_t) + sizeof(svalue_t[1]) * (vec->size - 1))
                                              outbuf_addv(&out, "array size doesn't match block size: %s %04x\n", entry->desc, entry->tag);
                                          for (i = 0; i < vec->size; i++) mark_svalue(&vec->item[i]);
                                          break;
                    case TAG_CLASS:
                                          vec = NODET_TO_PTR(entry, array_t *);
                                          if (entry->size != sizeof(array_t) + sizeof(svalue_t[1]) * (vec->size - 1))
                                              outbuf_addv(&out, "class size doesn't match block size: %s %04x\n", entry->desc, entry->tag);
                                          for (i = 0; i < vec->size; i++) mark_svalue(&vec->item[i]);
                                          break;
                    case TAG_MAPPING:
                                          map = NODET_TO_PTR(entry, mapping_t *);
                                          DO_MARK(map->table, TAG_MAP_TBL);

                                          i = map->table_size;
                                          do {
                                              for (node = map->table[i]; node; node = node->next) {
                                                  mark_svalue(node->values);
                                                  mark_svalue(node->values + 1);
                                              }
                                          } while (i--);
                                          break;
                    case TAG_OBJECT:
                                          ob = NODET_TO_PTR(entry, object_t *);
                                          mark_object(ob);
                                          {
                                              object_t *tmp = obj_list;
                                              while (tmp && tmp != ob)
                                                  tmp = tmp->next_all;
                                              if (!tmp) {
                                                  tmp = obj_list_destruct;
                                                  while (tmp && tmp != ob)
                                                      tmp = tmp->next_all;
                                              }
                                              if (!tmp) {
                                                  tmp = obj_list_dangling;
                                                  while (tmp && tmp != ob)
                                                      tmp = tmp->next_all;
                                                  if (tmp)
                                                      outbuf_addv(&out,
                                                              "WARNING: %s is dangling.\n",
                                                              ob->obname);
                                              }
                                              if (!tmp)
                                                  outbuf_addv(&out,
                                                          "WARNING: %s not in object list.\n",
                                                          ob->obname);
                                          }
                                          break;
                    case TAG_LPC_OBJECT:
                                          ob = NODET_TO_PTR(entry, object_t *);
                                          if (ob->obname) {
                                              DO_MARK(ob->obname, TAG_OBJ_NAME);
                                          }
                                          break;
                    case TAG_PROGRAM:
                                          prog = NODET_TO_PTR(entry, program_t *);

                                          if (prog->line_info) {
                                              DO_MARK(prog->file_info, TAG_LINENUMBERS);
                                          }

                                          for (i = 0; i < prog->num_inherited; i++)
                                              prog->inherit[i].prog->extra_ref++;

                                          for (i = 0; i < prog->num_functions_defined; i++)
                                              if (prog->function_table[i].funcname)
                                                  EXTRA_REF(BLOCK(prog->function_table[i].funcname))++;

                                          for (i = 0; i < prog->num_strings; i++)
                                              EXTRA_REF(BLOCK(prog->strings[i]))++;

                                          for (i = 0; i < prog->num_variables_defined; i++)
                                              EXTRA_REF(BLOCK(prog->variable_table[i]))++;

                                          EXTRA_REF(BLOCK(prog->filename))++;
                }
            }
        }

        /* now check */
        for (hsh = 0; hsh < MD_TABLE_SIZE; hsh++) {
            for (entry = table[hsh]; entry; entry = entry->next) {
                switch (entry->tag) {
                    case TAG_MUDLIB_STATS:
                        outbuf_addv(&out, "WARNING: Found orphan mudlib stat block: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_PROGRAM:
                        prog = NODET_TO_PTR(entry, program_t *);
                        if (prog->ref != prog->extra_ref)
                            outbuf_addv(&out, "Bad ref count for program %s, is %d - should be %d\n", prog->filename, prog->ref, prog->extra_ref);
                        if (prog->func_ref != prog->extra_func_ref)
                            outbuf_addv(&out, "Bad function ref count for program %s, is %d - should be %d\n", prog->filename, prog->func_ref, prog->extra_func_ref);
                        break;
                    case TAG_OBJECT:
                        ob = NODET_TO_PTR(entry, object_t *);
                        if (ob->ref != ob->extra_ref)
                            outbuf_addv(&out, "Bad ref count for object %s, is %d - should be %d\n", ob->obname, ob->ref, ob->extra_ref);
                        break;
                    case TAG_ARRAY:
                        vec = NODET_TO_PTR(entry, array_t *);
                        if (vec->ref != vec->extra_ref) {
                            outbuf_addv(&out, "Bad ref count for array, is %d - should be %d\n", vec->ref, vec->extra_ref);
                            print_depth = 0;
                            md_print_array(vec);
                        }
                        break;
                    case TAG_CLASS:
                        vec = NODET_TO_PTR(entry, array_t *);
                        if (vec->ref != vec->extra_ref)
                            outbuf_addv(&out, "Bad ref count for class, is %d - should be %d\n", vec->ref, vec->extra_ref);
                        break;
                    case TAG_MAPPING:
                        map = NODET_TO_PTR(entry, mapping_t *);
                        if (map->ref != map->extra_ref)
                            outbuf_addv(&out, "Bad ref count for mapping, is %d - should be %d\n", map->ref, map->extra_ref);
                        break;
                    case TAG_FUNP:
                        fp = NODET_TO_PTR(entry, funptr_t *);
                        if (fp->hdr.ref != fp->hdr.extra_ref)
                            outbuf_addv(&out, "Bad ref count for function pointer (owned by %s), is %d - should be %d\n", (fp->hdr.owner ? fp->hdr.owner->obname : "(null)"), fp->hdr.ref, fp->hdr.extra_ref);
                        break;
#ifndef NO_BUFFER_TYPE
                    case TAG_BUFFER:
                        buf = NODET_TO_PTR(entry, buffer_t *);
                        if (buf->ref != buf->extra_ref)
                            outbuf_addv(&out, "Bad ref count for buffer, is %d - should be %d\n", buf->ref, buf->extra_ref);
                        break;
#endif
                    case TAG_PREDEFINES:
                        outbuf_addv(&out, "WARNING: Found orphan predefine: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_LINENUMBERS:
                        outbuf_addv(&out, "WARNING: Found orphan line number block: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_OBJ_NAME:
                        outbuf_addv(&out, "WARNING: Found orphan object name: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_INTERACTIVE:
                        outbuf_addv(&out, "WARNING: Found orphan interactive: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_UID:
                        outbuf_addv(&out, "WARNING: Found orphan uid node: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_SENTENCE:
                        sent = NODET_TO_PTR(entry, sentence_t *);
                        outbuf_addv(&out, "WARNING: Found orphan sentence: %s:%s - %s %04x\n", sent->ob->obname, sent->function, entry->desc, entry->tag);
                        break;
                    case TAG_PERM_IDENT:
                        outbuf_addv(&out, "WARNING: Found orphan permanent identifier: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_STRING:
                        ptr = NODET_TO_PTR(entry, char *);
                        outbuf_addv(&out, "WARNING: Found orphan malloc'ed string: \"%s\" - %s %04x\n", ptr, entry->desc, entry->tag);
                        break;
                    case TAG_MALLOC_STRING:
                        msbl = NODET_TO_PTR(entry, malloc_block_t *);
                        /* don't give an error for the return value we are
constructing :) */
                        if (msbl == MSTR_BLOCK(out.buffer))
                            break;

                        if (msbl->ref != msbl->extra_ref)
                            outbuf_addv(&out, "Bad ref count for malloc string \"%s\" %s %04x, is %d - should be %d\n", (char *)(msbl + 1), entry->desc, entry->tag, msbl->ref, msbl->extra_ref);
                        break;
                    case TAG_SHARED_STRING:
                        ssbl = NODET_TO_PTR(entry, block_t *);
                        if (REFS(ssbl) != EXTRA_REF(ssbl))
                            outbuf_addv(&out, "Bad ref count for shared string \"%s\", is %d - should be %d\n", STRING(ssbl), REFS(ssbl), EXTRA_REF(ssbl));
                        break;
                    case TAG_ED:
                        outbuf_addv(&out, "Found allocated ed block: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_MAP_TBL:
                        outbuf_addv(&out, "WARNING: Found orphan mapping table: %s %04x\n", entry->desc, entry->tag);
                        break;
                    case TAG_MAP_NODE_BLOCK:
                        outbuf_addv(&out, "WARNING: Found orphan mapping node block: %s %04x\n", entry->desc, entry->tag);
                        break;
                }
                entry->tag &= ~TAG_MARKED;
            }
        }
    }

    if (flag & 1) {
        outbuf_add(&out, "\n\n");
        outbuf_add(&out, "      source                    blks   total\n");
        outbuf_add(&out, "------------------------------ ------ --------\n");
        for (i = 1; i < MAX_CATEGORY; i++) {
            if (totals[i])
                outbuf_addv(&out, "%-30s %6d %8d\n", sources[i], blocks[i], totals[i]);
            if (i == 5) outbuf_add(&out, "\n");
        }
    }
    if (!(flag & 2))
        outbuf_push(&out);
    else {
        FREE_MSTR(out.buffer);
        push_number(0);
    }
}
#endif                          /* DEBUGMALLOC_EXTENSIONS */
#endif                          /* DEBUGMALLOC */