lpmud/mudlib/
lpmud/mudlib/doc/
lpmud/mudlib/doc/LPC/
lpmud/mudlib/log/
lpmud/mudlib/players/
lpmud/mudlib/room/maze1/
lpmud/mudlib/room/sub/
#include "os.h"
#include "object.h"
#include "lnode.h"
#include "config.h"

#define SWAP_SIZE   50000   /* Big enough for most objects. */

/* simulate.c */
extern void fatal(char *fmt, ...);

/* lnode.c */
extern int tot_lnode_block;

/* main.c */
extern void *xalloc(int size);
extern char *string_copy(char *str);
extern void debug_message(char *a, ...);
extern char *escape_path (char *str);

static int swap_number = 1;

static char *swap_area;
static int swap_max_size, swap_size;

int num_swapped;
int total_bytes_swapped;

/* swap.c */
int swap(struct object *ob);
void load_ob_from_swap(struct object *ob);
void remove_swap_file(struct object *ob);

static void ass_size (unsigned int s);
static void move_prog_to_swap_area (struct lnode *p);
static struct lnode *load_prog_swap_area (char *base, char *s);

int swap (struct object *ob)
{
  char *file;
  FILE *f;

  if (NUM_RESET_TO_SWAP == 0)
    return 0;
  if (ob->heart_beat && ob->enable_heart_beat)
    return 0;
  if (ob->prog->num_ref > 1)
    return 0;
  file = xalloc (strlen (SWAP_DIR) + strlen ("/LPMUDswapXXXXXXXX") + 1);
  sprintf (file, "%s/LPMUDswap%d", SWAP_DIR, swap_number++);
  escape_path (file);
  if (ob->swap_num > 0) {
    struct stat st;
    /* There is already a swap file for this object. */
    free_prog (ob->prog);
    ob->prog = 0;
    ob->swapped = 1;
    ob->heart_beat = 0;         /* This pointer has to be set up again */
    num_swapped++;
    if (stat (file, &st) == -1) {
      perror (file);
      fatal ("Coulnd't stat a swap file.\n");
    }
    total_bytes_swapped += st.st_size;
    free (file);
    return 1;
  }
  f = fopen (file, "wb");
  if (f == 0) {
    perror (file);
    debug_message ("Couldn't open swap file %s for write\n", file);
    free (file);
    return 0;
  }
  swap_area = xalloc (SWAP_SIZE);
  swap_size = 0;
  swap_max_size = SWAP_SIZE;
  move_prog_to_swap_area ((struct lnode *) ob->prog);
  if (fwrite (swap_area, 1, swap_size, f) != (unsigned int) swap_size) {
    debug_message ("Error in writing to swap.\n");
    free (swap_area);
    free (file);
    return 0;
  }
  total_bytes_swapped += swap_size;
  fclose (f);
  free (swap_area);
  free (file);
  free_prog (ob->prog);
  ob->prog = 0;
  ob->swapped = 1;
  num_swapped++;
  ob->swap_num = swap_number - 1;
  ob->heart_beat = 0;           /* This pointer has to be set up again */
  return 1;
}

static void ass_size (unsigned int s)
{
  while (s + swap_size > (unsigned int) swap_max_size) {
    swap_max_size *= 2;
    swap_area = realloc (swap_area, swap_max_size);
    if (swap_area == 0)
      fatal ("swap realloc %d\n", swap_max_size);
  }
}

static void move_prog_to_swap_area (struct lnode *p)
{
  unsigned int s;
  int beg = swap_size;

  switch (p->line & L_MASK) {
  case L_SINGLE:
    s = sizeof (struct lnode_single);
    ass_size (s);
    memcpy (swap_area + swap_size, (char *) p, s);
    swap_size += s;
    break;
  case L_NUMBER:
    s = sizeof (struct lnode_number);
    ass_size (s);
    memcpy (swap_area + swap_size, (char *) p, s);
    swap_size += s;
    break;
  case L_NAME:
    s = sizeof (struct lnode_name);
    ass_size (s);
    memcpy (swap_area + swap_size, (char *) p, s);
    swap_size += s;
    ((struct lnode_name *) (swap_area + beg))->name = (char *) swap_size;
    s = strlen (((struct lnode_name *) p)->name) + 1;
    ass_size (s);
    strcpy (swap_area + swap_size, ((struct lnode_name *) p)->name);
    swap_size += s;
    break;
  case L_1:
    s = sizeof (struct lnode_1);
    ass_size (s);
    memcpy (swap_area + swap_size, (char *) p, s);
    swap_size += s;
    if (((struct lnode_1 *) p)->expr) {
      ((struct lnode_1 *) (swap_area + beg))->expr =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (((struct lnode_1 *) p)->expr);
    }
    break;
  case L_2:
    s = sizeof (struct lnode_2);
    ass_size (s);
    memcpy (swap_area + swap_size, (char *) p, s);
    swap_size += s;
    if (((struct lnode_2 *) p)->expr1) {
      ((struct lnode_2 *) (swap_area + beg))->expr1 =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (((struct lnode_2 *) p)->expr1);
    }
    if (((struct lnode_2 *) p)->expr2) {
      ((struct lnode_2 *) (swap_area + beg))->expr2 =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (((struct lnode_2 *) p)->expr2);
    }
    break;
  case L_3:
    s = sizeof (struct lnode_3);
    ass_size (s);
    memcpy (swap_area + swap_size, (char *) p, s);
    swap_size += s;
    if (((struct lnode_3 *) p)->expr1) {
      ((struct lnode_3 *) (swap_area + beg))->expr1 =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (((struct lnode_3 *) p)->expr1);
    }
    if (((struct lnode_3 *) p)->expr2) {
      ((struct lnode_3 *) (swap_area + beg))->expr2 =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (((struct lnode_3 *) p)->expr2);
    }
    if (((struct lnode_3 *) p)->expr3) {
      ((struct lnode_3 *) (swap_area + beg))->expr3 =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (((struct lnode_3 *) p)->expr3);
    }
    break;
  case L_DEF:
    {
      struct lnode_def *dp = (struct lnode_def *) p;
      s = sizeof (struct lnode_def);
      ass_size (s);
      memcpy (swap_area + swap_size, (char *) p, s);
      swap_size += s;
      ((struct lnode_def *) (swap_area + beg))->name = (char *) swap_size;
      s = strlen (dp->name) + 1;
      ass_size (s);
      strcpy (swap_area + swap_size, dp->name);
      swap_size += s;
      ((struct lnode_def *) (swap_area + beg))->block =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (dp->block);
      if (dp->next) {
        ((struct lnode_def *) (swap_area + beg))->next =
          (struct lnode_def *) swap_size;
        move_prog_to_swap_area ((struct lnode *) dp->next);
      }
      break;
    }
  case L_FUNCALL:
    s = sizeof (struct lnode_funcall);
    ass_size (s);
    memcpy (swap_area + swap_size, (char *) p, s);
    swap_size += s;
    ((struct lnode_funcall *) (swap_area + beg))->name = (char *) swap_size;
    s = strlen (((struct lnode_funcall *) p)->name) + 1;
    ass_size (s);
    strcpy (swap_area + swap_size, ((struct lnode_funcall *) p)->name);
    swap_size += s;
    if (((struct lnode_funcall *) p)->expr) {
      ((struct lnode_funcall *) (swap_area + beg))->expr =
        (struct lnode *) swap_size;
      move_prog_to_swap_area (((struct lnode_funcall *) p)->expr);
    }
    break;
  case L_BLOCK:
    {
      struct lnode_block *lb = (struct lnode_block *) p;
      char *block = lb->block;
      int i, *block_start;

      s = sizeof (struct lnode_block);
      ass_size (s);
      memcpy (swap_area + swap_size, (char *) p, s);
      swap_size += s;
      if (lb->num_nodes) {
        block_start = (int *) (swap_area + swap_size);
        ((struct lnode_block *) (swap_area + beg))->block =
          (char *) swap_size;
        ass_size (lb->num_nodes * sizeof (int));
        swap_size += lb->num_nodes * sizeof (int);
        for (i = 0; i < lb->num_nodes; i++) {
          block_start[i] = swap_size;
          move_prog_to_swap_area ((struct lnode *) block);
          block += lnode_size[((struct lnode *) block)->line >> L_SHIFT];
        }
      }
      break;
    }
  case L_VAR_DEF:
  case L_VARIABLE:
  default:
    fatal ("Bad type in free_sub_part(): 0x%x\n", p->line & L_MASK);
  }
}

void load_ob_from_swap (struct object *ob)
{
  FILE *f;
  char file[250];
  struct stat st;
  char *buffer;
  struct lnode_def *pr;

  sprintf (file, "%s/LPMUDswap%d", SWAP_DIR, ob->swap_num);
  escape_path (file);
  f = fopen (file, "rb");
  if (f == 0) {
    perror (file);
    fatal ("Can't open swap file %s\n", file);
  }
  if (fstat (fileno (f), &st) == -1) {
    perror (file);
    fatal ("Can't fstat swap file %s\n", file);
  }
  buffer = xalloc (st.st_size);
  if (fread (buffer, 1, st.st_size, f) != st.st_size)
    fatal ("Couldn't read the swap file.\n");
  ob->prog = (struct lnode_def *) load_prog_swap_area (buffer, buffer);
  add_prog_ref (ob->prog);
  ob->swapped = 0;
  free (buffer);
  num_swapped--;
  total_bytes_swapped -= st.st_size;
  /*
   * Now we have to restore the heart_beat pointer.
   */
  for (pr = ob->prog; pr; pr = pr->next) {
    if (strcmp (pr->name, "heart_beat") == 0) {
      ob->heart_beat = (struct lnode *) pr;
      break;
    }
  }
}

static struct lnode *load_prog_swap_area (char *base, char *s)
{
  struct lnode *p = (struct lnode *) s;
  struct lnode *new = NULL, *e1, *e2, *e3;

  switch (p->line & L_MASK) {
  case L_SINGLE:
    new = (struct lnode *) alloc_lnode_single (p->type);
    new->line = p->line;
    break;
  case L_NUMBER:
    new = (struct lnode *) alloc_lnode_number (p->type,
      ((struct lnode_number *) p)->number);
    new->line = p->line;
    break;
  case L_NAME:
    new = (struct lnode *) alloc_lnode_name (p->type,
      string_copy ((int) (((struct lnode_name *) p)->name) + base));
    new->line = p->line;
    break;
  case L_1:
    if (((struct lnode_1 *) p)->expr)
      e1 = (struct lnode *) load_prog_swap_area (base, base +
        (int) ((struct lnode_1 *) p)->expr);
    else
      e1 = 0;
    new = (struct lnode *) alloc_lnode_1 (p->type, e1);
    new->line = p->line;
    break;
  case L_2:
    if (((struct lnode_2 *) p)->expr1)
      e1 = load_prog_swap_area (base, base +
        (int) ((struct lnode_2 *) p)->expr1);
    else
      e1 = 0;
    if (((struct lnode_2 *) p)->expr2)
      e2 = load_prog_swap_area (base, base +
        (int) ((struct lnode_2 *) p)->expr2);
    else
      e2 = 0;
    new = (struct lnode *) alloc_lnode_2 (p->type, e1, e2);
    new->line = p->line;
    break;
  case L_3:
    if (((struct lnode_3 *) p)->expr1)
      e1 = load_prog_swap_area (base, base +
        (int) ((struct lnode_3 *) p)->expr1);
    else
      e1 = 0;
    if (((struct lnode_3 *) p)->expr2)
      e2 = load_prog_swap_area (base, base +
        (int) ((struct lnode_3 *) p)->expr2);
    else
      e2 = 0;
    if (((struct lnode_3 *) p)->expr3)
      e3 = load_prog_swap_area (base, base +
        (int) ((struct lnode_3 *) p)->expr3);
    else
      e3 = 0;
    new = (struct lnode *) alloc_lnode_3 (p->type, e1, e2, e3);
    new->line = p->line;
    break;
  case L_DEF:
    {
      struct lnode_def *dp = (struct lnode_def *) p;
      e1 = load_prog_swap_area (base, base + (int) dp->block);
      new = (struct lnode *) alloc_lnode_def (p->type,
        string_copy (base + (int) dp->name), e1, dp->num_var);
      new->line = p->line;
      if (dp->next)
        ((struct lnode_def *) new)->next =
          (struct lnode_def *) load_prog_swap_area (base,
          base + (int) dp->next);
      break;
    }
  case L_FUNCALL:
    if (((struct lnode_funcall *) p)->expr)
      e1 = load_prog_swap_area (base, base +
        (int) ((struct lnode_funcall *) p)->expr);
    else
      e1 = 0;
    new = (struct lnode *) alloc_lnode_funcall (p->type,
      string_copy (base + (int) ((struct lnode_funcall *) p)->name), e1);
    new->line = p->line;
    break;
  case L_BLOCK:
    {
      int size, num, *block_start;
      char *block, *block2;
      struct lnode *l;
      struct lnode_block *lb = (struct lnode_block *) p;

      block_start = (int *) (base + (int) lb->block);
      for (num = 0, size = 0; num < lb->num_nodes; num++) {
        l = load_prog_swap_area (base, base + block_start[num]);
        *((struct lnode **) (base + block_start[num])) = l;
        size += lnode_size[l->line >> L_SHIFT];
      }
      new = (struct lnode *) xalloc (sizeof (struct lnode_block));
      tot_lnode_block++;
      block = xalloc (size);
      ((struct lnode_block *) new)->block = block;
      ((struct lnode_block *) new)->num_nodes = lb->num_nodes;
      new->type = p->type;
      new->line = p->line;
      for (num = 0, block2 = block; num < lb->num_nodes; num++) {
        int size;
        l = (struct lnode *) *(int *) (base + block_start[num]);
        size = lnode_size[l->line >> L_SHIFT];
        memcpy (block2, (char *) l, size);
        block2 += size;
        free_lnode (l, 1);      /* Should the second argument be 1 here? */
      }
      break;
    }
  case L_VAR_DEF:
  case L_VARIABLE:
  default:
    fatal ("Bad type in free_sub_part(): 0x%x\n", p->line & L_MASK);
  }
  return new;
}

void remove_swap_file (struct object *ob)
{
  char file[250];

  if (!ob->swap_num)
    fatal ("Remove non-existing swap file.\n");
  sprintf (file, "%s/LPMUDswap%d", SWAP_DIR, ob->swap_num);
  escape_path (file);
  if (unlink (file) == -1) {
    perror (file);
    debug_message ("Could not unlink swap file %s\n", file);
  }
  ob->swap_num = 0;
}