/
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 <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#ifdef HAVE_VALUES_H
#include <values.h>
#endif

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

#ifdef AMIGA
#include "hosts/amiga/ixfile.h"
#include "hosts/amiga/socket.h"
#endif

/*
 * Swap out programs from objects.
 * Every block, free or allocated, is at least of size sizeof(struct program) .
 * The start can be interpreted as a struct program that habe at least a valid
 * total_size and ref field.   ref == 0 marks a free block.
 */

/* How many times should swapbuf be refilled when trying to swap an object
 * before we conclude that it's better to use fresh space?
 */
#define REFILL_MAX (2)

mp_int num_swapped = 0;
mp_int total_bytes_swapped = 0;
mp_int num_swapfree = 0;
mp_int total_bytes_swapfree = 0;
mp_int swapfile_size = 0;
static mp_int swapfile_scannable = 0;
/* Number of bytes that have been reused from freed swap prog blocks */
mp_int total_swap_reused = 0;

char file_name[100];

FILE *swap_file = (FILE *) 0;	/* The swap file is opened once */

mp_int total_num_prog_blocks, total_prog_block_size;

extern int d_flag;

static char *swapbuf = (char *)0;
		/* Pointer to a buffer that is used to scan for free blocks.
		 * The first sizeof(struct program) bytes are used to hold
		 * the end of the previous block.
		 */
static mp_int swapbuf_offs = 0;
		/* Offset into swapfile to the block that is loaded into
		 * swapbuf + sizeof(struct program)
		 * It is a multiple of SCAN_SWAP_BUFSIZE, in order to map
		 * efficiently to disk blocks.
		 */
static mp_int free_swap_offs = sizeof(struct program);
		/* Rover offset used to scan for free blocks in swapbuf.
		 * It is relative to the start of swapbuf, thus, you have
		 * swapbuff_offs + free_swap_offs - sizeof(struct program)
		 * as the corresponding position in the swapfile.
		 */
static mp_int free_swap_start;
		/* Offset into swap file to current run of free blocks */
static mp_int free_swap_size = 0;
		/* Total size of free blocks in current run */
static mp_int free_swap_num;
		/* Number of free blocks in current run */
static int free_swap_terminated = 1;
		/* The run of free blocks has been completely scanned */

#ifndef BUG_FREE
static mp_int debug_free_swap_offs = -1;
#endif

/*
 * marion - adjust pointers for swap out and later relocate on swap in
 *   program
 *   line_numbers
 *   functions
 *   strings
 *   variable_names
 *   inherit
 *   argument_types
 *   type_start
 */
int
locate_out (prog) struct program *prog; {
    char *p = 0; /* keep cc happy */

    if (!prog) return 0;
    if (d_flag > 1) {
	debug_message ("locate_out: %lX %lX %lX %lX %lX %lX %lX %lX\n",
	    prog->program, prog->line_numbers, prog->functions,
	    prog->strings, prog->variable_names, prog->inherit,
	    prog->argument_types, prog->type_start);
    }
    prog->program	= &p[prog->program - (char *)prog];
    prog->line_numbers	= (char *)
	&p[(char *)prog->line_numbers - (char *)prog];
    prog->functions	= (uint32 *)
	&p[(char *)prog->functions - (char *)prog];
    prog->function_names= (unsigned short *)
	&p[(char *)prog->function_names - (char *)prog];
    prog->strings	= (char **)
	&p[(char *)prog->strings - (char *)prog];
    prog->variable_names= (struct variable *)
	&p[(char *)prog->variable_names - (char *)prog];
    prog->inherit	= (struct inherit *)
	&p[(char *)prog->inherit - (char *)prog];
    if (prog->type_start) {
	prog->argument_types = (unsigned short *)
	    &p[(char *)prog->argument_types - (char *)prog];
	prog->type_start = (unsigned short *)
	    &p[(char *)prog->type_start - (char *)prog];
    }
    return 1;
}


/*
 * marion - relocate pointers after swap in
 *   program
 *   line_numbers
 *   functions
 *   strings
 *   variable_names
 *   inherit
 *   argument_types
 *   type_start
 */
int
locate_in (prog) struct program *prog; {
    extern int32 current_id_number;
    char *p = (char *)prog;

    if (!prog) return 0;
    prog->id_number	=
	++current_id_number ? current_id_number : renumber_programs();
    prog->program	= &p[prog->program - (char *)0];
    prog->line_numbers	= (char *)
	&p[(char *)prog->line_numbers - (char *)0];
    prog->functions	= (uint32 *)
	&p[(char *)prog->functions - (char *)0];
    prog->function_names= (unsigned short *)
	&p[(char *)prog->function_names - (char *)0];
    prog->strings	= (char **)
	&p[(char *)prog->strings - (char *)0];
    prog->variable_names= (struct variable *)
	&p[(char *)prog->variable_names - (char *)0];
    prog->inherit	= (struct inherit *)
	&p[(char *)prog->inherit - (char *)0];
    if (prog->type_start) {
	prog->argument_types = (unsigned short *)
	    &p[(char *)prog->argument_types - (char *)0];
	prog->type_start     = (unsigned short *)
	    &p[(char *)prog->type_start - (char *)0];
    }
    if (d_flag > 1) {
	debug_message ("locate_in: %lX %lX %lX %lX %lX %lX %lX\n",
	    prog->program, prog->line_numbers, prog->functions,
	    prog->strings, prog->variable_names, prog->inherit,
	    prog->argument_types, prog->type_start);
    }
    return 1;
}

/* The argument refilled is a pointer to a counter which holds the number
 * of refills. It is usually initialised to Zero by thye caller before the
 * first call. Well, set_swapbuf() sets it to -1 to indicate that the buffer
 * contents are invalid.
 * Don't call if there is no swapbuf or
 * swapfile_scannable < sizeof(struct program)
 * Returns success status, which only depends on *refilled.
 */
int refill_swapbuf(refilled)
    int *refilled;
{
    mp_int offs = free_swap_offs;
/* extra: Extra bytes we have to read. Is zero when we can reuse  *
 * the old end of the buffer, sizeof(struct program) if we can't. */
    int extra = 0;

#ifndef BUG_FREE
    if (offs & (sizeof(p_int) - 1))
	fatal("Bad alignment of free_swap_offs: %x\n", (p_int)offs);
    debug_free_swap_offs = offs;
#endif
    if (++*refilled >= REFILL_MAX)
	return 0;
    if (swapbuf_offs + offs - sizeof(struct program) >= swapfile_scannable) {
	swapbuf_offs = 0;
	free_swap_start = 0;
	free_swap_size = 0;
	free_swap_num = 0;
	offs = sizeof(struct program);
	free_swap_terminated = 1;
    } else {
	while (offs >= SCAN_SWAP_BUFSIZE) {
	    offs -= SCAN_SWAP_BUFSIZE;
	}
	swapbuf_offs += free_swap_offs - offs;
	if (offs < sizeof(struct program) ) {
	    /* *refilled was incremented above, so test for 0 */
	    if (*refilled && free_swap_offs - offs == SCAN_SWAP_BUFSIZE) {   
		memcpy(
		  swapbuf,
		  swapbuf+SCAN_SWAP_BUFSIZE,
		  sizeof(struct program)
		);
	    } else {
		extra = sizeof(struct program);
	    }
	}
    }

    /* This test is good not only for debugging, but also when the disk is on
     * fire, to stop subsequent damage.
     */
    if (swapfile_scannable < swapbuf_offs)
	fatal("Scan attempt to reuse space beyond the end of the swapfile.\n");
    if (fseek(swap_file, swapbuf_offs - extra, 0) == -1) {
	fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
	      errno, swapbuf_offs - extra);
    }
    if (fread(
	swapbuf + sizeof(struct program) - extra,
	swapfile_scannable - swapbuf_offs < SCAN_SWAP_BUFSIZE ?
	  swapfile_scannable - swapbuf_offs + extra :
	  SCAN_SWAP_BUFSIZE + extra,
	1,
	swap_file
    ) != 1) {
	fatal("Couldn't read the swap file, errno %d, offset %d, size %d.\n",
	      errno, swapbuf_offs - extra,
	      swapfile_scannable - swapbuf_offs < SCAN_SWAP_BUFSIZE ?
		swapfile_scannable - swapbuf_offs + extra :
		SCAN_SWAP_BUFSIZE + extra);
    }
    if (swapfile_scannable - swapbuf_offs + sizeof(struct program) <=
	SCAN_SWAP_BUFSIZE)
    {
	/* The last bytes of the buffer are invalid. Their number is in the
	 * range sizeof(struct program) .. SCAN_SWAP_BUFSIZE .
	 * We fake an allocated block to mask them.
	 * It is possible that the swapfile grows later beyond the area
	 * shadowed by the buffer, or has already grown, thus
	 * swapfile_scannable does not only save i/o, but is also necessary
	 * for consistence.
	 */
	struct program *fake_block =
	  (struct program *)(swapbuf+swapfile_scannable-swapbuf_offs) + 1;
	fake_block->ref = 1;
	fake_block->total_size = SCAN_SWAP_BUFSIZE;
    }
    free_swap_offs = offs;
    return 1;
}

void set_swapbuf(buf)
    char *buf;
{
    int refilled = -1;

    swapbuf = buf;
    if (buf && swapfile_size) {
	swapfile_scannable = swapfile_size;
	refill_swapbuf(&refilled);
    }
}

/*
 * Swap out an object. Only the program is swapped, not the 'struct object'.
 *
 * marion - the swap seems to corrupt the function table
 */
int swap(ob)
    struct object *ob;
{
    struct program *prog;
    mp_int swap_num;
    char *write_buffer;
    int write_size;
    int refilled;

    if (ob->flags & O_DESTRUCTED)
	return 0;
    if (d_flag > 1) { /* marion */
	debug_message("Swap object %s (ref %d)\n", ob->name, ob->ref);
    }
    if (swap_file == 0) {
#ifndef MSDOS
	char host[50];
	gethostname(host, sizeof host);
	sprintf(file_name, "%s.%s", SWAP_FILE, host);
	swap_file = fopen(file_name, "w+b");
#else
	swap_file = fopen(strcpy(file_name,"LPMUD.SWAP"),"w+b");
#endif
	/* Leave this file pointer open ! */
	if (swap_file == 0)
	    return 0;
    }
    if ( !(prog = ob->prog) )
    {
	fprintf(stderr, "warning:no program in object %s, don't swap it\n",
		ob->name);
	/* It's no good freeing a NULL pointer */
	return 0;
    }
    if ((ob->flags & O_HEART_BEAT) || (ob->flags & O_CLONE)) {
	if (d_flag > 1) {
	    debug_message ("  object not swapped - heart beat or cloned.\n");
	}
	return 0;
    }
    if (prog->ref > 1 || ob->interactive) {
	if (d_flag > 1) {
	    debug_message ("  object not swapped - inherited or interactive.\n");
	}
	return 0;
    }
    /*
     * Has this object already been swapped, and read in again ?
     * Then it is very easy to swap it out again.
     */
    if (ob->swap_num >= 0) {
	total_bytes_swapped += prog->total_size;
	free_prog(prog, 0);		/* Do not free the strings */
	ob->prog = 0;
	ob->flags |= O_SWAPPED;
	num_swapped++;
	return 1;
    }
#ifndef BUG_FREE
    if (free_swap_offs & (sizeof(p_int) - 1))
	fatal("Bad alignment of free_swap_offs: %x\n", (p_int)free_swap_offs);
    debug_free_swap_offs = free_swap_offs;
#endif
    refilled = 0;
    if (!swapbuf || total_bytes_swapfree < prog->total_size)
	refilled = REFILL_MAX + 1; /* Don't refill */
    while (
      refilled <= REFILL_MAX &&
      free_swap_size != prog->total_size &&
      free_swap_size < prog->total_size + sizeof(struct program)
    ) {
	if (free_swap_terminated) {
	    while (
	      (free_swap_offs < SCAN_SWAP_BUFSIZE || refill_swapbuf(&refilled)) &&
	      ((struct program *)(swapbuf+free_swap_offs))->ref )
	    {
		free_swap_offs +=
		  ((struct program *)(swapbuf+free_swap_offs))->total_size;
#ifndef BUG_FREE
		if (free_swap_offs & (sizeof(p_int) - 1))
		    fatal(
			"Bad alignment of free_swap_offs: %x\n",
			(p_int)free_swap_offs
		    );
#endif
	    }
	    free_swap_terminated = 0;
	    free_swap_start =
	      swapbuf_offs + free_swap_offs - sizeof(struct program);
	    /* free_swap_start might point now beyond the end of the swapfile.
	     * This is harmless as long as free_swap_size == 0. It will be
	     * fixed by refill_swapbuf(), unless refilled is exhausted.
	     */
	    free_swap_size = 0;
	    free_swap_num = 0;
	}
	while (free_swap_offs < SCAN_SWAP_BUFSIZE || refill_swapbuf(&refilled) )
	{
	    int size;
	    if ( ((struct program *)(swapbuf+free_swap_offs))->ref ) {
		free_swap_terminated = 1;
#ifndef BUG_FREE
		size = ((struct program *)(swapbuf+free_swap_offs))->total_size;
		if (size >
		    swapfile_scannable + SCAN_SWAP_BUFSIZE -
		     (swapbuf_offs + free_swap_offs - sizeof(struct program)) ||
		    size & (sizeof(p_int) - 1))
		    fatal("Bad allocated block size: %x\n", size);
#endif
		break;
	    }
	    size = ((struct program *)(swapbuf+free_swap_offs))->total_size;
	    free_swap_size += size;
	    free_swap_num++;
	    free_swap_offs += size;
	}
#ifndef BUG_FREE
        /* These extra tests allow to notice bugs earlier, thus giving
         * 'better' cores.
         */
	if (swapfile_scannable < free_swap_start + free_swap_size &&
	    free_swap_size)
	{
	    fatal("Attempt to reuse space beyond the end of the swapfile.\n");
	}
#endif
    }
    /* relocate the internal pointers */
    locate_out ((struct program *)prog);
    write_buffer = (char *)prog;
    write_size = prog->total_size;
    if (free_swap_size == write_size || (
	free_swap_size >= write_size + sizeof(struct program) && (
	    write_buffer = alloca(write_size+sizeof(struct program)),
	    memcpy(write_buffer, (char*)prog, write_size),
	    ((struct program *)((char*)write_buffer+write_size))->ref = 0,
	    ((struct program *)((char*)write_buffer+write_size))->total_size =
		free_swap_size - write_size,
	    write_size += sizeof(struct program),
	    num_swapfree++,
	MY_TRUE)
    ) ) {
	swap_num = free_swap_start;
	free_swap_start += prog->total_size;
	free_swap_size -= prog->total_size;
	num_swapfree -= free_swap_num;
	total_bytes_swapfree -= prog->total_size;
	total_swap_reused += prog->total_size;
	free_swap_num = 1;

	/* This test is good not only for debugging, but also when the disk is
	 * on fire, to stop subsequent damage.
	 */
	if (swapfile_scannable <= swap_num)
	    fatal("Attempt to reuse space beyond the end of the swapfile.\n");
    } else {
	swap_num = swapfile_size;
	swapfile_size += write_size;
    }
    if (fseek(swap_file, swap_num, 0) == -1) {
	fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
	      errno, swap_num);
    }
    if (swap_num != ftell(swap_file)) fatal("seek failure\n");
    /*
     * marion - it is more efficient to write one item the size of the
     *   program to the file than many items of size one. besides, it's
     *   much more reasonable, as the fwrite only fails for the whole
     *   block and not for a part of it.
     */
    ob->swap_num = swap_num;
    if (fwrite(write_buffer, write_size, 1, swap_file) != 1) {
	debug_message("I/O error in swap.\n");
	ob->swap_num = -1;
	return 0;
    }
    total_bytes_swapped += prog->total_size;
    num_swapped++;
    free_prog(prog, 0);	/* Don't free the shared strings */
    ob->prog = 0;
    ob->flags |= O_SWAPPED;
    return 1;
}

void load_ob_from_swap(ob)
    struct object *ob;
{
    extern int errno;
    struct program tmp_prog;

#ifndef BUG_FREE
    if (ob->swap_num == -1)
	fatal("Loading not swapped object.\n");
#endif

    /* This test is good not only for debugging, but also when the disk is on
     * fire, to stop subsequent damage.
     */
    if (swapfile_size <= ob->swap_num)
	fatal("Attempt to swap in from beyond the end of the swapfile.\n");
    if (fseek(swap_file, ob->swap_num, 0) == -1)
	fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
	      errno, ob->swap_num);
    if (d_flag > 1) { /* marion */
	debug_message("Unswap object %s (ref %d)\n", ob->name, ob->ref);
    }
    /*
     * The size of the program is unkown, so read first part to
     * find out.
     *
     * marion - again, the read in a block is more efficient
     */
    if (fread((char *)&tmp_prog, sizeof tmp_prog, 1, swap_file) != 1) {
	fatal("Couldn't read the swap file.\n");
    }
    ob->prog = (struct program *)xalloc(tmp_prog.total_size);
    memcpy((char *)ob->prog, (char *)&tmp_prog, sizeof tmp_prog);
    fread((char *)ob->prog + sizeof tmp_prog,
	  tmp_prog.total_size - sizeof tmp_prog, 1, swap_file);
    /*
     * to be relocated:
     *   program
     *   line_numbers
     *   functions
     *   strings
     *   variable_names
     *   inherit
     *	 argument_types
     *	 type_start
     *   to be replaced: id_number
     */
    locate_in (ob->prog); /* relocate the internal pointers */

    /* The reference count will already be 1 ! */
    ob->flags &= ~O_SWAPPED;
    if (!(ob->flags & O_DESTRUCTED) && function_exists("clean_up",ob)) {
	ob->flags |= O_WILL_CLEAN_UP;
    }
    total_bytes_swapped -= ob->prog->total_size;
    num_swapped--;
    total_prog_block_size += ob->prog->total_size;
    total_num_prog_blocks += 1;
}

int remove_swap_file(ob)
    struct object *ob;
{
    static p_int null_ref = 0;
#ifdef DEBUG
    if (ob->swap_num == -1)
	fatal("removing non-existant swap file.\n");
#endif
    if (ob->flags & O_SWAPPED)
	load_ob_from_swap(ob);
#ifndef BUG_FREE
    if (!ob->prog) fatal("removing swap file whith no program in object\n");
#endif

    /* This test is good not only for debugging, but also when the
     * processor is on fire, to stop subsequent damage.
     */
    if (swapfile_size <= ob->swap_num)
	fatal("Attempt to remove swap entry beyond the end of the swapfile.\n");
    if (fseek(
	swap_file,
	ob->swap_num +
	 ((char *)&((struct program *)0)->ref - (char *)((struct program *)0)),
	0
      ) == -1)
	fatal("Couldn't seek the swap file, errno %d, offset %d.\n",
	      errno, ob->swap_num);
    if (fwrite((char *)&null_ref, sizeof null_ref, 1, swap_file) != 1) {
	debug_message("I/O error in swap.\n");
	return 0;
    }
    total_bytes_swapfree += ob->prog->total_size;
    num_swapfree++;
    ob->swap_num = -1;
    return 1;
}

/*
 * This one is called at shutdown. Remove the swap file.
 */
void unlink_swap_file() {
    if (swap_file == 0)
	return;
#ifndef MSDOS
    unlink(file_name);
    fclose(swap_file);
#else
    fclose(swap_file);
    unlink(file_name);
#endif
}