/*
  debug.c

  This file keeps the debug() efun. All debug information and
  debug switches are managed from here.

*/
#include <varargs.h>
#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>		/* sys/types.h and netinet/in.h are here to enable include of comm.h below */
#include <sys/stat.h>
/* #include <netinet/in.h> Included in comm.h below */
#include <memory.h>

#include "config.h"
#include "lint.h"
#include "exec.h"
#include "interpret.h"

#ifdef RUSAGE			/* Defined in config.h */
#ifdef SOLARIS
#include <sys/times.h>
#include <limits.h>
#else
#include <sys/resource.h>
extern int getrusage (int, struct rusage *);
#ifdef sun
extern int getpagesize();
#endif
#ifndef RUSAGE_SELF
#define RUSAGE_SELF	0
#endif
#endif /* SOLARIS */
static struct vector *make_cpu_array (int,struct program *[]); 
#endif


#include "object.h"
#include "instrs.h"
#include "patchlevel.h"
#include "comm.h"
#include "switch.h"
#include "mapping.h"
#include "mudstat.h"

#define DUMP_FILE "OBJECT_DUMP"

int call_warnings;

extern struct svalue const0, const1;
static void make_fun_map (struct program *);

/*
 * The array below is the available subcommands to the debug() efun.
 *
 */
       			/*   Name		   Number   Params  */
static	char	*debc[] = { "index",		/* 0 */
			    "malloc", 		/* 1 */
			    "status", 		/* 2 */
			    "status tables",	/* 3 */
			    "mudstatus",	/* 4 	    on/off eval time */
			    "functionlist",	/* 5 	    object */
			    "rusage",		/* 6 */
			    "top_ten_cpu",	/* 7 */
			    "object_cpu",	/* 8 	    object */
			    "swap",		/* 9 	    object */
			    "version",		/* 10 */
			    "wizlist",		/* 11 	    wizname */
			    "trace",		/* 12 	    bitmask */
			    "traceprefix",	/* 13       pathstart */
			    "call_out_info",	/* 14       object */
			    "inherit_list",	/* 15	    object */
			    "load_average",	/* 16 */
			    "shutdown",		/* 17 */
			    "object_info",	/* 18 	    num object */
			    "function_map",	/* 19	    OBSOLETE */
			    "send_udp",		/* 20       host, port, msg */
			    "mud_port",		/* 21       */
			    "udp_port",		/* 22       */
			    "set_wizard",	/* 23       player */
			    "ob_flags",	        /* 24       ob */
			    "get_variables",	/* 25       ob null/varname */
			    "get_eval_cost",	/* 26 */
                            "debug malloc",     /* 27 */
			    "getprofile",	/* 28	    object */
			    "get_avg_response",	/* 29 */
			    "destruct",         /* 30       object */
			    "destroy",          /* 31       object */
			    "update snoops",    /* 32 */
			    "call_warnings",    /* 33       on/off */
			    "dump_objects",     /* 34 */
			    "query_debug_ob",     /* 35 object */
			    "set_debug_ob",      /* 36 object flags */
			    "set_swap",         /* 37
			    ({min_mem, max_mem, min_time, max_time}) */
			    "query_swap",       /* 38 */
			    "set_debug_prog", /* 39  object */
			    "query_debug_prog", /* 40 object flags */
			    0
			  };

extern struct vector *inherit_list (struct object *);

struct svalue *
debug_command(char *debcmd, int argc, struct svalue *argv)
{
    static struct svalue retval;
    int dbnum, dbi, il;

    for (dbi = -1, dbnum = 0; debc[dbnum]; dbnum++)
    {
	if (strcmp(debcmd, debc[dbnum]) == 0)
	    dbi = dbnum;
    }
    if (dbi < 0)
    {
	retval.type = T_NUMBER;
	retval.u.number = 0;
	return &retval;
    }

    switch (dbi)
    {
    case 0: /* index */
	retval.type = T_POINTER;
	retval.u.vec = allocate_array(dbnum);
	for (il = 0; il < dbnum; il++)
	{
	    retval.u.vec->item[il].type = T_STRING;
	    retval.u.vec->item[il].string_type = STRING_CONSTANT;
	    retval.u.vec->item[il].u.string = debc[il];
	}
	return &retval;
	break;
    case 1: /* malloc */
	retval.type = T_STRING;
	retval.string_type = STRING_MALLOC;
	retval.u.string = string_copy((char *)dump_malloc_data());
	return &retval;
	break;
    case 2: /* status */
    case 3: /* status tables */
	retval.type = T_STRING;
	retval.string_type = STRING_MALLOC;
	retval.u.string = (char *)get_gamedriver_info(debc[dbi]);
	return &retval;
	break;
    case 4: /* mudstatus on/off eval_lim time_lim */
	if (argc < 3 || 
	    argv[0].type != T_STRING ||
	    argv[1].type != T_NUMBER ||
	    argv[2].type != T_NUMBER)
	    break;
	if (strcmp(argv[0].u.string, "on") == 0)
	    mudstatus_set(1, argv[1].u.number, argv[2].u.number);
	else if (strcmp(argv[0].u.string, "off") == 0)
	    mudstatus_set(0, argv[1].u.number, argv[2].u.number);
	else
	    break;
	retval.type = T_NUMBER;
	retval.u.number = 1;
	return &retval;
	break;
    case 5: /* functionlist object */
	if (argc < 1 || argv[0].type != T_OBJECT)
	    break;
	access_program(argv->u.ob->prog);
	retval.type = T_POINTER;
	retval.u.vec = allocate_array(argv[0].u.ob->prog->num_functions);
	for (il = 0; il < (int)argv[0].u.ob->prog->num_functions; il++)
	{
	    retval.u.vec->item[il].type = T_STRING;
	    retval.u.vec->item[il].string_type = STRING_SHARED;
	    retval.u.vec->item[il].u.string = 
                argv[0].u.ob->prog->functions[il].name;
	    increment_string_ref(argv[0].u.ob->prog->functions[il].name);
	}
	access_program(current_prog);
	return &retval;
	break;
#ifdef RUSAGE /* Only defined if we compile GD with RUSAGE */
    case 6: /* rusage */
    {
	char buff[500];
#ifdef SOLARIS
	struct tms buffer;

	if (times(&buffer) == -1)
	    buff[0] = 0;
	else
	    sprintf(buff, "%ld %ld (s/%ld)",
		    buffer.tms_utime, buffer.tms_stime, CLK_TCK);
#else
	struct rusage rus;
	long utime, stime;
	int maxrss;
	
	if (getrusage(RUSAGE_SELF, &rus) < 0)
	    buff[0] = 0;
	else {
	    utime = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000;
	    stime = rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000;
	    maxrss = rus.ru_maxrss;
#ifdef sun
	    maxrss *= getpagesize() / 1024;
#endif
	    sprintf(buff, "%ld %ld %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
		    utime, stime, maxrss, rus.ru_ixrss, rus.ru_idrss,
		    rus.ru_isrss, rus.ru_minflt, rus.ru_majflt, rus.ru_nswap,
		    rus.ru_inblock, rus.ru_oublock, rus.ru_msgsnd, 
		    rus.ru_msgrcv, rus.ru_nsignals, rus.ru_nvcsw, 
		    rus.ru_nivcsw);
	}
#endif /* SOLARIS */
	retval.type = T_STRING;
	retval.string_type = STRING_MALLOC;
	retval.u.string = string_copy(buff);
	return &retval;
	break;
    }
    case 7: /* top_ten_cpu */
    {
#define NUMBER_OF_TOP_TEN 10
	struct program *p[NUMBER_OF_TOP_TEN];
	struct object *ob; 
	struct vector *v;
	int i, j;
	for(i = 0; i < NUMBER_OF_TOP_TEN; i++) 
	    p[i] = (struct program *)0L;
	ob = obj_list;
	do
	{
	    if (!ob->prog)  /* That this case exists is just weird /JnA */
		continue;

	    for(i = NUMBER_OF_TOP_TEN-1; i >= 0; i--) 
	    {
		if ( p[i] && (ob->prog->cpu <= p[i]->cpu))
		    break;
	    }

	    if (i < (NUMBER_OF_TOP_TEN - 1)) 
		for (j = 0; j <= i; j++)
		    if (strcmp(p[j]->name,ob->prog->name) == 0)
		    {
			i = NUMBER_OF_TOP_TEN-1;
			break;
		    }

	    if (i < (NUMBER_OF_TOP_TEN - 1)) 
	    {
		j = NUMBER_OF_TOP_TEN - 2;
		while(j > i) 
		{
		    p[j + 1] = p[j];
		    j--;
		}
		p[i + 1] = ob->prog;
	    }
	} while (obj_list != (ob = ob->next_all));
	v = make_cpu_array(NUMBER_OF_TOP_TEN, p);        
	if (v) 
	{                                                   
	    retval.type = T_POINTER;
	    retval.u.vec = v;
	    return &retval;
	}
	break;
#undef NUMBER_OF_TOP_TEN
    }
    case 8: /* object_cpu object */
    {
	int c_num;

	if (argc && (argv[0].type == T_OBJECT)) 
	{
	    long cpu = argv[0].u.ob->prog->cpu;
	    c_num = cpu;
	} 
	else 
	{
#ifdef SOLARIS
	    struct tms buffer;
	    
	    if (times(&buffer) == -1)
		c_num = -1;
	    else
		c_num = buffer.tms_utime + buffer.tms_stime;
#else
	    struct rusage rus;         

	    if (getrusage(RUSAGE_SELF, &rus) < 0) 
	    {  
		c_num = -1;
	    }
	    else 
	    {                                                               
		c_num =  (rus.ru_utime.tv_sec * 1000 + 
			  rus.ru_utime.tv_usec / 1000 +
			  rus.ru_stime.tv_sec * 1000 + 
			  rus.ru_stime.tv_usec / 1000);
	    }
#endif
        }
	retval.type = T_NUMBER;
	retval.u.number = c_num;
	return &retval;
	break;
    }
#else /* RUSAGE */
    case 6:
    case 7: /* rusage, top_ten_cpu and object_cpu */
    case 8:
	retval.type = T_STRING;
	retval.string_type = STRING_CONSTANT;
	retval.u.string = "Only valid if GD compiled with RUSAGE flag.\n";
	return &retval;
	break;
#endif /* RUSAGE */
    
    case 9:  /*	swap,		object 		*/
#if 0        /* can not swap while executing */
	if (argc && (argv[0].type == T_OBJECT))
	    (void)swap(argv[0].u.ob);
#endif
	retval = const1;
	return &retval;
	break;
    case 10: /*	version,		  	*/
    {
	char buff[9];
	sprintf(buff, "%6.6s%02d", GAME_VERSION, PATCH_LEVEL);
	retval.type = T_STRING;
	retval.string_type = STRING_MALLOC;
	retval.u.string = string_copy(buff);
	return &retval;
	break;
    }
    case 11: /* wizlist,  	wizname	 	*/
	/*
	 * Prints information, will be changed
         */
	retval = const1;
	return &retval;
	break;
    case 12: /* trace, 		bitmask		*/
    {
	int ot = -1;
	if (command_giver && command_giver->interactive) 
	{
	    if (argc && (argv[0].type == T_NUMBER))
	    {
		ot = command_giver->interactive->trace_level;
		command_giver->interactive->trace_level = argv[0].u.number;
	    }
	}
	retval.type = T_NUMBER;
	retval.u.number = ot;
	return &retval;
    }
	break;
    case 13: /* traceprefix, 	pathstart	*/
    {
	char *old = 0;
	
	if (command_giver && command_giver->interactive) 
	{
	    struct svalue *arg;
	    if (argc)
	    {
		old = command_giver->interactive->trace_prefix;
		if (argv[0].type == T_STRING) 
		{
		    command_giver->interactive->trace_prefix = 
			make_shared_string(argv[0].u.string);
		} 
		else
		    command_giver->interactive->trace_prefix = 0;
	    }
	}
	if (old) 
	{
	    retval.type = T_STRING;
	    retval.string_type = STRING_SHARED;
	    retval.u.string = old;
	} 
	else 
	    retval = const0;

	return &retval;
    }
	break;
    case 14: /*	call_out_info,	  		*/
	{
	    extern struct vector *get_calls(struct object *);
	    if (argv[0].type != T_OBJECT)
		break;
	    retval.type = T_POINTER;
	    retval.u.vec =  get_calls(argv[0].u.ob);
	    return &retval;
	    break;
	}
    case 15: /* inherit_list, 	object		*/
    {
	struct vector *vec;
	
	if (argc && (argv[0].type == T_OBJECT))
	{
	    retval.type = T_POINTER;
	    retval.u.vec = inherit_list(argv[0].u.ob);
	    return &retval;
	}
	else
	{
	    retval = const0;
	    return &retval;
	}
	break;
    }
    case 16: /*	load_average,	  		*/
	retval.type = T_STRING;
	retval.string_type = STRING_MALLOC;
	retval.u.string = string_copy(query_load_av());
	return &retval;
	break;

    case 17: /*	shutdown,		  	*/
	startshutdowngame(0);
	retval = const1;
	return &retval;
	break;
	    
    case 18: /* "object_info",	num object 	*/
    {
	struct object *ob;
	char db_buff[1024], tdb[200];
	int i;
	
	if (argc < 2 || argv[0].type != T_NUMBER || argv[1].type != T_OBJECT)
	    break;

	if (argv[0].u.number == 0) 
	{
	    int flags;
	    struct object *obj2;
	    
	    if ( argv[1].type != T_OBJECT)
		break;
	    ob = argv[1].u.ob;
	    flags = ob->flags;
	    sprintf(db_buff,"O_ENABLE_COMMANDS : %s\nO_CLONE           : %s\nO_DESTRUCTED      : %s\nO_SWAPPED         : %s\nO_ONCE_INTERACTIVE: %s\nO_CREATED         : %s\n",
			flags&O_ENABLE_COMMANDS ?"TRUE":"FALSE",
			flags&O_CLONE           ?"TRUE":"FALSE",
			flags&O_DESTRUCTED      ?"TRUE":"FALSE",
			flags&O_SWAPPED          ?"TRUE":"FALSE",
			flags&O_ONCE_INTERACTIVE?"TRUE":"FALSE",
			flags&O_CREATED		?"TRUE":"FALSE");
	    
	    sprintf(tdb,"time_of_ref : %d\n", ob->time_of_ref);
	    strcat(db_buff, tdb);
	    sprintf(tdb,"ref         : %d\n", ob->ref);
	    strcat(db_buff, tdb);
#ifdef DEBUG
	    sprintf(tdb,"extra_ref   : %d\n", ob->extra_ref);
	    strcat(db_buff, tdb);
#endif
	    sprintf(tdb,"swap_num    : %ld\n", ob->swap_num);
	    strcat(db_buff, tdb);
	    sprintf(tdb,"name        : '%s'\n", ob->name);
	    strcat(db_buff, tdb);
	    sprintf(tdb,"next_all    : OBJ(%s)\n",
			ob->next_all?ob->next_all->name:"NULL");
	    strcat(db_buff, tdb);
	    if (obj_list == ob) 
	    {
		sprintf(tdb,"This object is the head of the object list.\n");
		strcat(db_buff, tdb);
	    }

	    obj2 = obj_list;
	    i = 1;
	    do
		if (obj2->next_all == ob) 
		{
		    sprintf(tdb,"Previous object in object list: OBJ(%s)\n",
			    obj2->name);
		    strcat(db_buff, tdb);
		    sprintf(tdb, "position in object list:%d\n",i);
		    strcat(db_buff, tdb);
		    
		}
	    while (obj_list != (obj2 = obj2->next_all));
	}
        else if (argv[0].u.number == 1) 
        {
	    if (argv[1].type != T_OBJECT)
		break;
	    ob = argv[1].u.ob;
	    
	    sprintf(db_buff,"program ref's %d\n", ob->prog->ref);
	    sprintf(tdb,"Name %s\n", ob->prog->name);
	    strcat(db_buff, tdb);
	    sprintf(tdb,"program size %d\n", ob->prog->program_size);
	    strcat(db_buff, tdb);
	    sprintf(tdb,"num func's %d (%d) \n", ob->prog->num_functions
			,ob->prog->num_functions * sizeof(struct function));
	    strcat(db_buff, tdb);
	    sprintf(tdb,"sizeof rodata %d\n", ob->prog->rodata_size);
	    strcat(db_buff, tdb);
	    sprintf(tdb,"num vars %d (%d)\n", ob->prog->num_variables
			,ob->prog->num_variables * sizeof(struct variable));
	    strcat(db_buff, tdb);
	    sprintf(tdb,"num inherits %d (%d)\n", ob->prog->num_inherited
			,ob->prog->num_inherited * sizeof(struct inherit));
	    strcat(db_buff, tdb);
	    sprintf(tdb,"total size %d\n", ob->prog->total_size);
	    strcat(db_buff, tdb);
	}
        else
	{
	    sprintf(db_buff, "Bad number argument to object_info: %d\n",
		    argv[0].u.number);
        }
	retval.type = T_STRING;
	retval.string_type = STRING_MALLOC;
	retval.u.string = string_copy(db_buff);
	return &retval;
	break;
    }
    case 19: /* function_map,	19	    1 / 0 object */
    {
	/*
	    This is now obsolete, as all functions are hashed
	 */
	if (argc < 2 || 
	    argv[0].type != T_NUMBER ||
	    argv[1].type != T_OBJECT)
	    break;

	retval = const0;
	return &retval;
    }
    case 20: /* send_udp,	20     		host, port, msg */
    {
	int tmp;

	if (argc < 3 || 
	    argv[0].type != T_STRING ||
	    argv[1].type != T_NUMBER ||
	    argv[2].type != T_STRING)
	    break;
	tmp = send_udp(argv[0].u.string, argv[1].u.number, argv[2].u.string);
	if (tmp)
	    retval = const1;
	else
	    retval = const0;
	return &retval;
    }
    case 21: /* mud_port,	21  */
    {
	extern int port_number;
	retval.type = T_NUMBER;
	retval.u.number = port_number;
	return &retval;
    }
    case 22: /* udp_port,	22  */
    {
#ifdef CATCH_UDP_PORT
	extern int udp_port;
	retval.u.number = udp_port;
#else
	retval.u.number = -1;
#endif
	retval.type = T_NUMBER;
	return &retval;
    }
    case 23: /* set_wizard, 	object		*/
    {
	if (argc && (argv[0].type == T_OBJECT))
	{
	    retval = const1;
	    return &retval;
	}
	else
	{
	    retval = const0;
	    return &retval;
	}
	break;
    }
    case 24: /* ob_flags,	24 ob  */
    {
	if (argc && (argv[0].type == T_OBJECT))
	{
	    retval.type = T_NUMBER;
	    retval.u.number = argv[0].u.ob->flags;
	    return &retval;
	}
	retval = const0;
	return &retval;
    }
    case 25: /* get_variables, 25       object NULL/string */
    {
	struct svalue get_variables();
	struct svalue get_variable();
	
 	switch (argc)
 	{
 	case 1:
 	    if ( argv[0].type != T_OBJECT)
 	    {
 		retval = const0;
 		return &retval;
 	    }
 	    retval = get_variables(argv[0].u.ob);
 	    return &retval;
 	case 2:
 	    if ( argv[0].type != T_OBJECT || argv[1].type != T_STRING)
 	    {
 		retval = const0;
 		return &retval;
 	    }
 	    retval = get_variable(argv[0].u.ob, argv[1].u.string);
 	    return &retval;
 	case 3:
	    if ( argv[0].type == T_OBJECT && argv[1].type == T_STRING)
	    {
		retval = get_variable(argv[0].u.ob, argv[1].u.string);
		return &retval;
	    }
 	    if ( argv[0].type == T_OBJECT)
	    {
		retval = get_variables(argv[0].u.ob);
		return &retval;
	    }
	    retval = const0;
	    return &retval;
 	default:
 	    retval = const0;
 	    return &retval;
 	    
 	}
    }
    case 26: /* get_eval_cost,	26  */
    {
	extern int eval_cost;
	retval.type = T_NUMBER;
	retval.u.number = eval_cost;
	return &retval;
    }

    case 27: /* debug malloc, 27 */
    {
#ifdef DEBUG_MALLOC
        debug_malloc();
#endif
        retval = const1;
        return &retval;
    }    
    case 28: /* getprofile, 28	object */
    {
#ifndef PROFILE_FUNS
	retval.type = T_STRING;
	retval.string_type = STRING_CONSTANT;
	retval.u.string = "Only valid if GD compiled with PROFILE_FUNS flag.\n";
	return &retval;
	break;
#else
	if (argc < 1 || argv[0].type != T_OBJECT)
	    break;
	retval.type = T_POINTER;
	retval.u.vec = allocate_array(argv[0].u.ob->prog->num_functions);
	access_program(argv[0].u.ob->prog);
	for (il = 0; il < argv[0].u.ob->prog->num_functions; il++)
	{
	    char buff[200]; /* I know longer funnames crashes the GD... */

	    sprintf(buff, "%09d:%09ld: %s",
		    argv[0].u.ob->prog->functions[il].num_calls,
		    argv[0].u.ob->prog->functions[il].time_spent / 100,
		    argv[0].u.ob->prog->functions[il].name);
	    retval.u.vec->item[il].type = T_STRING;
	    retval.u.vec->item[il].string_type = STRING_MALLOC;
	    retval.u.vec->item[il].u.string = string_copy(buff);
	}
	access_program(current_prog);
	return &retval;
	break;
#endif
    }
    case 29: /* get_avg_response, 29 */
    {
	extern int get_msecs_response();
	extern int msr_point;
	int il, sum, num, tmp;

	if (msr_point >=0)
	{
	    for (sum = 0, num = 0,il = 0; il < 100; il++)
	    {
		if ((tmp = get_msecs_response(il)) >=0)
		{
		    sum += tmp;
		    num++;
		}
	    }
	    retval.type = T_NUMBER;
	    retval.u.number = (num > 0) ? sum / num : 0;
	    return &retval;
	}
	break;
    }
    case 30: /* destruct, 30 */
    case 31: /* destroy, 31 */
    {
	extern void destruct_object();
	if (argc && argv[0].type == T_OBJECT &&
            !(argv[0].u.ob->flags & O_DESTRUCTED))
            destruct_object(argv[0].u.ob);
	break;
    }
    case 32: /* update snoops, 31 */
	read_snoop_file();
	break;
    case 33: /* call_warnings, int 0 = off, 1 = on */
	if (argc && (argv[0].type == T_STRING))
	{
	    if (strcmp(argv[0].u.string, "on") == 0)
		call_warnings++;
	    else
		call_warnings = call_warnings > 0 ? call_warnings - 1 : 0;
	    retval.type = T_NUMBER;
	    retval.u.number = call_warnings;
	    return &retval;
	}
	else
	{
	    retval.type = T_NUMBER;
	    retval.u.number = -1;
	    return &retval;
	}
	break;
    case 34: /* dump objects */
    {
	FILE *ufile;
	char *mem_variables(struct object *);
	struct object *ob, *nob;
	char line[255];      
       
	if ((ufile = fopen(DUMP_FILE, "w")) == NULL)
	{
	    retval.type = T_NUMBER;
	    retval.u.number = -1;
	    return &retval;
	}
       
	ob = obj_list;
	do
	{
	    if (ob)
	    {
		extern int num_call_outs(struct object *);
		int num_co, num_inv;
		struct object *o;

		num_co = num_call_outs(ob);

		for (num_inv = 0, o = ob->contains; o;
		     num_inv++, o = o->next_inv) ;

		sprintf(line, "%s %s in %s callouts %d inventory %d\n",
			mem_variables(ob), ob->name,
			ob->super ? ob->super->name : "(void)",
			num_co, num_inv);
	    }
	    if (fputs(line, ufile) < 0)
	    {
		fclose(ufile);
		break;
	    }
	    nob = ob->next_all;
	}
	while (obj_list != (ob = nob));
	fclose(ufile);
	break;
    }
    case 35: /* query_debug_ob */
	if (!argc || argv[0].type != T_OBJECT)
	    break;
	retval.type = T_NUMBER;
	retval.u.number = argv[0].u.ob->debug_flags;
	return &retval;
    case 36: /* set_debug_ob */
	if (!argc || argv[0].type != T_OBJECT || argv[1].type != T_NUMBER)
	    break;
	retval.type = T_NUMBER;
	retval.u.number = argv[0].u.ob->debug_flags;
	argv[0].u.ob->debug_flags = argv[1].u.number;
	return &retval;
	
    case 37: /* set_swap */
	{
	    extern int max_swap_memory;
	    extern int min_swap_memory;
	    extern int min_swap_time;
	    extern int max_swap_time;
	    struct vector *v;
	    
	    if (!argc || argv[0].type != T_POINTER ||
		argv[0].u.vec->size != 4)
		break;
	    v = argv[0].u.vec;
	    if (v->item[0].type != T_NUMBER ||
		v->item[0].u.number < 0) 
		break; /* Too low min_swap_memory */
	    
	    if (v->item[1].type != T_NUMBER ||
		v->item[1].u.number < v->item[0].u.number)
		break;
	    
	    if (v->item[2].type != T_NUMBER ||
		v->item[2].u.number < 0)
		break;
	    
	    if (v->item[3].type != T_NUMBER ||
		v->item[3].u.number < v->item[2].u.number)
		break;
	    min_swap_memory = v->item[0].u.number;
	    max_swap_memory = v->item[1].u.number;
	    min_swap_time = v->item[2].u.number;
	    max_swap_time = v->item[3].u.number;
	    retval.type = T_NUMBER;
	    retval.u.number = 1;
	    return &retval;
	}
	
    case 38: /* query_swap */
	{
	    if (argc == 0 || argv[0].type == T_NUMBER &&
		argv[0].u.number == 0)
	    {
		extern int max_swap_memory;
		extern int min_swap_memory;
		extern int min_swap_time;
		extern int max_swap_time;
		struct vector *v;
		
		v = allocate_array(4);
		v->item[0].type = T_NUMBER;
		v->item[0].u.number = min_swap_memory;
		v->item[1].type = T_NUMBER;
		v->item[1].u.number = max_swap_memory;
		v->item[2].type = T_NUMBER;
		v->item[2].u.number = min_swap_time;
		v->item[3].type = T_NUMBER;
		v->item[3].u.number = max_swap_time;
		
		retval.type = T_POINTER;
		retval.u.vec = v;
		return &retval;
	    }
	    else if (argv[0].type == T_NUMBER)
	    {
		extern int used_memory;
		extern int swap_out_obj_sec, swap_out_obj_min;
		extern int swap_out_prog_sec, swap_out_prog_min;
		extern int swap_in_obj_sec, swap_in_obj_min;
		extern int swap_in_prog_sec, swap_in_prog_min;
		extern int swap_out_obj_hour, swap_in_obj_hour;
		extern int swap_out_prog_hour, swap_in_prog_hour;
		extern struct object *swap_ob;
		extern struct program *swap_prog;
		extern int used_memory;

		switch(argv[0].u.number)
		{
		case 1:
		    {
			struct vector *v, *v0;
			
			v = allocate_array(4);
			
			v0 = allocate_array(3);
			v0->item[0].type = T_NUMBER;
			v0->item[0].u.number = swap_in_obj_sec;
			v0->item[1].type = T_NUMBER;
			v0->item[1].u.number = swap_in_obj_min;
			v0->item[2].type = T_NUMBER;
			v0->item[2].u.number = swap_in_obj_hour;
			v->item[0].type = T_POINTER;
			v->item[0].u.vec = v0;
			
			v0 = allocate_array(3);
			v0->item[0].type = T_NUMBER;
			v0->item[0].u.number = swap_in_prog_sec;
			v0->item[1].type = T_NUMBER;
			v0->item[1].u.number = swap_in_prog_min;
			v0->item[2].type = T_NUMBER;
			v0->item[2].u.number = swap_in_prog_hour;
			v->item[1].type = T_POINTER;
			v->item[1].u.vec = v0;
			
			v0 = allocate_array(3);
			v0->item[0].type = T_NUMBER;
			v0->item[0].u.number = swap_out_obj_sec;
			v0->item[1].type = T_NUMBER;
			v0->item[1].u.number = swap_out_obj_min;
			v0->item[2].type = T_NUMBER;
			v0->item[2].u.number = swap_out_obj_hour;
			v->item[2].type = T_POINTER;
			v->item[2].u.vec = v0;
			
			v0 = allocate_array(3);
			v0->item[0].type = T_NUMBER;
			v0->item[0].u.number = swap_out_prog_sec;
			v0->item[1].type = T_NUMBER;
			v0->item[1].u.number = swap_out_prog_min;
			v0->item[2].type = T_NUMBER;
			v0->item[2].u.number = swap_out_prog_hour;
			v->item[3].type = T_POINTER;
			v->item[3].u.vec = v0;
			
			retval.type = T_POINTER;
			retval.u.vec = v;
			return &retval;
		    }
		case 2:
		    retval.type = T_NUMBER;
		    retval.u.number =
			(swap_prog->time_of_ref < swap_ob->time_of_ref ?
			 swap_prog->time_of_ref :
			 swap_prog->time_of_ref);
		    return &retval;
		case 3:
		    retval.type = T_NUMBER;
		    retval.u.number = used_memory;
		    return &retval;
		}    
	    }
	    break;
	}		
    case 39: /* query_debug_prog */
	if (!argc || argv[0].type != T_OBJECT)
	    break;
	retval.type = T_NUMBER;
	retval.u.number = argv[0].u.ob->prog->debug_flags;
	return &retval;
    case 40: /* set_debug_prog */
	if (argc < 2 || argv[0].type != T_OBJECT || argv[1].type != T_NUMBER)
	    break;
	retval.type = T_NUMBER;
	retval.u.number = argv[0].u.ob->prog->debug_flags;
	argv[0].u.ob->prog->debug_flags = argv[1].u.number;
	return &retval;
    }
    retval = const0;
    return &retval;
}

#ifdef RUSAGE
static struct vector *
make_cpu_array(int i, struct program *prog[])
{
    int num;
    struct vector *ret;
    char buff[1024]; /* should REALLY be enough */

    if (i <= 0) 
	return 0;
    ret = allocate_array(i);

    for(num = 0; num < i; num++) 
    {
	sprintf(buff, "%8.8ld:%s", prog ? prog[num]->cpu : 0L, 
		prog ? prog[num]->name : "");
	free_svalue(&ret->item[num]);
	ret->item[num].type = T_STRING;
	ret->item[num].string_type = STRING_MALLOC;
	ret->item[num].u.string = string_copy(buff);
    }
    return ret;
}
#endif

struct svalue 
get_variables(struct object *ob)
{
    int i, j;
    struct vector *names;
    struct vector *values;
    struct svalue res;
    extern char *make_shared_string();
    struct program *prog;
    int num_var;
    struct object oob;
    
    if (ob->flags & O_DESTRUCTED || !ob->variables)
	return const0;

    access_object(ob);
    
    num_var = ob->prog->num_variables + ob->prog->inherit[ob->prog->num_inherited - 1]
	.variable_index_offset;

    names = allocate_array(num_var);
    values = allocate_array(num_var);
    
    for (j = ob->prog->num_inherited - 1; j >= 0; j--)
	if (!(ob->prog->inherit[j].type & TYPE_MOD_SECOND) &&
	    ob->prog->num_variables > 0)
	{
	    access_program(ob->prog->inherit[j].prog);
	    for (i = 0; i < (int)ob->prog->inherit[j].prog->num_variables; i++)
	    {
		if (num_var == 0)
		    error("Wrong number of variables in object.\n");
		names->item[--num_var].type = T_STRING;
		names->item[num_var].string_type = STRING_SHARED;
		names->item[num_var].u.string =
		    ob->prog->inherit[j].prog->
		      variable_names[i].name;
		increment_string_ref(ob->prog->inherit[j].prog->
				       variable_names[i].name);
		assign_svalue_no_free(&values->item[num_var],
				      &ob->variables[ob->prog->inherit[j]
						     .variable_index_offset + i]);
	    }
	}
    res.type = T_MAPPING;
    res.u.map = make_mapping(names, values);
    free_vector(names);
    free_vector(values);
    access_object(current_object);
    access_program(current_prog);
    return res;
}

struct svalue 
get_variable(struct object *ob, char *var_name)
{
    int i;
    struct svalue res = const0;
    
   if (ob->flags & O_DESTRUCTED || !ob->variables)
	return res;
    access_object(ob);
    
    if ((i = find_status(ob->prog, var_name,0)) != -1)
	assign_svalue_no_free(&res, &ob->variables[i]);
    access_object(current_object);
    return res;
}

/* Using globals is a bit unclean. I should return and tidy up later */
static int mapping_elem, array_elem, string_size, num_string;
static int num_map, num_arr, num_num, num_ob, num_float;

void mem_mapping(struct mapping *);
void mem_array(struct vector *);

INLINE void
mem_incr(struct svalue *var)
{
    switch(var->type)
    {
    case T_MAPPING:
	mapping_elem += var->u.map->size;
	num_map++;
	mem_mapping(var->u.map);
	break;
    case T_POINTER:
	array_elem += var->u.vec->size;
	num_arr++;
	mem_array(var->u.vec);
	break;
    case T_STRING:
	string_size += strlen(var->u.string);
	num_string++;
	break;
    case T_OBJECT:
	/* Check for destructed objects while we'r at it :) */
	if (var->u.ob->flags & O_DESTRUCTED)
	{
	    num_num++;
	    free_svalue(var);
	    break;
	}
	num_ob++;
	break;
    case T_FLOAT:
	num_float++;
	break;
    case T_NUMBER:
	num_num++;
	break;
    default:
	;
    }
}

void
mem_mapping(struct mapping *m)
{
    struct apair **p;
    struct apair **next;
    int i;
    int size;

    if (m->size < 0)
	return;
    size = m->size;
    m->size = -1;
    for (i = 0 ; i < size ; i++)
    {
        for(p = &m->pairs[i]; *p; )
        {
	    /* Check for destructed objects while we'r at it :) */
	    if ((*p)->arg.type == T_OBJECT &&
		(*p)->arg.u.ob->flags & O_DESTRUCTED)
	    {
		struct apair *f;

		f = *p;
		*p = f->next;
		f->next = 0;
		free_apairs(f);
		mapping_elem--;
	    }
	    else
	    {
		mem_incr(&(*p)->arg);
		mem_incr(&(*p)->val);
		p = &(*p)->next;
	    }
        }
    }
    m->size = size;
}

void
mem_array(struct vector *v)
{
    int i;
    int size;

    if (v->size < 0)
	return;
    size = v->size;
    v->size = -1;
    for (i = 0; i < size; i++)
	mem_incr(&v->item[i]);
    v->size = size;
}

char *
mem_variables(struct object *ob)
{
    int i, j;
    struct svalue *var;
    struct program *prog;
    int num_var;
    static char buf[128];
    int was_swapped;
    extern void swap_object(struct object *);

    if (ob->flags & O_DESTRUCTED || !ob->variables)
	return "NOT AVAILABLE                       ";

    if (ob->flags & O_SWAPPED)
    {
	load_ob_from_swap(ob);
	was_swapped = 1;
    }
    else
	was_swapped = 0;

    mapping_elem = 0;
    array_elem = 0;
    string_size = 0;
    num_string = 0;
    num_arr = 0;
    num_map = 0;
    num_ob = 0;
    num_num = 0;
    num_float = 0;

    num_var = ob->prog->num_variables +
	ob->prog->inherit[ob->prog->num_inherited - 1].variable_index_offset;

    for (i = -1; i < num_var; i++)
    {
	mem_incr(&ob->variables[i]);
    }
    sprintf(buf, "A %6d %6d M %6d %6d S %6d %6d O %6d N %6d F %6d",
	    num_arr, array_elem, num_map, mapping_elem,
	    num_string, string_size, num_ob, num_num, num_float);
    if (was_swapped)
	swap_object(ob);
    return buf;
}