/*
	efuns_main.c: this file contains the efunctions called from
	inside eval_instruction() in interpret.c.  Note: if you are adding
    local efunctions that are specific to your driver, you would be better
    off adding them to a separate source file.  Doing so will make it much
    easier for you to upgrade (won't have to patch this file).  Be sure
    to #include "lpc_incl.h" in that separate source file.
*/

#include "std.h"
#include "efuns_main.h"
#include "file_incl.h"
#include "file.h"
#include "comm.h"
#include "parse.h"
#include "sprintf.h"
#include "backend.h"
#include "port.h"
#include "swap.h"
#include "otable.h"
#include "crc32.h"
#include "reclaim.h"
#include "dumpstat.h"
#include "call_out.h"
#include "ed.h"
#include "md.h"
#include "master.h"
#include "efun_protos.h"
#include "add_action.h"
#ifdef LPC_TO_C
#include "interface.h"
#include "compile_file.h"
#endif

int call_origin = 0;

int data_size PROT((object_t * ob));
void reload_object PROT((object_t * obj));

#ifdef F_ALL_INVENTORY
void
f_all_inventory PROT((void))
{
    array_t *vec = all_inventory(sp->u.ob, 0);
    free_object(sp->u.ob, "f_all_inventory");
    sp->type = T_ARRAY;
    sp->u.arr = vec;
}
#endif

#ifdef F_ALLOCATE
void
f_allocate PROT((void))
{
    if (st_num_arg == 2) {
	(sp-1)->u.arr = allocate_array2((sp-1)->u.number, sp);
	pop_stack();
    } else {
	sp->u.arr = allocate_array(sp->u.number);
    }
    sp->type = T_ARRAY;
}
#endif

#ifdef F_ALLOCATE_BUFFER
void
f_allocate_buffer PROT((void))
{
    buffer_t *buf;

    buf = allocate_buffer(sp->u.number);
    if (buf) {
	pop_stack();
	push_refed_buffer(buf);
    } else {
	assign_svalue(sp, &const0);
    }
}
#endif

#ifdef F_ALLOCATE_MAPPING
void
f_allocate_mapping PROT((void))
{
    array_t *arr;
    
    if (st_num_arg == 2) {
	if ((sp-1)->type != T_ARRAY)
	    error("Bad argument 1 to allocate_mapping()\n");
	if (sp->type == T_ARRAY) {
	    arr = (sp-1)->u.arr;
	    if (sp->u.arr->size != arr->size)
		error("Arrays passed to allocate_mapping() must be the same size\n");
	    (sp-1)->u.map = mkmapping(arr, sp->u.arr);
	} else {
	    arr = (sp-1)->u.arr;
	    (sp-1)->u.map = allocate_mapping2(arr, sp);
	}
	pop_stack();
	free_array(arr);
    } else if (sp->type == T_NUMBER) {
	sp->u.map = allocate_mapping(sp->u.number);
    } else {
	error("Bad argument 1 to allocate_mapping()\n");
    }
    sp->type = T_MAPPING;
}
#endif

#ifdef F_BIND
void
f_bind PROT((void))
{
    object_t *ob = sp->u.ob;
    funptr_t *old_fp = (sp-1)->u.fp;
    funptr_t *new_fp;
    svalue_t *res;

    if (ob == old_fp->hdr.owner) {
	/* no change */
	free_object(ob, "bind nop");
	sp--;
	return;
    }
    
    if (old_fp->hdr.type == (FP_LOCAL | FP_NOT_BINDABLE))
	error("Illegal to rebind a pointer to a local function.\n");
    if (old_fp->hdr.type & FP_NOT_BINDABLE)
	error("Illegal to rebind a functional that references globals or local functions.\n");
    
    /* the object doing the binding */
    push_object(current_object);

    /* the old owner */
    push_object(old_fp->hdr.owner);

    /* the new owner */
    push_object(ob);
    
    res = apply_master_ob(APPLY_VALID_BIND, 3);
    if (!MASTER_APPROVED(res))
	error("Master object denied permission to bind() function pointer.\n");
    
    new_fp = ALLOCATE(funptr_t, TAG_FUNP, "f_bind");
    *new_fp = *old_fp;
    new_fp->hdr.ref = 1;
    new_fp->hdr.owner = ob; /* one ref from being on stack */
    if (new_fp->hdr.args)
	new_fp->hdr.args->ref++;
    if ((old_fp->hdr.type & 0x0f) == FP_FUNCTIONAL) {
	new_fp->f.functional.prog->func_ref++;
	debug(d_flag, ("add func ref /%s: now %i\n",
		   new_fp->f.functional.prog->name,
		   new_fp->f.functional.prog->func_ref));
    }

    free_funp(old_fp);
    sp--;
    sp->u.fp = new_fp;
}
#endif

#ifdef F_CACHE_STATS
static void print_cache_stats P1(outbuffer_t *, ob)
{
    outbuf_add(ob, "Function cache information\n");
    outbuf_add(ob, "-------------------------------\n");
    outbuf_addv(ob, "%% cache hits:    %10.2f\n",
	     100 * ((double) apply_low_cache_hits / apply_low_call_others));
    outbuf_addv(ob, "call_others:     %10lu\n", apply_low_call_others);
    outbuf_addv(ob, "cache hits:      %10lu\n", apply_low_cache_hits);
    outbuf_addv(ob, "cache size:      %10lu\n", APPLY_CACHE_SIZE);
    outbuf_addv(ob, "slots used:      %10lu\n", apply_low_slots_used);
    outbuf_addv(ob, "%% slots used:    %10.2f\n",
		100 * ((double) apply_low_slots_used / APPLY_CACHE_SIZE));
    outbuf_addv(ob, "collisions:      %10lu\n", apply_low_collisions);
    outbuf_addv(ob, "%% collisions:    %10.2f\n",
	     100 * ((double) apply_low_collisions / apply_low_call_others));
}

void f_cache_stats PROT((void))
{
    outbuffer_t ob;

    outbuf_zero(&ob);
    print_cache_stats(&ob);
    outbuf_push(&ob);
}
#endif

#ifdef F__CALL_OTHER
 /* enhanced call_other written 930314 by Luke Mewburn <zak@rmit.edu.au> */
void
f__call_other PROT((void))
{
    svalue_t *arg;
    char *funcname;
    int i;
    int num_arg = st_num_arg;
    object_t *ob;

    if (current_object->flags & O_DESTRUCTED) {	/* No external calls allowed */
	pop_n_elems(num_arg);
	push_undefined();
	return;
    }
    arg = sp - num_arg + 1;
    if (arg[1].type == T_STRING)
	funcname = arg[1].u.string;
    else {			/* must be T_ARRAY then */
        array_t *v = arg[1].u.arr;
        svalue_t *sv;

        check_for_destr(v);
        if (((i = v->size) < 1) || !((sv = v->item)->type == T_STRING))
            error("call_other: 1st elem of array for arg 2 must be a string\n");
        funcname = sv->u.string;
        num_arg = 2 + merge_arg_lists(num_arg - 2, v, 1);
    }

    if (arg[0].type == T_OBJECT)
	ob = arg[0].u.ob;
    else if (arg[0].type == T_ARRAY) {
	array_t *ret;

	ret = call_all_other(arg[0].u.arr, funcname, num_arg - 2);
        pop_stack();
        free_array(arg->u.arr);
        sp->u.arr = ret;
	return;
    } else {
	ob = find_object(arg[0].u.string);
	if (!ob || !object_visible(ob))
	    error("call_other() couldn't find object\n");
    }
    /* Send the remaining arguments to the function. */
#ifdef TRACE
    if (TRACEP(TRACE_CALL_OTHER)) {
	do_trace("Call other ", funcname, "\n");
    }
#endif
    call_origin = ORIGIN_CALL_OTHER;
    if (apply_low(funcname, ob, num_arg - 2) == 0) {	/* Function not found */
	pop_2_elems();
	push_undefined();
	return;
    }
    /*
     * The result of the function call is on the stack.  So is the function
     * name and object that was called, though. These have to be removed.
     */
    free_svalue(--sp, "f_call_other:1");
    free_svalue(--sp, "f_call_other:2");
    *sp = *(sp+2);
    return;
}
#endif

#ifdef F_CALL_OUT
void
f_call_out PROT((void))
{
    svalue_t *arg = sp - st_num_arg + 1;
    int num = st_num_arg - 2;
#ifdef CALLOUT_HANDLES
    int ret;

    if (!(current_object->flags & O_DESTRUCTED)) {
	ret = new_call_out(current_object, arg, arg[1].u.number, num, arg + 2);
	/* args have been transfered; don't free them;
	   also don't need to free the int */
	sp -= num + 1;
    } else {
	ret = 0;
	pop_n_elems(num);
	sp--;
    }
    /* the function */
    free_svalue(sp, "call_out");
    put_number(ret);
#else
    if (!(current_object->flags & O_DESTRUCTED)) {
	new_call_out(current_object, arg, arg[1].u.number, num, arg + 2);
	sp -= num + 1;
    } else {
	pop_n_elems(num);
	sp--;
    }
    free_svalue(sp--, "call_out");
#endif
}
#endif

#ifdef F_CALL_OUT_INFO
void
f_call_out_info PROT((void))
{
    push_refed_array(get_all_call_outs());
}
#endif

#if defined(F_CALL_STACK) || defined(F_ORIGIN)
static char *origin_name P1(int, orig) {
    /* FIXME: this should use ffs() if available (BSD) */
    int i = 0;
    static char *origins[] = {
	"driver",
	"local",
	"call_other",
	"simul",
	"internal",
	"efun",
	"function pointer",
	"functional"
    };
    while (orig >>= 1) i++;
    return origins[i];
}
#endif

#ifdef F_CALL_STACK
void
f_call_stack PROT((void))
{
    int i, n = csp - &control_stack[0] + 1;
    array_t *ret;

    if (sp->u.number < 0 || sp->u.number > 3)
	error("First argument of call_stack() must be 0, 1, 2, or 3.\n");

    ret = allocate_empty_array(n);
    
    switch (sp->u.number) {
    case 0:
	ret->item[0].type = T_STRING;
	ret->item[0].subtype = STRING_MALLOC;
	ret->item[0].u.string = add_slash(current_prog->name);
	for (i = 1; i < n; i++) {
	    ret->item[i].type = T_STRING;
	    ret->item[i].subtype = STRING_MALLOC;
	    ret->item[i].u.string = add_slash((csp - i + 1)->prog->name);
	}
	break;
    case 1:
	ret->item[0].type = T_OBJECT;
	ret->item[0].u.ob = current_object;
	add_ref(current_object, "f_call_stack: curr");
	for (i = 1; i < n; i++) {
	    ret->item[i].type = T_OBJECT;
	    ret->item[i].u.ob = (csp - i + 1)->ob;
	    add_ref((csp - i + 1)->ob, "f_call_stack");
	}
	break;
    case 2:
	for (i = 0; i < n; i++) {
	    ret->item[i].type = T_STRING;
	    if (((csp - i)->framekind & FRAME_MASK) == FRAME_FUNCTION) {
		program_t *prog = (i ? (csp-i+1)->prog : current_prog);
		int index = (csp-i)->fr.table_index;
		function_t *cfp = &prog->function_table[index];

		ret->item[i].subtype = STRING_SHARED;
		ret->item[i].u.string = cfp->name;
		ref_string(cfp->name);
	    } else {
		ret->item[i].subtype = STRING_CONSTANT;
		ret->item[i].u.string = (((csp - i)->framekind & FRAME_MASK) == FRAME_CATCH) ? "CATCH" : "<function>";
	    }
	}
	break;
    case 3:
	ret->item[0].type = T_STRING;
	ret->item[0].subtype = STRING_CONSTANT;
	ret->item[0].u.string = origin_name(caller_type);
	
	for (i = 1; i < n; i++) {
	    ret->item[i].type = T_STRING;
	    ret->item[i].subtype = STRING_CONSTANT;
	    ret->item[i].u.string = origin_name((csp-i+1)->caller_type);
	}
	break;
    }
    put_array(ret);
}
#endif

#ifdef F_CAPITALIZE
void
f_capitalize PROT((void))
{
    if (uislower(sp->u.string[0])) {
	unlink_string_svalue(sp);
	sp->u.string[0] = toupper((unsigned char)sp->u.string[0]);
    }
}
#endif

#ifdef F_CHILDREN
void
f_children PROT((void))
{
    array_t *vec;

    vec = children(sp->u.string);
    free_string_svalue(sp);
    put_array(vec);
}
#endif

#ifdef F_CLASSP
void
f_classp PROT((void))
{
    if (sp->type == T_CLASS) {
	free_class(sp->u.arr);
	*sp = const1;
    } else {
	free_svalue(sp, "f_classp");
	*sp = const0;
    }
}
#endif

#ifdef F_CLEAR_BIT
void
f_clear_bit PROT((void))
{
    char *str;
    int len, ind, bit;

    if (sp->u.number > MAX_BITS)
        error("clear_bit() bit requested : %d > maximum bits: %d\n", sp->u.number, MAX_BITS);
    bit = (sp--)->u.number;
    if (bit < 0)
	error("Bad argument 2 (negative) to clear_bit().\n");
    ind = bit / 6;
    bit %= 6;
    len = SVALUE_STRLEN(sp);
    if (ind >= len) 
	return;         /* return first arg unmodified */
    unlink_string_svalue(sp);
    str = sp->u.string;

    if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
        error("Illegal bit pattern in clear_bit character %d\n", ind);
    str[ind] = ((str[ind] - ' ') & ~(1 << bit)) + ' ';
}
#endif

#ifdef F_CLONEP
void
f_clonep PROT((void))
{
    if ((sp->type == T_OBJECT) && (sp->u.ob->flags & O_CLONE)) {
        free_object(sp->u.ob, "f_clonep");
        *sp = const1;
    } else {
        free_svalue(sp, "f_clonep");
        *sp = const0;
    }
}
#endif

#ifdef F__NEW
void
f__new PROT((void))
{
    svalue_t *arg = sp - st_num_arg + 1;
    object_t *ob;
    
    ob = clone_object(arg->u.string, st_num_arg - 1);
    free_string_svalue(sp);
    if (ob) {
	put_unrefed_undested_object(ob, "f_clone_object");
    } else *sp = const0;
}
#endif

#ifdef F_CP
void
f_cp PROT((void))
{
    int i;

    i = copy_file(sp[-1].u.string, sp[0].u.string);
    free_string_svalue(sp--);
    free_string_svalue(sp);
    put_number(i);
}
#endif

#ifdef F_CRC32
void
f_crc32 PROT((void))
{
    int len;
    unsigned char *buf;
    UINT32 crc;

    if (sp->type == T_STRING) {
        len = SVALUE_STRLEN(sp);
        buf = (unsigned char *) sp->u.string;
#ifndef NO_BUFFER_TYPE
    } else if (sp->type == T_BUFFER) {
        len = sp->u.buf->size;
        buf = sp->u.buf->item;
#endif
    } else {
#ifdef NO_BUFFER_TYPE
        bad_argument(sp, T_STRING, 1, F_CRC32);
#else
        bad_argument(sp, T_STRING | T_BUFFER, 1, F_CRC32);
#endif
    }
    crc = compute_crc32(buf, len);
    free_svalue(sp, "f_crc32");
    put_number(crc);
}
#endif

#ifdef F_CTIME
void
f_ctime PROT((void))
{
    char *cp, *nl, *p;
    int l;
    
    cp = time_string((time_t)sp->u.number);

    if ((nl = strchr(cp, '\n')))
	l = nl - cp;
    else
	l = strlen(cp);

    p = new_string(l, "f_ctime");
    strncpy(p, cp, l);
    p[l] = '\0';
    put_malloced_string(p);
}
#endif

#ifdef F_DEEP_INHERIT_LIST
void
f_deep_inherit_list PROT((void))
{
    array_t *vec;

    if (!(sp->u.ob->flags & O_SWAPPED)) {
        vec = deep_inherit_list(sp->u.ob);
    } else {
        vec = &the_null_array;
    }
    free_object(sp->u.ob, "f_deep_inherit_list");
    put_array(vec);
}
#endif

#ifdef DEBUG_MACRO

#ifdef F_SET_DEBUG_LEVEL
void
f_set_debug_level PROT((void))
{
    if (sp->type == T_STRING) {
	debug_level_set(sp->u.string);
	free_string_svalue(sp--);
    } else
	debug_level = (sp--)->u.number;
}

void
f_clear_debug_level PROT((void)) {
    debug_level_clear(sp->u.string);
}

void     
f_debug_levels PROT((void)) {
    /* not in debug.h since debug.h is included in many places that don't
       know about mapping_t */
    mapping_t *debug_levels PROT((void));
    
    push_refed_mapping(debug_levels());
}
#endif

#endif

#ifdef F_DEEP_INVENTORY
void
f_deep_inventory PROT((void))
{
    array_t *vec;

    vec = deep_inventory(sp->u.ob, 0);
    free_object(sp->u.ob, "f_deep_inventory");
    put_array(vec);
}
#endif

#ifdef F_DESTRUCT
void
f_destruct PROT((void))
{
    destruct_object(sp->u.ob);
    sp--; /* Ok since the object was removed from the stack */
}
#endif

#ifdef F_DUMPALLOBJ
void
f_dumpallobj PROT((void))
{
    if (st_num_arg) {
        dumpstat(sp->u.string);
        free_string_svalue(sp--);
    } else {
        dumpstat("/OBJ_DUMP");
    }
}
#endif

#ifdef F_ED
void
f_ed PROT((void))
{
    if (!command_giver || !command_giver->interactive) {
	pop_n_elems(st_num_arg);
        return;
    }

    if (!st_num_arg) {
        /* ed() */
        ed_start(0, 0, 0, 0, 0);
    } else if (st_num_arg == 1) {
        /* ed(fname) */
        ed_start(sp->u.string, 0, 0, 0, 0);
	pop_stack();
    } else if (st_num_arg == 2) {
        /* ed(fname,exitfn) */
        ed_start((sp - 1)->u.string, 0, sp->u.string, 0, current_object);
        pop_2_elems();
    } else if (st_num_arg == 3) {
        /* ed(fname,exitfn,restricted) / ed(fname,writefn,exitfn) */
        if (sp->type == T_NUMBER) {
            ed_start((sp - 2)->u.string, 0, (sp - 1)->u.string, sp->u.number,
                     current_object);
	} else if (sp->type == T_STRING) {
            ed_start((sp - 2)->u.string, (sp - 1)->u.string, sp->u.string, 0,
                     current_object);
	} else {
            bad_argument(sp, T_NUMBER | T_STRING, 3, F_ED);
	}
        pop_3_elems();
    } else {                    /* st_num_arg == 4 */
        /* ed(fname,writefn,exitfn,restricted) */
        if (!((sp - 1)->type == T_STRING))
            bad_argument(sp - 1, T_STRING, 3, F_ED);
        if (!(sp->type == T_NUMBER))
            bad_argument(sp, T_NUMBER, 4, F_ED);
        ed_start((sp - 3)->u.string, (sp - 2)->u.string, (sp - 1)->u.string,
                 sp->u.number, current_object);
	pop_n_elems(4);
    }
}
#endif

#ifdef F_ED_CMD
void f_ed_cmd PROT((void))
{
    char *res;
    
    if (current_object->flags & O_DESTRUCTED)
	error("destructed objects can't use ed.\n");

    if (!(current_object->flags & O_IN_EDIT))
	error("ed_cmd() called with no ed session active.\n");

    res = object_ed_cmd(current_object, sp->u.string);

    free_string_svalue(sp);
    if (res) {
	sp->subtype = STRING_MALLOC;
	sp->u.string = res;
    } else {
	sp->subtype = STRING_CONSTANT;
	sp->u.string = "";
    }
}
#endif

#ifdef F_ED_START
void f_ed_start PROT((void))
{
    char *res;
    char *fname;
    int restr = 0;

    if (st_num_arg == 2)
	restr = (sp--)->u.number;

    if (st_num_arg)
	fname = sp->u.string;
    else
	fname = 0;

    if (current_object->flags & O_DESTRUCTED)
	error("destructed objects can't use ed.\n");

    if (current_object->flags & O_IN_EDIT)
	error("ed_start() called while an ed session is already started.\n");

    res = object_ed_start(current_object, fname, restr);

    if (fname) free_string_svalue(sp);
    else {
	STACK_INC;
	sp->type = T_STRING;
    }
    
    if (res) {
	sp->subtype = STRING_MALLOC;
	sp->u.string = res;
    } else {
	sp->subtype = STRING_CONSTANT;
	sp->u.string = "";
    }
}
#endif

#ifdef F_ENABLE_WIZARD
void
f_enable_wizard PROT((void))
{
    if (current_object->interactive)
	current_object->flags |= O_IS_WIZARD;
}
#endif

#ifdef F_ERROR
void
f_error PROT((void))
{
    int l = SVALUE_STRLEN(sp);
    char err_buf[2048];

    if (sp->u.string[l - 1] == '\n')
	l--;
    if (l > 2045) l = 2045;
    
    err_buf[0] = '*';
    strncpy(err_buf + 1, sp->u.string, l);
    err_buf[l + 1] = '\n';
    err_buf[l + 2] = 0;
    
    error_handler(err_buf);
}
#endif

#ifdef F_DISABLE_WIZARD
void
f_disable_wizard PROT((void))
{
    if (current_object->interactive)
	current_object->flags &= ~O_IS_WIZARD;
}
#endif

#ifdef F_ENVIRONMENT
void
f_environment PROT((void))
{
    object_t *ob;
    
    if (st_num_arg) {
        if ((ob = sp->u.ob)->flags & O_DESTRUCTED)
            error("environment() of destructed object.\n");
        ob = ob->super;
        free_object((sp--)->u.ob, "f_environment");
    } else if (!(current_object->flags & O_DESTRUCTED))
        ob = current_object->super;
    else error("environment() of destructed object.\n");

    if (ob && object_visible(ob)) push_object(ob);
    else push_number(0);
}
#endif

#ifdef F_EXEC
void
f_exec PROT((void))
{
    int i;

    i = replace_interactive((sp - 1)->u.ob, sp->u.ob);

    /* They might have been destructed */
    if (sp->type == T_OBJECT)
	free_object(sp->u.ob, "f_exec:1");
    if ((--sp)->type == T_OBJECT)
	free_object(sp->u.ob, "f_exec:2");
    put_number(i);
}
#endif

#ifdef F_EXPLODE
void
f_explode PROT((void))
{
    array_t *vec;

    vec = explode_string((sp - 1)->u.string, SVALUE_STRLEN(sp-1),
			 sp->u.string, SVALUE_STRLEN(sp));
    free_string_svalue(sp--);
    free_string_svalue(sp);
    put_array(vec);
}
#endif

#ifdef F_FILE_NAME
void
f_file_name PROT((void))
{
    char *res;

    /* This function now returns a leading '/' */
    res = (char *) add_slash(sp->u.ob->name);
    free_object(sp->u.ob, "f_file_name");
    put_malloced_string(res);
}
#endif

#ifdef F_FILE_SIZE
void
f_file_size PROT((void))
{
    int i = file_size(sp->u.string);
    free_string_svalue(sp);
    put_number(i);
}
#endif

#ifdef F_FILTER
void
f_filter PROT((void))
{
    svalue_t *arg = sp - st_num_arg + 1;

    if (arg->type == T_MAPPING) filter_mapping(arg, st_num_arg);
    else if (arg->type == T_STRING) filter_string(arg, st_num_arg);
    else filter_array(arg, st_num_arg);
}
#endif

#ifdef F_FIND_CALL_OUT
void
f_find_call_out PROT((void))
{
    int i;
#ifdef CALLOUT_HANDLES
    if (sp->type == T_NUMBER) {
	i = find_call_out_by_handle(sp->u.number);
    } else { /* T_STRING */
#endif
	i = find_call_out(current_object, sp->u.string);
	free_string_svalue(sp);
#ifdef CALLOUT_HANDLES
    }
#endif
    put_number(i);
}
#endif

#ifdef F_FIND_OBJECT
void
f_find_object PROT((void))
{
    object_t *ob;
    
    if ((sp--)->u.number)
        ob = find_object(sp->u.string);
    else
        ob = find_object2(sp->u.string);
    free_string_svalue(sp);
    if (ob && object_visible(ob)) { 
	/* find_object only returns undested objects */
	put_unrefed_undested_object(ob, "find_object"); 
    } else
	*sp = const0;
}
#endif

#ifdef F_FUNCTION_PROFILE
/* f_function_profile: John Garnett, 1993/05/31, 0.9.17.3 */
void
f_function_profile PROT((void))
{
    array_t *vec;
    mapping_t *map;
    program_t *prog;
    int nf, j;
    object_t *ob;

    ob = sp->u.ob;
    if (ob->flags & O_SWAPPED) {
	load_ob_from_swap(ob);
    }
    prog = ob->prog;
    nf = prog->num_functions_defined;
    vec = allocate_empty_array(nf);
    for (j = 0; j < nf; j++) {
	map = allocate_mapping(3);
	add_mapping_pair(map, "calls", prog->function_table[j].calls);
	add_mapping_pair(map, "self", prog->function_table[j].self
			 - prog->function_table[j].children);
	add_mapping_pair(map, "children", prog->function_table[j].children);
	add_mapping_shared_string(map, "name", prog->function_table[j].name);
	vec->item[j].type = T_MAPPING;
	vec->item[j].u.map = map;
    }
    free_object(ob, "f_function_profile");
    put_array(vec);
}
#endif

#ifdef F_FUNCTION_EXISTS
void
f_function_exists PROT((void))
{
    char *str, *res;
    int l;
    object_t *ob;
    int flag = 0;
    
    if (st_num_arg > 1) {
	if (st_num_arg > 2)
	    flag = (sp--)->u.number;
	ob = (sp--)->u.ob;
	free_object(ob, "f_function_exists");
    } else {
	if (current_object->flags & O_DESTRUCTED) {
	    free_string_svalue(sp);
	    *sp = const0;
	    return;
	}
	ob = current_object;
    }

    str = function_exists(sp->u.string, ob, flag);
    free_string_svalue(sp);
    if (str) {
	l = SHARED_STRLEN(str) - 2; /* no .c */
	res = new_string(l + 1, "function_exists");
	res[0] = '/';
	strncpy(res + 1, str, l);
	res[l + 1] = 0;

	sp->subtype = STRING_MALLOC;
        sp->u.string = res;
    } else *sp = const0;
}
#endif

#ifdef F_GENERATE_SOURCE
void f_generate_source PROT((void))
{
    int i;

    if (st_num_arg == 2) {
	i = generate_source(sp - 1, sp->u.string);
	pop_stack();
    } else
	i = generate_source(sp, 0);
    free_svalue(sp, "f_generate_source");
    put_number(i);
}
#endif

#ifdef F_GET_CHAR
void
f_get_char PROT((void))
{
    svalue_t *arg;
    int i, tmp;
    int flag;

    arg = sp - st_num_arg + 1;  /* Points arg at first argument. */
    if (st_num_arg == 1 || !(arg[1].type == T_NUMBER)) {
        tmp = 0;
        flag = 0;
    } else {
        tmp = 1;
        st_num_arg--;           /* Don't count the flag as an arg */
        flag = arg[1].u.number;
    }
    st_num_arg--;
    i = get_char(arg, flag, st_num_arg, &arg[1 + tmp]);
    free_svalue(arg, "f_get_char");
    (sp = arg)->type = T_NUMBER;
    sp->u.number = i;
}
#endif

#ifdef F_GET_CONFIG
void
f_get_config PROT((void))
{
    if (!get_config_item(sp, sp))
	error("Bad argument to get_config()\n");
}
#endif

#ifdef F_GET_DIR
void
f_get_dir PROT((void))
{
    array_t *vec;

    vec = get_dir((sp - 1)->u.string, sp->u.number);
    free_string_svalue(--sp);
    if (vec) { put_array(vec); }
    else *sp = const0;
}
#endif

#ifdef F_IMPLODE
void
f_implode PROT((void))
{
    array_t *arr;
    int flag;
    svalue_t *args;
    
    if (st_num_arg == 3) {
	args = (sp - 2);
	if (args[1].type == T_STRING)
	    error("Third argument to implode() is illegal with implode(array, string)\n");
	flag = 1;
    } else {
	args = (sp - 1);
	flag = 0;
    }
    arr = args->u.arr;
    check_for_destr(arr);

    if (args[1].type == T_STRING) {
	/* st_num_arg == 2 here */
	char *str;
	    
	str = implode_string(arr, sp->u.string,
			     SVALUE_STRLEN(sp));
	free_string_svalue(sp--);
	free_array(arr);
	put_malloced_string(str);
    } else { /* function */
	funptr_t *funp = args[1].u.fp;
	    
	/* this pulls the extra arg off the stack if it exists */
	implode_array(funp, arr, args, flag);
	sp--;
	free_funp(funp);
	free_array(arr);
    }
}
#endif

#ifdef F_IN_EDIT
void
f_in_edit PROT((void))
{
    char *fn;
    ed_buffer_t *eb = 0;

#ifdef OLD_ED
    if (sp->u.ob->interactive)
	eb = sp->u.ob->interactive->ed_buffer;
#else
    if (sp->u.ob->flags & O_IN_EDIT)
	eb = find_ed_buffer(sp->u.ob);
#endif
    if (eb && (fn = eb->fname)) {
	free_object(sp->u.ob, "f_in_edit:1");
	put_malloced_string(add_slash(fn));
	return;
    }
    free_object(sp->u.ob, "f_in_edit:1");
    *sp = const0;
    return;
}
#endif

#ifdef F_IN_INPUT
void
f_in_input PROT((void))
{
    int i;

    i = sp->u.ob->interactive && sp->u.ob->interactive->input_to;
    free_object(sp->u.ob, "f_in_input");
    put_number(i != 0);
}
#endif

#ifdef F_INHERITS
int
inherits P2(program_t *, prog, program_t *, thep)
{
    int j, k = prog->num_inherited;
    program_t *pg;

    for (j = 0; j < k; j++) {
        if ((pg = prog->inherit[j].prog) == thep)
            return 1;
        if (!strcmp(pg->name, thep->name))
            return 2;
        if (inherits(pg, thep))
            return 1;
    }
    return 0;
}

void
f_inherits PROT((void))
{
    object_t *ob, *base;
    int i;

    base = (sp--)->u.ob;
    ob = find_object2(sp->u.string);
    if (!ob || (ob->flags & O_SWAPPED)) {
	free_object(base, "f_inherits");
        assign_svalue(sp, &const0);
        return;
    }
    if (base->flags & O_SWAPPED)
        load_ob_from_swap(base);
    i = inherits(base->prog, ob->prog);
    free_object(base, "f_inherits");
    free_string_svalue(sp);
    put_number(i);
}
#endif

#ifdef F_SHALLOW_INHERIT_LIST
void
f_shallow_inherit_list PROT((void))
{
    array_t *vec;

    if (!(sp->u.ob->flags & O_SWAPPED)) {
        vec = inherit_list(sp->u.ob);
    } else {
        vec = &the_null_array;
    }
    free_object(sp->u.ob, "f_inherit_list");
    put_array(vec);
}
#endif

#ifdef F_INPUT_TO
void
f_input_to PROT((void))
{
    svalue_t *arg;
    int i, tmp;
    int flag;

    arg = sp - st_num_arg + 1;  /* Points arg at first argument. */
    if ((st_num_arg == 1) || !(arg[1].type == T_NUMBER)) {
        tmp = flag = 0;
    } else {
        tmp = 1;
        st_num_arg--;           /* Don't count the flag as an arg */
        flag = arg[1].u.number;
    }
    st_num_arg--;               /* Don't count the name of the func either. */
    i = input_to(arg, flag, st_num_arg, &arg[1 + tmp]);
    free_svalue(arg, "f_input_to");
    (sp = arg)->type = T_NUMBER;
    sp->u.number = i;
}
#endif

#ifdef F_INTERACTIVE
void
f_interactive PROT((void))
{
    int i;

    i = (sp->u.ob->interactive != 0);
    free_object(sp->u.ob, "f_interactive");
    put_number(i);
}
#endif

#ifdef F_INTP
void
f_intp PROT((void))
{
    if (sp->type == T_NUMBER) sp->u.number = 1;
    else {
	free_svalue(sp, "f_intp");
        put_number(0);
    }
}
#endif

#ifdef F_FUNCTIONP
void
f_functionp PROT((void))
{
    int i;
    
    if (sp->type == T_FUNCTION) {
        i = sp->u.fp->hdr.type;
        if (sp->u.fp->hdr.args) 
	    i |= FP_HAS_ARGUMENTS;
	if (!sp->u.fp->hdr.owner || (sp->u.fp->hdr.owner->flags & O_DESTRUCTED))
	    i |= FP_OWNER_DESTED;
        free_funp(sp->u.fp);
        put_number(i);
        return;
    }
    assign_svalue(sp, &const0);
}
#endif

#ifdef F_KEYS
void
f_keys PROT((void))
{
    array_t *vec;

    vec = mapping_indices(sp->u.map);
    free_mapping(sp->u.map);
    put_array(vec);
}
#endif

#ifdef F_VALUES
void
f_values PROT((void))
{
    array_t *vec;

    vec = mapping_values(sp->u.map);
    free_mapping(sp->u.map);
    put_array(vec);
}
#endif

#ifdef F_LINK
void
f_link PROT((void))
{
    svalue_t *ret, *arg;
    int i;

    arg = sp;
    push_svalue(arg - 1);
    push_svalue(arg);
    ret = apply_master_ob(APPLY_VALID_LINK, 2);
    if (MASTER_APPROVED(ret))
        i = do_rename((sp - 1)->u.string, sp->u.string, F_LINK);
    else
	i = 0;
    (--sp)->type = T_NUMBER;
    sp->u.number = i;
    sp->subtype = 0;
}
#endif				/* F_LINK */

#ifdef F_LOWER_CASE
void
f_lower_case PROT((void))
{
    char *str;

    str = sp->u.string;
    /* find first upper case letter, if any */
    for (; *str; str++) {
	if (uisupper(*str)) {
	    int l = str - sp->u.string;
	    unlink_string_svalue(sp);
	    str = sp->u.string + l;
	    *str = tolower((unsigned char)*str);
	    for (str++; *str; str++) {
		if (uisupper(*str))
		    *str = tolower((unsigned char)*str);
	    }
	    return;
	}
    }
}
#endif

#ifdef F_LPC_INFO
void f_lpc_info PROT((void))
{
    outbuffer_t out;

    interface_t **p = interface;
    object_t *ob;

    outbuf_zero(&out);
    outbuf_addv(&out, "%30s  Loaded  Using compiled program\n", "Program");
    while (*p) {
	outbuf_addv(&out, "%30s: ", (*p)->fname);
	ob = lookup_object_hash((*p)->fname);
	if (ob) {
	    if (ob->flags & O_COMPILED_PROGRAM) {
		outbuf_add(&out, " No\n");
	    } else if (ob->flags & O_SWAPPED) {
		outbuf_add(&out, " Yes      Swapped\n");
	    } else if (ob->prog->program_size == 0) {
		outbuf_add(&out, " Yes      Yes\n");
	    } else {
		outbuf_add(&out, " Yes      No\n");
	    }
	} else {
	    outbuf_add(&out, "Something REALLY wierd happened; no record of the object.\n");
	}
	p++;
    }
    outbuf_push(&out);
}
#endif

#ifdef F_MALLOC_STATUS
void f_malloc_status PROT((void))
{
    outbuffer_t ob;

    outbuf_zero(&ob);

#ifdef BSDMALLOC
    outbuf_add(&ob, "Using BSD malloc");
#endif
#ifdef SMALLOC
    outbuf_add(&ob, "Using Smalloc");
#endif
#ifdef SYSMALLOC
    outbuf_add(&ob, "Using system malloc");
#endif
#ifdef DEBUGMALLOC
    outbuf_add(&ob, ", wrapped with debugmalloc");
#endif
#ifdef WRAPPEDMALLOC
    outbuf_add(&ob, ", wrapped with wrappedmalloc");
#endif
    outbuf_add(&ob, ".\n");
#ifdef DO_MSTATS
    show_mstats(&ob, "malloc_status()");
#endif
#if (defined(WRAPPEDMALLOC) || defined(DEBUGMALLOC))
    dump_malloc_data(&ob);
#endif
    outbuf_push(&ob);
}
#endif

#ifdef F_MAP_DELETE
void
f_map_delete PROT((void))
{
    mapping_delete((sp - 1)->u.map, sp);
    pop_stack();
#ifndef COMPAT_32
    free_mapping((sp--)->u.map);
#endif
}
#endif

#ifdef F_MAPP
void
f_mapp PROT((void))
{
    if (sp->type == T_MAPPING) {
        free_mapping(sp->u.map);
        *sp = const1;
    } else {
        free_svalue(sp, "f_mapp");
        *sp = const0;
    }
}
#endif

#ifdef F_MAP
void
f_map PROT((void))
{
    svalue_t *arg = sp - st_num_arg + 1;

    if (arg->type == T_MAPPING) map_mapping(arg, st_num_arg);
    else if (arg->type == T_ARRAY) map_array(arg, st_num_arg);
    else map_string(arg, st_num_arg);
}
#endif

#ifdef F_MASTER
void
f_master PROT((void))
{
    if (!master_ob)
	push_number(0);
    else
	push_object(master_ob);
}
#endif

/*
This efun searches a mapping for a path.  Each key is assumed to be a
string.  The value is completely arbitrary.  The efun finds the largest
matching path in the mapping.  Keys ended in '/' are assumed to match
paths with character that follow the '/', i.e. / is a wildcard for anything
below this directory.  DO NOT CHANGE THIS EFUN TIL YOU UNDERSTAND IT.  It
catches folks by suprise at first, but it is coded the way it is for a reason.
It effectively implements the search loop in TMI's access object as a single
efun.

        Cygnus
*/
#ifdef F_MATCH_PATH
void
f_match_path PROT((void))
{
    svalue_t *value;
    register char *src, *dst;
    svalue_t *nvalue;
    mapping_t *map;
    char *tmpstr;
    
    value = &const0u;

    tmpstr = DMALLOC(SVALUE_STRLEN(sp) + 1, TAG_STRING, "match_path");

    src = sp->u.string;
    dst = tmpstr;
    
    while (*src != '\0') {
	while (*src != '/' && *src != '\0')
	    *dst++ = *src++;
	if (*src == '/') {
	    while (*++src == '/');
	    if (*src != '\0' || dst == tmpstr)
		*dst++ = '/';
	}
	*dst = '\0';
	nvalue = find_string_in_mapping((sp - 1)->u.map, tmpstr);
	
	if (nvalue != &const0u)
	    value = nvalue;
    }

    FREE(tmpstr);
    /* Don't free mapping first, in case sometimes one uses a ref 1 mapping */
    /* Randor - 5/29/94 */
    free_string_svalue(sp--);
    map = sp->u.map;
    assign_svalue_no_free(sp, value);
    free_mapping(map);
}
#endif				/* F_MATCH_PATH */

#ifdef F_MEMBER_ARRAY
void
f_member_array PROT((void))
{
    array_t *v;
    int i;

    if (st_num_arg > 2) {
        i = (sp--)->u.number;
        if (i<0) bad_arg(3, F_MEMBER_ARRAY);
    } else i = 0;

    if (sp->type == T_STRING) {
        char *res;
        CHECK_TYPES(sp-1, T_NUMBER, 1, F_MEMBER_ARRAY);
        if (i > SVALUE_STRLEN(sp)) error("Index to start search from in member_array() is > string length.\n");
        if ((res = strchr(sp->u.string + i, (sp-1)->u.number)))
            i = res - sp->u.string;
        else
            i = -1;
        free_string_svalue(sp--);
    } else {
        int size = (v = sp->u.arr)->size;
        svalue_t *sv;
	svalue_t *find;
	int flen;

        find = (sp - 1);
	/* optimize a bit */
	if (find->type == T_STRING) {
	    /* *not* COUNTED_STRLEN() which can do a (costly) strlen() call */
	    if (find->subtype & STRING_COUNTED)
		flen = MSTR_SIZE(find->u.string);
	    else flen = 0;
	}

        for (; i < size; i++) {
            switch (find->type|(sv= v->item + i)->type) {
            case T_STRING:
		if (flen && (sv->subtype & STRING_COUNTED)
		    && flen != MSTR_SIZE(sv->u.string))
		    continue;
                if (strcmp(find->u.string, sv->u.string)) continue;
		break;
            case T_NUMBER:
                if (find->u.number == sv->u.number) break;
                continue;
            case T_REAL:
                if (find->u.real == sv->u.real) break;
                continue;
            case T_ARRAY:
                if (find->u.arr == sv->u.arr) break;
                continue;
            case T_OBJECT:
	    {
		if (sv->u.ob->flags & O_DESTRUCTED) {
		    assign_svalue(sv, &const0u);
		    continue;
		}
                if (find->u.ob == sv->u.ob) break;
                continue;
	    }
            case T_MAPPING:
                if (find->u.map == sv->u.map) break;
                continue;
            case T_FUNCTION:
                if (find->u.fp == sv->u.fp) break;
                continue;
#ifndef NO_BUFFER_TYPE
            case T_BUFFER:
                if (find->u.buf == sv->u.buf) break;
                continue;
#endif
            default:
                if (sv->type == T_OBJECT && (sv->u.ob->flags & O_DESTRUCTED)) {
                    assign_svalue(sv, &const0u);
                    if (find->type == T_NUMBER && !find->u.number) break;
		}
                continue;
	    }
            break;
	}
        if (i == size)
            i = -1;                     /* Return -1 for failure */
        free_array(v);
	free_svalue(find, "f_member_array");
	sp--;
    }
    put_number(i);
}
#endif

#ifdef F_MESSAGE
void
f_message PROT((void))
{
    array_t *use, *avoid;
    int num_arg = st_num_arg;
    svalue_t *args;

    args = sp - num_arg + 1;
    switch (args[2].type) {
    case T_OBJECT:
    case T_STRING:
	use = allocate_empty_array(1);
	use->item[0] = args[2];
	args[2].type = T_ARRAY;
	args[2].u.arr = use;
	break;
    case T_ARRAY:
	use = args[2].u.arr;
	break;
    case T_NUMBER:
	if (args[2].u.number == 0) {
	    int len = SVALUE_STRLEN(args + 1);
	    
	    /* this is really bad and probably should be rm'ed -Beek;
	     * on the other hand, we don't have a debug_message() efun yet.
	     * Well, there is one in contrib now ...
	     */
	    /* for compatibility (write() simul_efuns, etc)  -bobf */
	    if (len > LARGEST_PRINTABLE_STRING)
		error("Printable strings limited to length of %d.\n",
		      LARGEST_PRINTABLE_STRING);

	    add_message(command_giver, args[1].u.string, len);
	    pop_n_elems(num_arg);
	    return;
	}
    default:
	bad_argument(&args[2], T_OBJECT | T_STRING | T_ARRAY | T_NUMBER,
		     3, F_MESSAGE);
    }
    if (num_arg == 4) {
	switch (args[3].type) {
	case T_OBJECT:
	    avoid = allocate_empty_array(1);
	    avoid->item[0] = args[3];
	    args[3].type = T_ARRAY;
	    args[3].u.arr = avoid;
	    break;
	case T_ARRAY:
	    avoid = args[3].u.arr;
	    break;
	default:
	    avoid = &the_null_array;
	}
    } else
	avoid = &the_null_array;
    do_message(&args[0], &args[1], use, avoid, 1);
    pop_n_elems(num_arg);
}
#endif

#ifdef F_MKDIR
void
f_mkdir PROT((void))
{
    char *path;

    path = check_valid_path(sp->u.string, current_object, "mkdir", 1);
    if (!path || OS_mkdir(path, 0770) == -1) {
        free_string_svalue(sp);
        *sp = const0;
    }
    else{
        free_string_svalue(sp);
        *sp = const1;
    }
}
#endif

#ifdef F_MOVE_OBJECT
void
f_move_object PROT((void))
{
    object_t *o1, *o2;

    /* get destination */
    if (sp->type == T_OBJECT)
        o2 = sp->u.ob;
    else {
        if (!(o2 = find_object(sp->u.string)) || !object_visible(o2))
            error("move_object failed: could not find destination\n");
    }
    
    if ((o1 = current_object)->flags & O_DESTRUCTED)
	error("move_object(): can't move a destructed object\n");
    
    move_object(o1, o2);
    pop_stack();
}
#endif

#ifdef F_MUD_STATUS
void f_mud_status PROT((void))
{
    int tot, res, verbose = 0;
    outbuffer_t ob;

    outbuf_zero(&ob);
    verbose = (sp--)->u.number;

    if (reserved_area)
	res = RESERVED_SIZE;
    else
	res = 0;

    if (verbose) {
	char dir_buf[1024];
	FILE *testfp;

	if ((testfp = fopen(".mudos_test_file", "w"))) {
	    fclose(testfp);
	    outbuf_add(&ob, "Open-file-test succeeded.\n");
	    unlink(".mudos_test_file");
	} else {
	    outbuf_addv(&ob, "Open file test failed: %s\n", port_strerror(errno));
	}

	outbuf_addv(&ob, "current working directory: %s\n\n",
		    get_current_dir(dir_buf, 1024));
	outbuf_add(&ob, "add_message statistics\n");
	outbuf_add(&ob, "------------------------------\n");
	outbuf_addv(&ob, "Calls to add_message: %d   Packets: %d   Average packet size: %f\n\n",
	add_message_calls, inet_packets, (float) inet_volume / inet_packets);

	stat_living_objects(&ob);

#ifdef F_CACHE_STATS
	print_cache_stats(&ob);
	outbuf_add(&ob, "\n");
#endif
	print_swap_stats(&ob);
	outbuf_add(&ob, "\n");

        tot = show_otable_status(&ob, verbose);
        outbuf_add(&ob, "\n");
        tot += heart_beat_status(&ob, verbose);
        outbuf_add(&ob, "\n");
        tot += add_string_status(&ob, verbose);
        outbuf_add(&ob, "\n");
        tot += print_call_out_usage(&ob, verbose);
    } else {
	/* !verbose */
	outbuf_addv(&ob, "Sentences:\t\t\t%8d %8d\n", tot_alloc_sentence,
		    tot_alloc_sentence * sizeof(sentence_t));
#ifndef DEBUG
	outbuf_addv(&ob, "Objects:\t\t\t%8d %8d\n",
		    tot_alloc_object, tot_alloc_object_size);
#else
	outbuf_addv(&ob, "Objects:\t\t\t%8d %8d (%8d dangling)\n",
		    tot_alloc_object, tot_alloc_object_size, tot_dangling_object);
#endif
	outbuf_addv(&ob, "Prog blocks:\t\t\t%8d %8d\n",
		    total_num_prog_blocks, total_prog_block_size);
#ifdef ARRAY_STATS
	outbuf_addv(&ob, "Arrays:\t\t\t\t%8d %8d\n", num_arrays,
		    total_array_size);
#else
	outbuf_add(&ob, "<Array statistics disabled, no information available>\n");
#endif
	outbuf_addv(&ob, "Mappings:\t\t\t%8d %8d\n", num_mappings,
		    total_mapping_size);
	outbuf_addv(&ob, "Mappings(nodes):\t\t%8d\n", total_mapping_nodes);
	outbuf_addv(&ob, "Interactives:\t\t\t%8d %8d\n", num_user,
		    num_user * sizeof(interactive_t));

	tot = show_otable_status(&ob, verbose) +
	    heart_beat_status(&ob, verbose) +
	    add_string_status(&ob, verbose) +
	    print_call_out_usage(&ob, verbose);
    }

    tot += total_prog_block_size +
#ifdef ARRAY_STATS
	total_array_size +
#endif
	total_mapping_size +
	tot_alloc_sentence * sizeof(sentence_t) +
	tot_alloc_object_size +
	num_user * sizeof(interactive_t) +
	res;

    if (!verbose) {
	outbuf_add(&ob, "\t\t\t\t\t --------\n");
	outbuf_addv(&ob, "Total:\t\t\t\t\t %8d\n", tot);
    }
    outbuf_push(&ob);
}
#endif

#ifdef F_OBJECTP
void
f_objectp PROT((void))
{
    if (sp->type == T_OBJECT && !(sp->u.ob->flags & O_DESTRUCTED)) {
        free_object(sp->u.ob, "f_objectp");
        *sp = const1;
    } else {
        free_svalue(sp, "f_objectp");
        *sp = const0;
    }
}
#endif

#ifdef F_OPCPROF
void
f_opcprof PROT((void))
{
    if (st_num_arg == 1) {
        opcdump(sp->u.string);
        free_string_svalue(sp--);
    } else
	opcdump("/OPCPROF");
}
#endif

#ifdef F_ORIGIN
void
f_origin PROT((void))
{
    push_constant_string(origin_name(caller_type));
}
#endif

#ifdef F_POINTERP
void
f_pointerp PROT((void))
{
    if (sp->type == T_ARRAY) {
        free_array(sp->u.arr);
        *sp = const1;
    } else {
        free_svalue(sp, "f_pointerp");
        *sp = const0;
    }
}
#endif

#ifdef F_PRESENT
void
f_present PROT((void))
{
    int num_arg = st_num_arg;
    svalue_t *arg = sp - num_arg + 1;
    object_t *ob;
    
#if !defined(NO_RESETS) && defined(LAZY_RESETS)
    if (num_arg == 2) {
	try_reset(arg[1].u.ob);
    }
#endif
    ob = object_present(arg, num_arg == 1 ? 0 : arg[1].u.ob);
    pop_n_elems(num_arg);
    if (ob && object_visible(ob)) push_object(ob);
    else push_number(0);
}
#endif

#ifdef F_PREVIOUS_OBJECT
void
f_previous_object PROT((void))
{
    control_stack_t *p;
    int i;
    object_t *ob;
    
    if ((i = sp->u.number) > 0) {
        if (i >= CFG_MAX_CALL_DEPTH) {
            sp->u.number = 0;
            return;
	}
        ob = 0;
        p = csp;
        do {
            if ((p->framekind & FRAME_OB_CHANGE) && !(--i)) {
                ob = p->prev_ob;
                break;
	    }
	} while (--p >= control_stack);
    } else if (i == -1) {
        array_t *v;

        i = previous_ob ? 1 : 0;
        p = csp;
        do {
            if ((p->framekind & FRAME_OB_CHANGE) && p->prev_ob) i++;
	} while (--p >= control_stack);
        v = allocate_empty_array(i);
        p = csp;
        if (previous_ob) {
	    if (!(previous_ob->flags & O_DESTRUCTED)) {
		v->item[0].type = T_OBJECT;
		v->item[0].u.ob = previous_ob;
		add_ref(previous_ob, "previous_object(-1)");
	    } else v->item[0] = const0u;
            i = 1;
	} else i = 0;
        do {
            if ((p->framekind & FRAME_OB_CHANGE) && (ob = p->prev_ob)) {
		if (!(ob->flags & O_DESTRUCTED)) {
		    v->item[i].type = T_OBJECT;
		    v->item[i].u.ob = ob;
		    add_ref(ob, "previous_object(-1)");
		} else v->item[i] = const0u;
		i++;
	    }
	} while (--p >= control_stack);
        put_array(v);
        return;
    } else if (i < 0) {
        error("Illegal negative argument to previous_object()\n");
    } else
        ob = previous_ob;
    if (!ob || (ob->flags & O_DESTRUCTED))
        sp->u.number = 0;
    else {
        put_unrefed_undested_object(ob, "previous_object()");
    }
}
#endif

#ifdef F_PRINTF
void
f_printf PROT((void))
{
    int num_arg = st_num_arg;
    char *ret;
    
    if (command_giver) {
	ret = string_print_formatted((sp - num_arg + 1)->u.string,
				     num_arg - 1, sp - num_arg + 2);
	if (ret) {
	    tell_object(command_giver, ret, COUNTED_STRLEN(ret));
	    FREE_MSTR(ret);
	}
    }
    
    pop_n_elems(num_arg);
}
#endif

#ifdef F_PROCESS_STRING
void
f_process_string PROT((void))
{
    char *str;

    str = process_string(sp->u.string);
    if (str != sp->u.string) {
        free_string_svalue(sp);
        put_malloced_string(str);
    }
}
#endif

#ifdef F_PROCESS_VALUE
void
f_process_value PROT((void))
{
    svalue_t *ret;

    ret = process_value(sp->u.string);
    free_string_svalue(sp);
    if (ret) assign_svalue_no_free(sp, ret);
    else *sp = const0;
}
#endif

#ifdef F_QUERY_ED_MODE
void
f_query_ed_mode PROT((void))
{
    /* n = prompt for line 'n'
       0 = normal ed prompt
       -1 = not in ed
       -2 = more prompt */
    if (current_object->flags & O_IN_EDIT) {
	push_number(object_ed_mode(current_object));
    } else
	push_number(-1);
}
#endif

#ifdef F_QUERY_HOST_NAME
void
f_query_host_name PROT((void))
{
    char *tmp;

    if ((tmp = query_host_name()))
        push_constant_string(tmp);
    else
        push_number(0);
}
#endif

#ifdef F_QUERY_IDLE
void
f_query_idle PROT((void))
{
    int i;

    i = query_idle(sp->u.ob);
    free_object(sp->u.ob, "f_query_idle");
    put_number(i);
}
#endif

#ifdef F_QUERY_IP_NAME
void
f_query_ip_name PROT((void))
{
    char *tmp;

    tmp = query_ip_name(st_num_arg ? sp->u.ob : 0);
    if (st_num_arg) free_object((sp--)->u.ob, "f_query_ip_name");
    if (!tmp) push_number(0);
    else share_and_push_string(tmp);
}
#endif

#ifdef F_QUERY_IP_NUMBER
void
f_query_ip_number PROT((void))
{
    char *tmp;

    tmp = query_ip_number(st_num_arg ? sp->u.ob : 0);
    if (st_num_arg) free_object((sp--)->u.ob, "f_query_ip_number");
    if (!tmp) push_number(0);
    else share_and_push_string(tmp);
}
#endif

#ifdef F_QUERY_LOAD_AVERAGE
void
f_query_load_average PROT((void))
{
    copy_and_push_string(query_load_av());
}
#endif

#ifdef F_QUERY_PRIVS
void
f_query_privs PROT((void))
{
    object_t *ob;
    
    ob = sp->u.ob;
    if (ob->privs != NULL) {
        free_object(ob, "f_query_privs");
        sp->type = T_STRING;
        sp->u.string = make_shared_string(ob->privs);
        sp->subtype = STRING_SHARED;
    } else {
        free_object(ob, "f_query_privs");
        *sp = const0;
    }
}
#endif

#ifdef F_QUERY_SNOOPING
void
f_query_snooping PROT((void))
{
    object_t *ob;
    
    ob = query_snooping(sp->u.ob);
    free_object(sp->u.ob, "f_query_snooping");
    if (ob) { put_unrefed_undested_object(ob, "query_snooping"); }
    else *sp = const0;
}
#endif

#ifdef F_QUERY_SNOOP
void
f_query_snoop PROT((void))
{
    object_t *ob;
    
    ob = query_snoop(sp->u.ob);
    free_object(sp->u.ob, "f_query_snoop");
    if (ob) { put_unrefed_undested_object(ob, "query_snoop"); }
    else *sp = const0;
}
#endif

#ifdef F_RANDOM
void
f_random PROT((void))
{
    if (sp->u.number <= 0) {
	sp->u.number = 0;
	return;
    }
    sp->u.number = random_number(sp->u.number);
}
#endif

#ifdef F_READ_BYTES
void
f_read_bytes PROT((void))
{
    char *str;
    int start = 0, len = 0, rlen = 0, num_arg = st_num_arg;
    svalue_t *arg;

    arg = sp - num_arg + 1;
    if (num_arg > 1)
        start = arg[1].u.number;
    if (num_arg == 3) {
        len = arg[2].u.number;
    }
    str = read_bytes(arg[0].u.string, start, len, &rlen);
    pop_n_elems(num_arg);
    if (str == 0)
        push_number(0);
    else {
        push_malloced_string(str);
    }
}
#endif

#ifdef F_READ_BUFFER
void
f_read_buffer PROT((void))
{
    char *str;
    int start = 0, len = 0, rlen = 0, num_arg = st_num_arg;
    int from_file = 0;          /* new line */
    svalue_t *arg = sp - num_arg + 1;

    if (num_arg > 1) {
       start = arg[1].u.number;
       if (num_arg == 3) {
	   len = arg[2].u.number;
       }
    }
    if (arg[0].type == T_STRING) {
        from_file = 1;          /* new line */
        str = read_bytes(arg[0].u.string, start, len, &rlen);
    } else {                    /* T_BUFFER */
        str = read_buffer(arg[0].u.buf, start, len, &rlen);
    }
    pop_n_elems(num_arg);
    if (str == 0) {
        push_number(0);
    } else if (from_file) {     /* changed */
        buffer_t *buf;

        buf = allocate_buffer(rlen);
        memcpy(buf->item, str, rlen);
	STACK_INC;
        sp->type = T_BUFFER;
        sp->u.buf = buf;
        FREE_MSTR(str);
    } else {                    /* T_BUFFER */
        push_malloced_string(str);
    }
}
#endif

#ifdef F_READ_FILE
void
f_read_file PROT((void))
{
    char *str;
    int start,len;

    if (st_num_arg == 3) {
        len = (sp--)->u.number;
    } else len = 0;
    if (st_num_arg > 1)
        start = (sp--)->u.number;
    else start = 0;

    str = read_file(sp->u.string, start, len);
    free_string_svalue(sp);
    if (!str) *sp = const0;
    else { sp->subtype = STRING_MALLOC; sp->u.string = str; }
}
#endif

#ifdef F_RECEIVE
void
f_receive PROT((void))
{
    if (sp->type == T_STRING) {
	if (current_object->interactive) {
	    int len = SVALUE_STRLEN(sp);
	    
	    if (len > LARGEST_PRINTABLE_STRING)
		error("Printable strings limited to length of %d.\n",
		      LARGEST_PRINTABLE_STRING);
		
	    add_message(current_object, sp->u.string, len);
	}
	free_string_svalue(sp--);
    }
#ifndef NO_BUFFER_TYPE
    else {
	if (current_object->interactive)
	    add_message(current_object, (char *)sp->u.buf->item, sp->u.buf->size);

	free_buffer((sp--)->u.buf);
    }
#endif
}
#endif

#ifdef F_REG_ASSOC
void
f_reg_assoc PROT((void)) {
    svalue_t *arg;
    array_t *vec;

    arg = sp - st_num_arg + 1;
    
    if (!(arg[2].type == T_ARRAY))
	error("Bad argument 3 to reg_assoc()\n");
    
    vec = reg_assoc(arg[0].u.string, arg[1].u.arr, arg[2].u.arr, st_num_arg > 3 ? &arg[3] : &const0);

    if (st_num_arg == 4)
	pop_3_elems();
    else
	pop_2_elems();
    free_string_svalue(sp);
    sp->type = T_ARRAY;
    sp->u.arr = vec;
}
#endif

#ifdef F_REGEXP
void
f_regexp PROT((void))
{
    array_t *v;
    int flag;

    if (st_num_arg > 2) {
        if (!(sp->type == T_NUMBER)) error("Bad argument 3 to regexp()\n");
	if (sp[-2].type == T_STRING) error("3rd argument illegal for regexp(string, string)\n"); 
	flag = (sp--)->u.number;
    } else flag = 0;
    if (sp[-1].type == T_STRING) {
	flag = match_single_regexp((sp - 1)->u.string, sp->u.string);
	free_string_svalue(sp--);
	free_string_svalue(sp);
	put_number(flag);
    } else {
	v = match_regexp((sp - 1)->u.arr, sp->u.string, flag);

	free_string_svalue(sp--);
	free_array(sp->u.arr);
	sp->u.arr = v;
    }
}
#endif

#ifdef F_REMOVE_CALL_OUT
void
f_remove_call_out PROT((void))
{
    int i;

    if (st_num_arg) {
#ifdef CALLOUT_HANDLES
	if (sp->type == T_STRING) {
#endif
	    i = remove_call_out(current_object, sp->u.string);
	    free_string_svalue(sp);
#ifdef CALLOUT_HANDLES
	} else {
	    i = remove_call_out_by_handle(sp->u.number);
	}
#endif
    } else {
	remove_all_call_out(current_object);
	i = 0;
	STACK_INC;
    }
    put_number(i);
}
#endif

#ifdef F_RENAME
void
f_rename PROT((void))
{
    int i;

    i = do_rename((sp - 1)->u.string, sp->u.string, F_RENAME);
    free_string_svalue(sp--);
    free_string_svalue(sp);
    put_number(i);
}
#endif				/* F_RENAME */

/* This is an enhancement to the f_replace_string() in efuns_main.c of
   MudOS v21.  When the search pattern has more than one character,
   this version of f_replace_string() uses a skip table to more efficiently
   search the file for the search pattern (the basic idea is to avoid
   strings comparisons where possible).  This version is anywhere from
   15% to 40% faster than the old version depending on the size of the
   string to be searched and the length of the search string (and depending
   on the relative frequency with which the letters in the search string
   appear in the string to be searched).

   Note: this version should behave identically to the old version (except
   for runtime).  When the search pattern is only one character long, the
   old algorithm is used.  The new algorithm is actually about 10% slower
   than the old one when the search string is only one character long.

   This enhancement to f_replace_string() was written by John Garnett
   (aka Truilkan) on 1995/04/29.  I believe the original replace_string()
   was written by Dave Richards (Cygnus).

   I didn't come up with the idea of this algorithm (learned it in
   a university programming course way back when).  For those interested
   in the workings of the algorithm, you can probably find it in a book on
   string processing algorithms.  Its also fairly easy to figure out the
   algorithm by tracing through it for a small example.
*/
#ifdef F_REPLACE_STRING

/*
syntax for replace_string is now:
    string replace_string(src, pat, rep);   // or
    string replace_string(src, pat, rep, max);  // or
    string replace_string(src, pat, rep, first, last);

The 4th/5th args are optional (to retain backward compatibility).
- src, pat, and rep are all strings.
- max is an integer. It will replace all occurances up to max
  matches (starting as 1 as the first), with a value of 0 meaning
  'replace all')
- first and last are just a range to replace between, with
  the following constraints
    first < 1: change all from start
    last == 0 || last > max matches:    change all to end
    first > last: return unmodified array.
(i.e, with 4 args, it's like calling it with:
    replace_string(src, pat, rep, 0, max);
)
*/

void
f_replace_string PROT((void))
{
    int plen, rlen, dlen, slen, first, last, cur, j;
    
    char *pattern;
    char *replace;
    register char *src, *dst1, *dst2;
    svalue_t *arg;
    int skip_table[256];
    char *slimit;
    char *flimit;
    char *climit;
    int probe;
    int skip;
    
    if (st_num_arg > 5) {
        error("Too many args to replace_string.\n");
        pop_n_elems(st_num_arg);
        return;
    }
    arg = sp - st_num_arg + 1;
    src = arg->u.string;
    first = 0;
    last = 0;
    
    if (st_num_arg >= 4) {
        CHECK_TYPES((arg+3), T_NUMBER, 4, F_REPLACE_STRING);
        first = (arg+3)->u.number;
	
        if (st_num_arg == 4) {
            last = first;
            first = 0;
        } else if (st_num_arg == 5) {
            CHECK_TYPES(sp, T_NUMBER, 5, F_REPLACE_STRING);
            /* first set above. */
            last = sp->u.number;
        }
    }
        
    if (!last)
        last = max_string_length;
    
    if (first > last) {         /* just return it */
        pop_n_elems(st_num_arg - 1);
        return;
    }
    pattern = (arg+1)->u.string;
    plen = SVALUE_STRLEN(arg+1);
    if (!plen) {
        pop_n_elems(st_num_arg - 1);    /* just return it */
	
        return;
    }
    replace = (arg+2)->u.string;
    rlen = SVALUE_STRLEN(arg+2);
    dlen = 0;
    cur = 0;

    if (rlen <= plen) {
	/* we're going to do in string replacement */
	unlink_string_svalue(arg);
	src = arg->u.string;
    }
    
    if (plen > 1) {
        /* build skip table */
        for (j = 0; j < 256; j++) {
            skip_table[j] = plen;
        }
        for (j = 0; j < plen; j++) {
            skip_table[(unsigned char)pattern[j]] = plen - j - 1;
        }
        slen = SVALUE_STRLEN(arg);
        slimit = src + slen;
        flimit = slimit - plen + 1;
        probe = plen - 1;
    }
    
    if (rlen <= plen) {
        /* in string replacement */
        dst2 = dst1 = arg->u.string;
	
        if (plen > 1) { /* pattern length > 1, jump table most efficient */
            while (src < flimit) {
                if ((skip = skip_table[(unsigned char)src[probe]])) {
                    for (climit = dst2 + skip; dst2 < climit; *dst2++ = *src++)
                        ;
                } else if (memcmp(src, pattern, plen) == 0) {
                    cur++;
                    if ((cur >= first) &&  (cur <= last)) {
                        if (rlen) {
                            memcpy(dst2, replace, rlen);
                            dst2 += rlen;
                        }
                        src += plen;
                        if (cur == last) break;
                    } else {
			memcpy(dst2, src, plen);
			dst2 += plen;
                        src += plen;
                    }
                } else {
                    *dst2++ = *src++;
                }
            }
            memcpy(dst2, src, slimit - src);
            dst2 += (slimit - src);
	    *dst2 = 0;
	    arg->u.string = extend_string(dst1, dst2 - dst1);
        } else { /* pattern length <= 1, brute force most efficient */
	    /* Beek - if it was zero, we already returned, so plen == 1 */
	    /* assume source string is a string < maximum string length */
	    if (rlen) {
		while (*src) {
		    if (*src == *pattern) {
			cur++;
		    
			if (cur >= first && cur <= last) {
			    *src = *replace;
			}
		    }
		    src++;
		}
	    } else { /* rlen is zero */
		while (*src) {
		    if (*src++ == *pattern) {
			cur++;
			if (cur >= first) {
			    dst2 = src - 1;
			    while (*src) {
				if (*src == *pattern) {
				    cur++;
				    if (cur <= last) {
					src++;
					continue;
				    } else {
					while (*src)
					    *dst2++ = *src++;
					break;
				    }
				}
				*dst2++ = *src++;
			    }
			    *dst2 = 0;
			    arg->u.string = extend_string(dst1, dst2 - dst1);
			    break;
			}
		    }
		}
	    }
	}
        pop_n_elems(st_num_arg - 1);
    } else {
        dst2 = dst1 = new_string(max_string_length, "f_replace_string: 2");
	
        if (plen > 1) {
            while (src < flimit) {
                if ((skip = skip_table[(unsigned char)src[probe]])) {
                    for (climit = dst2 + skip; dst2 < climit; *dst2++ = *src++)
                        ;
		    
                } else if (memcmp(src, pattern, plen) == 0) {
                    cur++;
                    if ((cur >= first) && (cur <= last)) {
			if (max_string_length - dlen <= rlen) {
			    pop_n_elems(st_num_arg);
			    push_svalue(&const0u);
			    FREE_MSTR(dst1);
			    return;
			}
			memcpy(dst2, replace, rlen);
			dst2 += rlen;
			dlen += rlen;
			src += plen;
                        if (cur == last) break;
                    } else {
			dlen += plen;
			if (max_string_length - dlen <= 0) {
			    pop_n_elems(st_num_arg);
			    push_svalue(&const0u);
			    
			    FREE_MSTR(dst1);
			    return;
			}			    
			memcpy(dst2, src, plen);
			dst2 += plen;
                        src += plen;
                    }
                } else {
                    if (max_string_length - dlen <= 1) {
                        pop_n_elems(st_num_arg);
                        push_svalue(&const0u);
			
                        FREE_MSTR(dst1);
                        return;
                    }
                    *dst2++ = *src++;
                    dlen++;
                }
            }
            if (max_string_length - dlen <= (slimit - src)) {
                pop_n_elems(st_num_arg);
                push_svalue(&const0u);
                FREE_MSTR(dst1);
                return;
            }
            memcpy(dst2, src, slimit - src);
            dst2 += (slimit - src);
        } else { /* plen <= 1 */
	    /* Beek: plen == 1 */
            while (*src != '\0') {
                if (*src == *pattern) {
                    cur++;
                    if (cur >= first && cur <= last) {
                        if (rlen != 0) {
                            if (max_string_length - dlen <= rlen) {
                                pop_n_elems(st_num_arg);
                                push_svalue(&const0u);
                                FREE_MSTR(dst1);
                                return;
                            }
                            strncpy(dst2, replace, rlen);
                            dst2 += rlen;
                            dlen += rlen;
                        }
                        src++;
                        continue;
                    }
                }
                if (max_string_length - dlen <= 1) {
                    pop_n_elems(st_num_arg);
                    push_svalue(&const0u);
                    FREE_MSTR(dst1);
                    return;
                }
                *dst2++ = *src++;
                dlen++;
            }
        }
        *dst2 = '\0';

        pop_n_elems(st_num_arg);
        /*
         * shrink block or make a copy of exact size
         */
        push_malloced_string(extend_string(dst1, dst2 - dst1));
    }
}
#endif

#ifdef F_RESOLVE
void
f_resolve PROT((void))
{
    int i, query_addr_number PROT((char *, svalue_t *));

    i = query_addr_number((sp - 1)->u.string, sp);
    pop_stack();
    free_string_svalue(sp);
    put_number(i);
}
#endif

#ifdef F_RESTORE_OBJECT
void
f_restore_object PROT((void))
{
    int flag;

    flag = (st_num_arg > 1) ? (sp--)->u.number : 0;
    flag = restore_object(current_object, sp->u.string, flag);
    free_string_svalue(sp);
    put_number(flag);
}
#endif

#ifdef F_RESTORE_VARIABLE
void
f_restore_variable PROT((void)) {
    svalue_t v;

    unlink_string_svalue(sp);
    v.type = T_NUMBER;

    restore_variable(&v, sp->u.string);
    FREE_MSTR(sp->u.string);
    *sp = v;
}
#endif

#ifdef F_RM
void
f_rm PROT((void))
{
    int i;

    i = remove_file(sp->u.string);
    free_string_svalue(sp);
    put_number(i);
}
#endif

#ifdef F_RMDIR
void
f_rmdir PROT((void))
{
    char *path;

    path = check_valid_path(sp->u.string, current_object, "rmdir", 1);
    if (!path || rmdir(path) == -1) {
        free_string_svalue(sp);
        *sp = const0;
    }
    else {
        free_string_svalue(sp);
        *sp = const1;
    }
}
#endif

#ifdef F_SAVE_OBJECT
void
f_save_object PROT((void))
{
    int flag;

    flag = (st_num_arg == 2) && (sp--)->u.number;
    flag = save_object(current_object, sp->u.string, flag);
    free_string_svalue(sp);
    put_number(flag);
}
#endif

#ifdef F_SAVE_VARIABLE
void
f_save_variable PROT((void)) {
    char *p;

    p = save_variable(sp);
    pop_stack();
    push_malloced_string(p);
}
#endif

#ifdef F_SAY
void
f_say PROT((void))
{
    array_t *avoid;
    static array_t vtmp =
    {1,
#ifdef DEBUG
     1,
#endif
     1,
#ifdef PACKAGE_MUDLIB_STATS
     {(mudlib_stats_t *) NULL, (mudlib_stats_t *) NULL}
#endif
    };

    if (st_num_arg == 1) {
	avoid = &the_null_array;
	say(sp, avoid);
	pop_stack();
    } else {
	if (sp->type == T_OBJECT) {
	    vtmp.item[0].type = T_OBJECT;
	    vtmp.item[0].u.ob = sp->u.ob;
	    avoid = &vtmp;
	} else {		/* must be an array... */
	    avoid = sp->u.arr;
	}
	say(sp - 1, avoid);
	pop_2_elems();
    }
}
#endif

#ifdef F_SET_EVAL_LIMIT
/* warning: do not enable this without using valid_override() in the master
   object and a set_eval_limit() simul_efun to restrict access.
*/
void
f_set_eval_limit PROT((void))
{
    switch (sp->u.number) {
    case 0:
	sp->u.number = eval_cost = max_cost;
	break;
    case -1:
	sp->u.number = eval_cost;
	break;
    case 1:
	sp->u.number = max_cost;
	break;
    default:
	max_cost = sp->u.number;
	break;
    }
}
#endif

#ifdef F_SET_BIT
void
f_set_bit PROT((void))
{
    char *str;
    int len, old_len, ind, bit;

    if (sp->u.number > MAX_BITS)
        error("set_bit() bit requested: %d > maximum bits: %d\n", sp->u.number, MAX_BITS);
    bit = (sp--)->u.number;
    if (bit < 0)
	error("Bad argument 2 (negative) to set_bit().\n");
    ind = bit/6;
    bit %= 6;
    old_len = len = SVALUE_STRLEN(sp);
    if (ind >= len)
        len = ind + 1;
    if (ind < old_len) {
	unlink_string_svalue(sp);
	str = sp->u.string;
    } else {
	str = new_string(len, "f_set_bit: str");
	str[len] = '\0';
	if (old_len)
	    memcpy(str, sp->u.string, old_len);
	if (len > old_len)
	    memset(str + old_len, ' ', len - old_len);
	free_string_svalue(sp);
	sp->subtype = STRING_MALLOC;
	sp->u.string = str;
    }

    if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
        error("Illegal bit pattern in set_bit character %d\n", ind);
    str[ind] = ((str[ind] - ' ') | (1 << bit)) + ' ';
}
#endif

#ifdef F_SET_HEART_BEAT
void
f_set_heart_beat PROT((void))
{
    set_heart_beat(current_object, (sp--)->u.number);
}
#endif

#ifdef F_QUERY_HEART_BEAT
void
f_query_heart_beat PROT((void))
{
    object_t *ob;
    
    free_object(ob = sp->u.ob, "f_query_heart_beat");
    put_number(query_heart_beat(ob));
}
#endif

#ifdef F_SET_HIDE
void
f_set_hide PROT((void))
{
    if (!valid_hide(current_object)) {
	sp--;
	return;
    }
    if ((sp--)->u.number) {
	num_hidden++;
	if (!(current_object->flags & O_HIDDEN) && current_object->interactive)
	    num_hidden_users++;
	current_object->flags |= O_HIDDEN;
    } else {
	num_hidden--;
	if ((current_object->flags & O_HIDDEN) && current_object->interactive)
	    num_hidden_users--;
	current_object->flags &= ~O_HIDDEN;
    }
}
#endif

#ifdef F_SET_LIGHT
void
f_set_light PROT((void))
{
    object_t *o1;

    add_light(current_object, sp->u.number);
    o1 = current_object;
#ifndef NO_ENVIRONMENT
    while (o1->super)
	o1 = o1->super;
#endif
    sp->u.number = o1->total_light;
}
#endif

#ifdef F_SET_PRIVS
void
f_set_privs PROT((void))
{
    object_t *ob;

    ob = (sp - 1)->u.ob;
    if (ob->privs != NULL)
        free_string(ob->privs);
    if (!(sp->type == T_STRING)) {
        ob->privs = NULL;
	sp--; /* It's a number */
    } else {
        ob->privs = make_shared_string(sp->u.string);
	free_string_svalue(sp--);
    }	    
    free_object(ob, "f_set_privs");
    sp--;
}
#endif

#ifdef F_SHADOW
void
f_shadow PROT((void))
{
    object_t *ob;

    ob = (sp - 1)->u.ob;
    if (!((sp--)->u.number)) {
        ob = ob->shadowed;
        free_object(sp->u.ob, "f_shadow:1");
        if (ob) {
	  add_ref(ob, "shadow(ob, 0)");
	  sp->u.ob = ob;
	}
        else *sp = const0;
        return;
    }
    if (ob == current_object) {
        error("shadow: Can't shadow self\n");
    }
    if (validate_shadowing(ob)) {
        if (current_object->flags & O_DESTRUCTED) {
            free_object(ob, "f_shadow:2");
            *sp = const0;
            return;
	}
        /*
         * The shadow is entered first in the chain.
         */
        while (ob->shadowed)
            ob = ob->shadowed;
        current_object->shadowing = ob;
        ob->shadowed = current_object;
        free_object(sp->u.ob, "f_shadow:3");
	add_ref(ob, "shadow(ob, 1)");
        sp->u.ob = ob;
        return;
    }
    free_object(sp->u.ob, "f_shadow:4");
    *sp = const0;
}
#endif

#ifdef F_SHOUT
void
f_shout PROT((void))
{
    shout_string(sp->u.string);
    free_string_svalue(sp--);
}
#endif

#ifdef F_SHUTDOWN
void
f_shutdown PROT((void))
{
    if (st_num_arg) {
	shutdownMudOS(sp->u.number);
    } else {
	shutdownMudOS(0);
	push_number(0);
    }
}
#endif

#ifdef F_SIZEOF
void
f_sizeof PROT((void))
{
    int i;

    switch (sp->type) {
    case T_CLASS:
	i = sp->u.arr->size;
	free_class(sp->u.arr);
	break;
    case T_ARRAY:
	i = sp->u.arr->size;
	free_array(sp->u.arr);
	break;
    case T_MAPPING:
	i = sp->u.map->count;
	free_mapping(sp->u.map);
	break;
#ifndef NO_BUFFER_TYPE
    case T_BUFFER:
	i = sp->u.buf->size;
	free_buffer(sp->u.buf);
	break;
#endif
    case T_STRING:
	i = SVALUE_STRLEN(sp);
	free_string_svalue(sp);
	break;
    default:
	i = 0;
	free_svalue(sp, "f_sizeof");
    }
    sp->type = T_NUMBER;
    sp->u.number = i;
}
#endif

#ifdef F_SNOOP
void
f_snoop PROT((void))
{
    /*
     * This one takes a variable number of arguments. It returns 0 or an
     * object.
     */
    if (st_num_arg == 1) {
        if (!new_set_snoop(sp->u.ob, 0) || (sp->u.ob->flags & O_DESTRUCTED)) {
	    free_object(sp->u.ob, "f_snoop:1");
	    *sp = const0;
	}
    } else {
        if (!new_set_snoop((sp - 1)->u.ob, sp->u.ob) || 
	    (sp->u.ob->flags & O_DESTRUCTED)) {
	    free_object((sp--)->u.ob, "f_snoop:2");
	    free_object(sp->u.ob, "f_snoop:3");
	    *sp = const0;
	} else {
	    free_object((--sp)->u.ob, "f_snoop:4");
	    sp->u.ob = (sp+1)->u.ob;
	}
    }
}
#endif

#ifdef F_SPRINTF
void
f_sprintf PROT((void))
{
    char *s;
    int num_arg = st_num_arg;

    s = string_print_formatted((sp - num_arg + 1)->u.string,
                               num_arg - 1, sp - num_arg + 2);
    pop_n_elems(num_arg);

    STACK_INC;
    sp->type = T_STRING;
    if (!s) {
	sp->subtype = STRING_CONSTANT;
	sp->u.string = "";
    } else {
	sp->subtype = STRING_MALLOC;
	sp->u.string = s;
    }
}
#endif

#ifdef F_STAT
void
f_stat PROT((void))
{
    struct stat buf;
    char *path;
    array_t *v;
    object_t *ob;
    
    path = check_valid_path((--sp)->u.string, current_object, "stat", 0);
    if (!path) {
        free_string_svalue(sp);
        *sp = const0;
        return;
    }
    if (stat(path, &buf) != -1) {
        if (buf.st_mode & S_IFREG) {    /* if a regular file */
            v = allocate_empty_array(3);
            v->item[0].type = T_NUMBER;
            v->item[0].u.number = buf.st_size;
            v->item[1].type = T_NUMBER;
            v->item[1].u.number = buf.st_mtime;
            v->item[2].type = T_NUMBER;
            ob = find_object2(path);
            if (ob && !object_visible(ob))
                ob = 0;
            if (ob)
                v->item[2].u.number = ob->load_time;
            else
                v->item[2].u.number = 0;
            free_string_svalue(sp);
            put_array(v);
            return;
	}
    }
    v = get_dir(sp->u.string, (sp+1)->u.number);
    free_string_svalue(sp);
    if (v) { put_array(v); }
    else *sp = const0;
}
#endif

#ifdef F_STRSRCH
/*
 * int strsrch(string big, string little, [ int flag ])
 * - search for little in big, starting at right if flag is set
 *   return int offset of little, -1 if not found
 *
 * Written 930706 by Luke Mewburn <zak@rmit.edu.au>
 */

void
f_strsrch PROT((void))
{
    register char *big, *little, *pos;
    static char buf[2];         /* should be initialized to 0 */
    int i, blen, llen;

    sp--;
    big = (sp - 1)->u.string;
    blen = SVALUE_STRLEN(sp - 1);
    if (sp->type == T_NUMBER) {
        little = buf;
        if ((buf[0] = (char) sp->u.number))
	    llen = 1;
	else
	    llen = 0;
    } else {
        little = sp->u.string;
	llen = SVALUE_STRLEN(sp);
    }

    if (!llen || blen < llen) {
        pos = NULL;

        /* start at left */
    } else if (!((sp+1)->u.number)) {
        if (!little[1])         /* 1 char srch pattern */
            pos = strchr(big, (int) little[0]);
        else
            pos = (char *)strstr(big, little);
        /* start at right */
    } else {                    /* XXX: maybe test for -1 */
        if (!little[1])         /* 1 char srch pattern */
            pos = strrchr(big, (int) little[0]);
        else {
            char c = *little;

            pos = big + blen;   /* find end */
            pos -= llen;        /* find rightmost pos it _can_ be */
            do {
                do {
                    if (*pos == c) break;
		} while (--pos >= big);
                if (*pos != c) {
                    pos = NULL;
                    break;
		}
                for (i = 1; little[i] && (pos[i] == little[i]); i++);   /* scan all chars */
                if (!little[i])
                    break;
	    } while (--pos >= big);
	}
    }

    if (!pos)
        i = -1;
    else
        i = (int) (pos - big);
    if (sp->type == T_STRING) free_string_svalue(sp);
    free_string_svalue(--sp);
    put_number(i);
}				/* strsrch */
#endif

#ifdef F_STRCMP
void
f_strcmp PROT((void))
{
    int i;

    i = strcmp((sp - 1)->u.string, sp->u.string);
    free_string_svalue(sp--);
    free_string_svalue(sp);
    put_number(i);
}
#endif

#ifdef F_STRINGP
void
f_stringp PROT((void))
{
    if (sp->type == T_STRING) {
        free_string_svalue(sp);
        *sp = const1;
    }
    else {
        free_svalue(sp, "f_stringp");
        *sp = const0;
    }
}
#endif

#ifdef F_BUFFERP
void
f_bufferp PROT((void))
{
    if (sp->type == T_BUFFER) {
        free_buffer(sp->u.buf);
        *sp = const1;
    } else {
        free_svalue(sp, "f_bufferp");
        *sp = const0;
    }
}
#endif

#ifdef F_SWAP
void
f_swap PROT((void))
{
    object_t *ob = sp->u.ob;
    control_stack_t *p;

    /* a few sanity checks */
    if (!(ob->flags & O_SWAPPED) && (ob != current_object)) {
	for (p = csp; p >= control_stack; p--)
	    if (ob == csp->ob) {
		pop_stack();
		return;
	    }
	(void) swap(sp->u.ob);
    }

    pop_stack();
}
#endif

#ifdef F_TELL_OBJECT
void
f_tell_object PROT((void))
{
    tell_object((sp - 1)->u.ob, sp->u.string, SVALUE_STRLEN(sp));
    free_string_svalue(sp--);
    pop_stack();
}
#endif

#ifdef F_TELL_ROOM
void
f_tell_room PROT((void))
{
    array_t *avoid;
    static array_t vtmp =
    {1,
#ifdef DEBUG
     1,
#endif
     1,
#ifdef PACKAGE_MUDLIB_STATS
     {(mudlib_stats_t *) NULL, (mudlib_stats_t *) NULL}
#endif
    };

    int num_arg = st_num_arg;
    svalue_t *arg = sp - num_arg + 1;
    object_t *ob;
    
    if (arg->type == T_OBJECT) {
        ob = arg[0].u.ob;
    } else {                    /* must be a string... */
        ob = find_object(arg[0].u.string);
        if (!ob || !object_visible(ob))
            error("Bad argument 1 to tell_room()\n");
    }

    if (num_arg == 2) {
        avoid = &the_null_array;
    } else {
	if (arg[2].type == T_OBJECT) {
	    vtmp.item[0].type = T_OBJECT;
	    vtmp.item[0].u.ob = arg[2].u.ob;
	    avoid = &vtmp;
	} else {
	    avoid = arg[2].u.arr;
	}
    }

    tell_room(ob, &arg[1], avoid);
    if (num_arg > 2 && arg[2].type != T_OBJECT)
	free_array(avoid);
    free_svalue(arg + 1, "f_tell_room");
    free_svalue(arg, "f_tell_room");
    sp = arg - 1;
}
#endif

#ifdef F_TEST_BIT
void
f_test_bit PROT((void))
{
    int ind = (sp--)->u.number;

    if (ind / 6 >= SVALUE_STRLEN(sp)) {
        free_string_svalue(sp);
        *sp = const0;
        return;
    }
    if (ind < 0) error("Bad argument 2 (negative) to test_bit().\n");
    if ((sp->u.string[ind / 6] - ' ') & (1 << (ind % 6))) {
        free_string_svalue(sp);
        *sp = const1;
    } else {
        free_string_svalue(sp);
        *sp = const0;
    }
}
#endif

#ifdef F_NEXT_BIT
void
f_next_bit PROT((void))
{
    int start = (sp--)->u.number;
    int len = SVALUE_STRLEN(sp);
    int which, bit, value;
    
    if (!len || start / 6 >= len) {
        free_string_svalue(sp);
	put_number(-1);
        return;
    }
    /* Find the next bit AFTER start */
    if (start > 0) {
	if (start % 6 == 5) {
	    which = (start / 6) + 1;
	    value = sp->u.string[which] - ' ';
	} else {
	    /* we have a partial byte to check */
	    which = start / 6;
	    bit = 0x3f - ((1 << ((start % 6) + 1)) - 1);
	    value = (sp->u.string[which] - ' ') & bit;
	}
    } else {
	which = 0;
	value = *sp->u.string - ' ';
    }

    while (1) {
	if (value)  {
	    if (value & 0x07) {
		if (value & 0x01)
		    bit = which * 6;
		else if (value & 0x02)
		    bit = which * 6 + 1;
		else if (value & 0x04)
		    bit = which * 6 + 2;
		break;
	    } else if (value & 0x38) {
		if (value & 0x08)
		    bit = which * 6 + 3;
		else if (value & 0x10)
		    bit = which * 6 + 4;
		else if (value & 0x20)
		    bit = which * 6 + 5;
		break;
	    }
	}
	which++;
	if (which == len) {
	    bit = -1;
	    break;
	}
	value = sp->u.string[which] - ' ';
    }

    free_string_svalue(sp);
    put_number(bit);
}
#endif

#ifdef F__THIS_OBJECT
void
f__this_object PROT((void))
{
    push_object(current_object);
}
#endif

#ifdef F_THIS_PLAYER
void
f_this_player PROT((void))
{
    if (sp->u.number) {
	if (current_interactive)
	    put_unrefed_object(current_interactive, "this_player(1)");
	else sp->u.number = 0;
    } else {
	if (command_giver)
	    put_unrefed_object(command_giver, "this_player(0)");
	/* else zero is on stack already */
    }
}
#endif

#ifdef F_SET_THIS_PLAYER
void
f_set_this_player PROT((void))
{
    if (sp->type == T_NUMBER)
	set_command_giver(0);
    else
	set_command_giver(sp->u.ob);
    pop_stack();
}
#endif

#ifdef F_THROW
void
f_throw PROT((void))
{
    free_svalue(&catch_value, "f_throw");
    catch_value = *sp--;
    throw_error();		/* do the longjump, with extra checks... */
}
#endif

#ifdef F_TIME
void
f_time PROT((void))
{
    push_number(current_time);
}
#endif

#ifdef F__TO_FLOAT
void
f__to_float PROT((void))
{
    double temp = 0;

    switch(sp->type) {
        case T_NUMBER:
            sp->type = T_REAL;
            sp->u.real = (double) sp->u.number;
            break;
        case T_STRING:
            sscanf(sp->u.string, "%lf", &temp);
            free_string_svalue(sp);
            sp->type = T_REAL;
            sp->u.real = temp;
    }
}
#endif

#ifdef F__TO_INT
void
f__to_int PROT((void))
{
    switch(sp->type) {
        case T_REAL:
            sp->type = T_NUMBER;
            sp->u.number = (int) sp->u.real;
            break;
        case T_STRING:
        {
            int temp;
	    char *p;
	    
            temp = strtol(sp->u.string, &p, 10);
	    if (*p) {
		/* have to be a little careful here.  Checkign if p ==
		 * sp->u.string isn't good enough.
		 * 
		 * Odd cases:
		 * to_int("  foo")  // p == sp->u.string + 2
		 *
		 * POSIX guarantees the strtol() works in terms of isspace(),
		 * though.  If there is something other than whitespace, then
		 * there was a valid character consistent with the base,
		 * so we were successful.
		 *
		 * (note: this means to_int("10x") == 10.  If you want to
		 *  detect trailing garbage, use sscanf(str, "%d%s", ...).
		 */
		while (p > sp->u.string && uisspace(*(p - 1)))
		    p--;

		if (p == sp->u.string) {
		    free_string_svalue(sp);
		    *sp = const0u;
		    break;
		}
	    }
            free_string_svalue(sp);
            sp->u.number = temp;
            sp->type = T_NUMBER;
            break;
	}
#ifndef NO_BUFFER_TYPE
        case T_BUFFER:
            if (sp->u.buf->size < sizeof(int)) {
                free_buffer(sp->u.buf);
                *sp = const0;
	    } else {
                int hostint, netint;

                memcpy((char *) &netint, sp->u.buf->item, sizeof(int));
                hostint = ntohl(netint);
                free_buffer(sp->u.buf);
                put_number(hostint);
	    }
#endif
    }
}
#endif

#ifdef F_TYPEOF
void
f_typeof PROT((void))
{
    char *t = type_name(sp->type);

    free_svalue(sp, "f_typeof");
    put_constant_string(t);
}
#endif

#ifdef F_UNDEFINEDP
void
f_undefinedp PROT((void))
{
    if (sp->type == T_NUMBER) {
        if (!sp->u.number && (sp->subtype == T_UNDEFINED)) {
	    *sp = const1;
        } else *sp = const0;
    } else {
        free_svalue(sp, "f_undefinedp");
        *sp = const0;
    }
}
#endif

#ifdef F_UPTIME
void
f_uptime PROT((void))
{
    push_number(current_time - boot_time);
}
#endif

#ifdef F_USERP
void
f_userp PROT((void))
{
    int i;

    i = (int) sp->u.ob->flags & O_ONCE_INTERACTIVE;
    free_object(sp->u.ob, "f_userp");
    put_number(i != 0);
}
#endif

#ifdef F_USERS
void
f_users PROT((void))
{
    push_refed_array(users());
}
#endif

#ifdef F_WIZARDP
void
f_wizardp PROT((void))
{
    int i;

    i = (int) sp->u.ob->flags & O_IS_WIZARD;
    free_object(sp->u.ob, "f_wizardp");
    put_number(i != 0);
}
#endif

#ifdef F_VIRTUALP
void
f_virtualp PROT((void))
{
    int i;

    i = (int) sp->u.ob->flags & O_VIRTUAL;
    free_object(sp->u.ob, "f_virtualp");
    put_number(i != 0);
}
#endif

#ifdef F_WRITE
void
f_write PROT((void))
{
    do_write(sp);
    pop_stack();
}
#endif

#ifdef F_WRITE_BYTES
void
f_write_bytes PROT((void))
{
    int i;

    switch(sp->type) {
        case T_NUMBER:
        {
            int netint;
            char *netbuf;

            if (!sp->u.number) bad_arg(3, F_WRITE_BYTES);
            netint = htonl(sp->u.number);       /* convert to network
                                                 * byte-order */
            netbuf = (char *) &netint;
            i = write_bytes((sp - 2)->u.string, (sp - 1)->u.number, netbuf,
                            sizeof(int));
            break;
	}

#ifndef NO_BUFFER_TYPE
        case T_BUFFER:
        {
            i = write_bytes((sp - 2)->u.string, (sp - 1)->u.number,
                            (char *) sp->u.buf->item, sp->u.buf->size);
            break;
	}
#endif

        case T_STRING:
        {
            i = write_bytes((sp - 2)->u.string, (sp - 1)->u.number,
                            sp->u.string, SVALUE_STRLEN(sp));
            break;
	}

        default:
        {
#ifdef NO_BUFFER_TYPE
            bad_argument(sp, T_STRING | T_NUMBER, 3, F_WRITE_BYTES);
#else
            bad_argument(sp, T_BUFFER | T_STRING | T_NUMBER, 3, F_WRITE_BYTES);
#endif
	}
    }
    free_svalue(sp--, "f_write_bytes");
    free_string_svalue(--sp);
    put_number(i);
}
#endif

#ifdef F_WRITE_BUFFER
void
f_write_buffer PROT((void))
{
    int i;

    if ((sp-2)->type == T_STRING) {
        f_write_bytes();
        return;
    }

    switch(sp->type) {
        case T_NUMBER:
        {
            int netint;
            char *netbuf;

            netint = htonl(sp->u.number);       /* convert to network
                                                 * byte-order */
            netbuf = (char *) &netint;
            i = write_buffer((sp - 2)->u.buf, (sp - 1)->u.number, netbuf,
                            sizeof(int));
            break;
	}

        case T_BUFFER:
        {
            i = write_buffer((sp - 2)->u.buf, (sp - 1)->u.number,
                            (char *) sp->u.buf->item, sp->u.buf->size);
            break;
	}

        case T_STRING:
        {
            i = write_buffer((sp - 2)->u.buf, (sp - 1)->u.number,
                            sp->u.string, SVALUE_STRLEN(sp));
            break;
	}

        default:
        {
            bad_argument(sp, T_BUFFER | T_STRING | T_NUMBER, 3, F_WRITE_BUFFER);
	}
    }
    free_svalue(sp--, "f_write_buffer");
    free_svalue(--sp, "f_write_buffer");
    put_number(i);
}
#endif

#ifdef F_WRITE_FILE
void
f_write_file PROT((void))
{
    int flags = 0;

    if (st_num_arg == 3) {
        flags = (sp--)->u.number;
    }
    flags = write_file((sp - 1)->u.string, sp->u.string, flags);
    free_string_svalue(sp--);
    free_string_svalue(sp);
    put_number(flags);
}
#endif

#ifdef F_DUMP_FILE_DESCRIPTORS
void
f_dump_file_descriptors PROT((void))
{
    outbuffer_t out;

    outbuf_zero(&out);
    dump_file_descriptors(&out);
    outbuf_push(&out);
}
#endif

#ifdef F_RECLAIM_OBJECTS
void f_reclaim_objects PROT((void))
{
    push_number(reclaim_objects());
}
#endif

#ifdef F_MEMORY_INFO
void
f_memory_info PROT((void))
{
    int mem, swapped;
    object_t *ob;
    
    if (st_num_arg == 0) {
	int res, tot;

	if (reserved_area)
	    res = RESERVED_SIZE;
	else
	    res = 0;
	tot = total_prog_block_size +
#ifdef ARRAY_STATS
	    total_array_size +
#endif
	    total_mapping_size +
	    tot_alloc_object_size +
	    tot_alloc_sentence * sizeof(sentence_t) +
	    num_user * sizeof(interactive_t) +
	    show_otable_status(0, -1) +
	    heart_beat_status(0, -1) +
	    add_string_status(0, -1) +
	    print_call_out_usage(0, -1) + res;
	push_number(tot);
	return;
    }
    if (sp->type != T_OBJECT)
	bad_argument(sp, T_OBJECT, 1, F_MEMORY_INFO);
    ob = sp->u.ob;
    /* if swapped, reload it temporarily to get info and then swap it back out */
    if ((swapped = (ob->flags & O_SWAPPED)) == O_SWAPPED)
	load_ob_from_swap(ob);
    /* as documented, object memory usage is not additive due to sharing of
       structures, so always include the program's total size even if this
       object is a clone or the program has more than one reference to it.
       There's no reliable way to determine when the program size should be
       included or not to be more accurate -- Marius, 30-Jul-2000 */
    mem = ob->prog->total_size;
    if (swapped == O_SWAPPED)
	swap(ob);
    mem += (data_size(ob) + sizeof(object_t));
    free_object(ob, "f_memory_info");
    put_number(mem);
}
#endif

#ifdef F_RELOAD_OBJECT
void
f_reload_object PROT((void))
{
    reload_object(sp->u.ob);
    free_object((sp--)->u.ob, "f_reload_object");
}
#endif

#ifdef F_QUERY_SHADOWING
void
f_query_shadowing PROT((void))
{
    object_t *ob;
    
    if ((sp->type == T_OBJECT) && (ob = sp->u.ob)->shadowing) {
        add_ref(ob->shadowing, "query_shadowing(ob)");
        sp->u.ob = ob->shadowing;
        free_object(ob, "f_query_shadowing");
    } else {
        free_svalue(sp, "f_query_shadowing");
        *sp = const0;
    }
}
#endif

#ifdef F_SET_RESET
void
f_set_reset PROT((void))
{
    if (st_num_arg == 2) {
        (sp - 1)->u.ob->next_reset = current_time + sp->u.number;
        free_object((--sp)->u.ob, "f_set_reset:1");
        sp--;
    } else {
        sp->u.ob->next_reset = current_time + TIME_TO_RESET / 2 +
            random_number(TIME_TO_RESET / 2);
        free_object((sp--)->u.ob, "f_set_reset:2");
    }
}
#endif

#ifdef F_FLOATP
void
f_floatp PROT((void))
{
    if (sp->type == T_REAL) {
        sp->type = T_NUMBER;
        sp->u.number = 1;
    }
    else {
        free_svalue(sp, "f_floatp");
        *sp = const0;
    }
}
#endif

#ifdef F_FLUSH_MESSAGES
void
f_flush_messages PROT((void)) {
    if (st_num_arg == 1) {
	if (sp->u.ob->interactive)
	    flush_message(sp->u.ob->interactive);
	pop_stack();
    } else {
	int i;
	
	for (i = 0; i < max_users; i++) {
	    if (all_users[i] && !(all_users[i]->iflags & CLOSING))
		flush_message(all_users[i]);
	}
    }
}
#endif

#ifdef F_FIRST_INVENTORY
void
f_first_inventory PROT((void))
{
    object_t *ob;
    
    ob = first_inventory(sp);
    free_svalue(sp, "f_first_inventory");
    if (ob) { put_unrefed_undested_object(ob, "first_inventory"); }
    else *sp = const0;
}
#endif

#ifdef F_NEXT_INVENTORY
void
f_next_inventory PROT((void))
{
    object_t *ob;
    
    ob = sp->u.ob->next_inv;
    free_object(sp->u.ob, "f_next_inventory");

#ifdef F_SET_HIDE
    while (ob && (ob->flags & O_HIDDEN) && !object_visible(ob))
	ob = ob->next_inv;
#endif

    if (ob) {
	add_ref(ob, "next_inventory(ob) : 1");
	sp->u.ob = ob;
    } else
	*sp = const0;
}
#endif
