/
driver3.2@242/autoconf/
driver3.2@242/doc/LPC/
driver3.2@242/hosts/
driver3.2@242/hosts/amiga/NetIncl/
driver3.2@242/hosts/amiga/NetIncl/netinet/
driver3.2@242/hosts/amiga/NetIncl/sys/
driver3.2@242/hosts/atari/
driver3.2@242/hosts/fcrypt/
driver3.2@242/mudlib/
driver3.2@242/mudlib/sys/
driver3.2@242/util/
driver3.2@242/util/indent/hosts/next/
driver3.2@242/util/make_docs/
#include "lint.h"
#include "interpret.h"
#include "object.h"
#include "comm.h"
#include "stralloc.h"
#include "exec.h"
#include "wiz_list.h"

extern int trace_level;

/*
 * This file implements delayed calls of functions.
 * Static functions can not be called this way.
 *
 * Allocate the structures several in one chunk, to get rid of malloc
 * overhead.
 */

#define CHUNK_SIZE	20

struct call {
    int delta;
    union {
	struct {
	    char *name;
	    struct object *ob;
	} named;
	struct svalue lambda;
    } function;
    int is_lambda;
    struct svalue v;
    struct call *next;
    struct object *command_giver;
};

static struct call *call_list=0, *call_list_free=0;
static int num_call;

/*
 * Free a call out structure.
 */
static void free_call(cop)
    struct call *cop;
{
    if (cop->v.type == T_LVALUE) {
	int i;
	struct svalue *v;

	i = cop->v.x.num_arg;
	v = cop->v.u.lvalue;
	do {
	    free_svalue(v++);
	} while (--i);
	xfree((char *)cop->v.u.lvalue);
    } else {
	free_svalue(&cop->v);
    }
    cop->next = call_list_free;
    if (cop->is_lambda) {
	free_closure(&cop->function.lambda);
    } else {
	free_string(cop->function.named.name);
	free_object(cop->function.named.ob, "free_call");
    }
    if (cop->command_giver)
	free_object(cop->command_giver, "free_call");
    call_list_free = cop;
}

static INLINE void free_used_call(cop)
    struct call *cop;
{
    cop->next = call_list_free;
    if (cop->command_giver)
	free_object(cop->command_giver, "free_call");
    call_list_free = cop;
}

/*
 * Setup a new call out.
 */
void new_call_out(arg, num_arg)
    struct svalue *arg;
    int num_arg;
{
    int delay;
    struct call *cop, **copp, *cop2;

    if (!call_list_free) {
	int i;
	call_list_free =
	    (struct call *)permanent_xalloc(CHUNK_SIZE * sizeof (struct call));
	for (i=0; i<CHUNK_SIZE - 1; i++)
	    call_list_free[i].next  = &call_list_free[i+1];
	call_list_free[CHUNK_SIZE-1].next = 0;
	num_call += CHUNK_SIZE;
    }
    cop = call_list_free;
    call_list_free = call_list_free->next;
    if(arg[0].type == T_CLOSURE) {
	cop->function.lambda = arg[0];
	cop->is_lambda = 1;
    } else {
	struct object *ob;

	if (arg[0].x.string_type == STRING_SHARED) {
	    cop->function.named.name = arg[0].u.string;
	} else {
	    cop->function.named.name = make_shared_string(arg[0].u.string);
	    if (arg[0].x.string_type == STRING_MALLOC)
		xfree(arg[0].u.string);
	}
	cop->function.named.ob = ob = current_object;
	add_ref(ob, "call_out");
	cop->is_lambda = 0;
    }
    cop->command_giver = command_giver; /* save current player context */
    if (command_giver)
	add_ref(command_giver, "new_call_out");		/* Bump its ref */
    if (num_arg > 1) {
	struct svalue *v, *w;

	v = (struct svalue *)xalloc(num_arg * sizeof *v);
	cop->v.type = T_LVALUE;
	cop->v.x.num_arg = num_arg;
	cop->v.u.lvalue = v;
	w = &arg[2];
	do {
	    if (w->type == T_LVALUE) {
		/* We could give an error here, but it would be more work */
		free_svalue(w);
		w->type = T_NUMBER;
		w->u.number = 0;
	    }
	    transfer_svalue_no_free(v++, w++);
	} while (--num_arg);
    } else if (num_arg) {
	if (arg[2].type != T_LVALUE) {
	    transfer_svalue_no_free(&cop->v, &arg[2]);
	} else {
	    free_svalue(&arg[2]);
	    cop->v.type = T_NUMBER;
	    cop->v.u.number = 0;
	}
    } else {
	cop->v.type = T_NUMBER;
	cop->v.u.number = 0;
    }
    delay = arg[1].u.number;
    if (delay < 1)
	delay = 1;
    for (copp = &call_list; cop2 = *copp; copp = &cop2->next) {
	int delta;
	if ((delta = cop2->delta) >= delay) {
	    cop2->delta -= delay;
	    cop->delta = delay;
	    cop->next = *copp;
	    *copp = cop;
	    return;
	}
	delay -= delta;
    }
    *copp = cop;
    cop->delta = delay;
    cop->next = 0;
}

/*
 * See if there are any call outs to be called. Set the 'command_giver'
 * if it is a living object. Check for shadowing objects, which may also
 * be living objects.
 */
void call_out() {
    extern struct object *command_giver;
    extern struct object *current_interactive;
    extern int current_time;
    extern int tracedepth;
    extern struct svalue *inter_sp;
    extern struct wiz_list default_wizlist_entry;
    extern int32 initial_eval_cost, eval_cost, assigned_eval_cost;

    static int last_time;

    static struct call *current_call_out; /* don't set back with longjmp() */
    struct error_recovery_info error_recovery_info;
    struct svalue *sp;

    if (call_list == 0) {
	last_time = current_time;
	return;
    }
    if (last_time == 0)
	last_time = current_time;
    current_interactive = 0;
    call_list->delta -= current_time - last_time;
    last_time = current_time;
    error_recovery_info.last = error_recovery_pointer;
    error_recovery_info.type = ERROR_RECOVERY_BACKEND;
    error_recovery_pointer = &error_recovery_info;
    if (setjmp(error_recovery_info.context)) {
	extern void clear_state();

	struct call *cop;
	struct object *ob;
	struct wiz_list *user;

	clear_state();
	debug_message("Error in call out.\n");
	cop = current_call_out;
	if (cop->is_lambda)
	    ob = cop->function.lambda.u.lambda->ob;
	else
	    ob = cop->function.named.ob;
	if ( !(user = ob->user) )
	    user = &default_wizlist_entry;
	user->call_out_cost = eval_cost;
	cop->v.type = T_INVALID;
	free_call(cop);
    }
    tracedepth = 0;
    sp = inter_sp+1;
    while (call_list && call_list->delta <= 0) {
	struct call *cop;
	ph_int type;
	int num_arg;
	/*
	 * Move the first call_out out of the chain.
	 */
	cop = call_list;
	call_list = cop->next;
	current_call_out = cop;
	/*
	 * A special case:
	 * If a lot of time has passed, so that current call out was missed,
	 * then it will have a negative delta. This negative delta implies
	 * that the next call out in the list has to be adjusted.
	 */
	if (cop->delta < 0 && call_list)
	    call_list->delta += cop->delta;
	if (cop->command_giver && !(cop->command_giver->flags & O_DESTRUCTED))
	{
	    command_giver = cop->command_giver;
	    if (command_giver->interactive)
		trace_level = command_giver->interactive->trace_level;
	} else {
	    struct object *ob;

	    if (cop->is_lambda)
		ob = cop->function.lambda.u.lambda->ob;
	    else {
		ob = cop->function.named.ob;
		if (ob->flags & O_DESTRUCTED) {
		    free_call(cop);
		    continue;
		}
	    }
	    while(ob->shadowing)
		ob = ob->shadowing;
	    if (ob->flags & O_ENABLE_COMMANDS) {
		command_giver = ob;
		if (command_giver->interactive)
		    trace_level =
		      command_giver->interactive->trace_level;
	    } else {
		command_giver = 0;
		trace_level = 0;
	    }
	}
	if ((type = cop->v.type) == T_LVALUE) {
	    struct svalue *v;
	    int i;
	    struct object *ob;

	    v = cop->v.u.lvalue;
	    num_arg = i = cop->v.x.num_arg;
	    sp--;
	    do {
		if (v->type == T_OBJECT &&
		    (ob = v->u.ob)->flags & O_DESTRUCTED)
		{
		    sp++;
		    free_object(ob, "call_out");
		    sp->type = T_NUMBER;
		    sp->u.number = 0;
		    v++;
		} else {
		    *++sp = *v++;
		}
	    } while (--i);
	    xfree((char *)cop->v.u.lvalue);
	    inter_sp = sp;
	    sp -= num_arg - 1;
	} else {
	    /* single argument */
	    struct object *ob;

	    if (type == T_OBJECT) {
		ob = cop->v.u.ob;
		if (ob->flags & O_DESTRUCTED) {
		    free_object(ob, "call_out");
		    sp->type = T_NUMBER;
		    sp->u.number = 0;
		} else {
		    sp->type = T_OBJECT;
		    sp->u.ob = ob;
		}
	    } else {
		*sp = cop->v;
	    }
	    num_arg = 1;
	    inter_sp = sp;
	}
	if (cop->is_lambda) {
	    struct object *ob;
	    struct wiz_list *user;

	    ob = cop->function.lambda.u.lambda->ob;
	    current_object = ob;
	    if ( !(user = ob->user) )
		user = &default_wizlist_entry;
	    if (user->last_call_out != current_time) {
		user->last_call_out = current_time;
		CLEAR_EVAL_COST;
	    } else {
		assigned_eval_cost = eval_cost = user->call_out_cost;
	    }
	    call_lambda(&cop->function.lambda, num_arg);
	    user->call_out_cost = eval_cost;
	    free_closure(&cop->function.lambda);
	    free_svalue(sp);
	} else {
	    struct object *ob;

	    if ((ob = cop->function.named.ob)->flags & O_DESTRUCTED) {
		do {
		    pop_stack();
		} while (--num_arg);
	    } else {
		struct wiz_list *user;

		current_object = ob;
		if ( !(user = ob->user) )
		    user = &default_wizlist_entry;
		if (user->last_call_out != current_time) {
		    user->last_call_out = current_time;
		    CLEAR_EVAL_COST;
		} else {
		    assigned_eval_cost = eval_cost = user->call_out_cost;
		}
	        sapply(cop->function.named.name, ob, num_arg);
		user->call_out_cost = eval_cost;
	    }
	    free_string(cop->function.named.name);
	    free_object(cop->function.named.ob, "free_call");
	}
	free_used_call(cop);
    }
    inter_sp = sp - 1;
    error_recovery_pointer = error_recovery_info.last;
}

/*
 * Throw away a call out. First call to this function is discarded.
 * The time left until execution is returned.
 * -1 is returned if no call out pending.
 */
void remove_call_out(ob, fun)
    struct object *ob;
    struct svalue *fun;
{
    struct call **copp, *cop;
    int delay = 0;
    char *fun_name;

    if (fun->x.string_type == STRING_SHARED) {
	fun_name = fun->u.string;
    } else {
	fun_name = make_shared_string(fun->u.string);
	if (fun->x.string_type == STRING_MALLOC)
	    xfree(fun->u.string);
    }
    fun->type = T_NUMBER;
    for (copp = &call_list; cop = *copp; copp = &cop->next) {
	delay += cop->delta;
	if (cop->function.named.ob == ob &&
	    cop->function.named.name == fun_name &&
	    !cop->is_lambda)
	{
	    decrement_string_ref(fun_name);
	    if (cop->next)
		cop->next->delta += cop->delta;
	    *copp = cop->next;
	    free_call(cop);
	    fun->u.number = delay;
	    return;
	}
    }
    free_string(fun_name);
    fun->u.number = -1;
}

void find_call_out(ob, fun)
    struct object *ob;
    struct svalue *fun;
{
    struct call *cop;
    int delay = 0;
    char *fun_name;

    if (fun->x.string_type == STRING_SHARED) {
	fun_name = fun->u.string;
    } else {
	fun_name = make_shared_string(fun->u.string);
	if (fun->x.string_type == STRING_MALLOC)
	    xfree(fun->u.string);
    }
    fun->type = T_NUMBER;
    for (cop = call_list; cop; cop = cop->next) {
	delay += cop->delta;
	if (cop->function.named.ob == ob &&
	    cop->function.named.name == fun_name &&
	    !cop->is_lambda)
	{
	    decrement_string_ref(fun_name);
	    fun->u.number = delay;
	    return;
	}
    }
    free_string(fun_name);
    fun->u.number = -1;
}

int print_call_out_usage(verbose)
    int verbose;
{
    int i;
    struct call *cop;

    for (i=0, cop = call_list; cop; cop = cop->next)
	i++;
    if (verbose) {
	add_message("\nCall out information:\n");
	add_message("---------------------\n");
	add_message("Number of allocated call outs: %8d, %8d bytes\n",
		    num_call, num_call * sizeof (struct call));
	add_message("Current length: %d\n", i);
    } else {
	add_message("call out:\t\t\t%8d %8d (current length %d)\n", num_call,
		    num_call * sizeof (struct call), i);
    }
    return num_call * sizeof (struct call);
}

#ifdef DEBUG
void count_extra_ref_from_call_outs()
{
    struct call *cop;

    for (cop = call_list; cop; cop = cop->next) {
	if (cop->v.type == T_LVALUE)
	{
	    count_extra_ref_in_vector(cop->v.u.lvalue, cop->v.x.num_arg);
	} else {
	    count_extra_ref_in_vector(&cop->v, 1);
	}
	if (cop->is_lambda) {
	    count_extra_ref_in_vector(&cop->function.lambda, 1);
	} else {
	    count_extra_ref_in_object(cop->function.named.ob);
	}
	if (cop->command_giver)
	    count_extra_ref_in_object(cop->command_giver);
    }
}
#endif

void remove_stale_call_outs() {
    struct call **copp, *cop;

    for (copp = &call_list; cop = *copp; ) {
	if (!cop->is_lambda) {
	    if (cop->function.named.ob->flags & O_DESTRUCTED) {
		if (cop->next)
		    cop->next->delta += cop->delta;
		*copp = cop->next;
		free_call(cop);
		continue;
	    }
	}
	copp = &cop->next;
    }
}

#ifdef MALLOC_smalloc
void clear_ref_from_call_outs()
{
    struct call *cop;

    for (cop = call_list; cop; cop = cop->next) {
	struct object *ob;

	if (cop->v.type == T_LVALUE) {
	    struct svalue *v;

	    v = cop->v.u.lvalue;
	    clear_ref_in_vector(v, cop->v.x.num_arg);
	} else {
	    clear_ref_in_vector(&cop->v, 1);
	}
	if (cop->is_lambda) {
	    clear_ref_in_vector(&cop->function.lambda, 1);
	    /* else: cop->function.named.ob isn't destructed */
	}
	if ((ob = cop->command_giver) && ob->flags & O_DESTRUCTED) {
	    ob->ref = 0;
	    ob->prog->ref = 0;
	}
    }
}

void count_ref_from_call_outs()
{
    struct call *cop;
    struct object *ob;

    for (cop = call_list; cop; cop = cop->next) {
	if (cop->v.type == T_LVALUE) {
	    struct svalue *v;

	    v = cop->v.u.lvalue;
	    count_ref_in_vector(v, cop->v.x.num_arg);
	} else {
	    count_ref_in_vector(&cop->v, 1);
	}
	if (cop->is_lambda) {
	    count_ref_in_vector(&cop->function.lambda, 1);
	} else {
	    /* destructed objects have been taken care of beforehand */
	    cop->function.named.ob->ref++;
	    count_ref_from_string(cop->function.named.name);
	}
	if (ob = cop->command_giver) {
	    if (ob->flags & O_DESTRUCTED) {
		reference_destructed_object(ob);
		cop->command_giver = 0;
	    } else {
		ob->ref++;
	    }
	}
    }
}
#endif

/*
 * Construct an array of all pending call_outs. Every item in the array
 * consists of 4 items (but only if the object not is destructed):
 * 0:	The object.
 * 1:	The function (string).
 * 2:	The delay.
 * 3:	The argument.
 *      If there is more than one argument, the extra arguments are put
 *      in extra members of the array.
 */
struct vector *get_all_call_outs() {
    int i, next_time;
    struct call *cop;
    struct vector *v;

    for (i=0, cop = call_list; cop; cop = cop->next) {
	if (!cop->is_lambda && cop->function.named.ob->flags & O_DESTRUCTED)
	    continue;
	i++;
    }
    v = allocate_array(i);
    next_time = 0;
    /*
     * Take for granted that all items in an array are initialized to
     * number 0.
     */
    for (i=0, cop = call_list; cop; cop = cop->next) {
	struct vector *vv;
	char *function_name;

	next_time += cop->delta;
	vv = allocate_array(cop->v.type == T_LVALUE ? 3 + cop->v.x.num_arg : 4);
	if (cop->is_lambda) {
	    assign_svalue_no_free(&vv->item[1], &cop->function.lambda);
	} else {
	    struct object *ob;

	    ob = cop->function.named.ob;
	    if (ob->flags & O_DESTRUCTED) {
		free_vector(vv);
		continue;
	    }
	    vv->item[0].type = T_OBJECT;
	    vv->item[0].u.ob = ob;
	    add_ref(ob, "get_all_call_outs");
	    vv->item[1].type = T_STRING;
	    vv->item[1].x.string_type = STRING_SHARED;
	    function_name = cop->function.named.name;
	    increment_string_ref(function_name);
	    vv->item[1].u.string = function_name;
	}
	vv->item[2].u.number = next_time;
	if (cop->v.type == T_LVALUE) {
	    struct svalue *source, *dest;
	    int j;

	    source = cop->v.u.lvalue;
	    dest = &vv->item[3];
	    j = cop->v.x.num_arg;
	    do {
		assign_svalue_no_free(dest++, source++);
	    } while (--j);
	} else {
	    assign_svalue_no_free(&vv->item[3], &cop->v);
	}

	v->item[i].type = T_POINTER;
	v->item[i].u.vec = vv;		/* Ref count is already 1 */
	i++;
    }
    return v;
}