/
teeny/db/
teeny/dbm/
teeny/docs/
teeny/includes/
teeny/misc/
teeny/news/
teeny/text/
/* disk.c */

#include "copyright.h"
#include "config.h"

#include <stdio.h>
#ifdef STRING_H
#include <string.h>
#else
#include <strings.h>
#endif					/* STRING_H */
#include <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#ifndef NO_UIO_H
#include <sys/uio.h>
#endif					/* NO_UIO_H */

#include "teeny.h"
#include "db.h"

/*
 * Handles basic disk I/O for TeenyMUD. They mess with the descriptors
 * slightly, updating IN_MEMORY status and the where field of the descriptor. 
 *
 */

char           *snarf_str_field();
int            *snarf_lock();
char           *stuff_lock();
char           *stuff_str_field();

int             chunkfd;

long            lseek();	/* Berkeley thinks this is type off_t. Fuck
				 * them. */

/* A work buffer. An on-disk object had better NEVER be bigger than this. */

int             obj_work[MAX_DISK_SIZE / sizeof(int)];

/*
 * Write the object specified by the descriptor out to disk. Will not bother
 * to freeze an object that isn't dirty. 
 *
 */
disk_freeze(d)
  struct dsc     *d;

{
  int            *p;
  char           *q;
  struct obj_data *dat;
  struct dsc     *tmp;
  int             total_size;
  long            offset;
  long            ret;

  if (!ResidentP(d))
  {
    warning("disk_freeze", "attempt to freeze non-resident object");
    return;
  }
  dat = DSC_DATA(d);

  /* Is the data for this thing dirty? If not, patch up its */
  /* descriptor to point at the disk, and free the memory   */

  if (!DirtyP(d))
  {
    offset = dat->offset;
    DSC_CHUNK(d) = offset;
    DSC_FLAGS(d) &= ~IN_MEMORY;
    free_obj(dat);
    return;
  }
  /* Does this thing already own a chunk of disk? */

  if (dat->offset != (long) -1)
  {

    /* Make a descriptor for this chunk & give it back */

    tmp = get_descriptor();
    DSC_CHUNK(tmp) = dat->offset;
    DSC_SIZE(tmp) = dat->chunk_size;
    free_chunk(tmp);
  }
  /* Step one. Write the thing into our workspace. */

  p = obj_work;
  *p++ = dat->pennies;
  *p++ = dat->loc;
  *p++ = dat->contents;
  *p++ = dat->exits;
#ifdef TIMESTAMPS
  *p++ = dat->timestamp;
#endif

  /* Stuff variable length integer arrays. */
  /* NOTE: Order is important. These arrays *will* be correctly   */
  /* aligned, since they fall immediately after a mess of other */
  /* integers. Effectively, we start with a huge array of ints. */

  q = stuff_lock(p, dat->lock);

  /* Stuff variable length strings. Now alignment goes to hell    */
  /* Strings are zero terminated, so we don't care!!!  */

  q = stuff_str_field(q, dat->suc);
  q = stuff_str_field(q, dat->osuc);
  q = stuff_str_field(q, dat->fail);
  q = stuff_str_field(q, dat->ofail);
#ifdef DROP_FIELDS
  q = stuff_str_field(q, dat->drop);
  q = stuff_str_field(q, dat->odrop);
#endif
  q = stuff_str_field(q, dat->desc);
  q = stuff_str_field(q, dat->gender);

  /* Step two. How big is this bugger? Write it out to disk */

  total_size = q - (char *) obj_work;
  if (total_size != DSC_SIZE(d))
  {
    warning("disk_freeze", "computed disk size != stated");
  }
  offset = get_chunk(total_size);

#ifdef DISKDEBUG
  printf("Freeze seeking to offset %ld\n", offset);
#endif

  ret = lseek(chunkfd, (off_t) offset, 0);
  ret = (long) write(chunkfd, (char *) obj_work, total_size);

  if (ret != total_size)
  {
    warning("disk_freeze", "failed write");
  }
  /* Update descriptor */

  DSC_CHUNK(d) = offset;
  DSC_FLAGS(d) &= ~IN_MEMORY;

  /* Free up the memory used. */

  free_obj(dat);
}

free_obj(obj)

  struct obj_data *obj;

{
  ty_free((char *) (obj->lock));

  ty_free(obj->suc);
  ty_free(obj->osuc);
  ty_free(obj->fail);
  ty_free(obj->ofail);
#ifdef DROP_FIELDS
  ty_free(obj->drop);
  ty_free(obj->odrop);
#endif
  ty_free(obj->desc);
  ty_free(obj->gender);

  ty_free((char *) obj);
}

/*
 * Copies one of our internal arrays around. They start with a count of
 * number of ints, and are followed by that many ints. Yow. 
 *
 */

char           *
stuff_lock(dst, src)

  int            *dst, *src;

{
  int             count;

  if (src == NULL)
  {
    *dst++ = -1;		/* Indicate empty lock */
    return ((char *) dst);
  }
  count = *dst++ = *src++;	/* The very first one is how many follow */

  while (count > 0)
  {
    *dst++ = *src++;
    count--;
  }

  return ((char *) dst);
}

/*
 * Really just copies a string around, and returns a pointer to the first
 * byte after the string. 
 *
 */

char           *
stuff_str_field(dst, src)

  char           *dst, *src;

{
  int             len;

  if (src == NULL)
  {
    /* Shove in a zero length string */
    *dst++ = '\0';
    return (dst);
  }
  len = strlen(src);
  strcpy(dst, src);
  return (dst + len + 1);	/* plus one for the \0 */
}

/*
 * Read the object specified by the desriptor in from disk. 
 */
struct obj_data *
disk_thaw(d)
  struct dsc     *d;

{
  struct obj_data *new_obj;
  long            ret;
  int             len;
  long            offset;
  char           *q;
  int            *p;

  if (ResidentP(d))
  {
    warning("disk_thaw", "attempt to re-thaw thawed object");
    return (DSC_DATA(d));
  }
  offset = DSC_CHUNK(d);

#ifdef DISKDEBUG
  printf("Thaw seeking to offset %ld\n", offset);
#endif

  ret = lseek(chunkfd, (off_t) offset, 0);
  ret = (long) read(chunkfd, (char *) obj_work, DSC_SIZE(d));

  if (ret != DSC_SIZE(d))
  {
    warning("disk_thaw", "couldn't thaw chunk");
    return (NULL);
  }
  /* OK. It's in memory. Snarf the data out */

  len = DSC_SIZE(d);
  new_obj = (struct obj_data *) ty_malloc(sizeof(struct obj_data)
					  ,"disk_thaw");
  p = obj_work;

  /* Fix up internal object data fields. */

  new_obj->offset = offset;
  new_obj->chunk_size = DSC_SIZE(d);
  new_obj->descriptor = d;

  new_obj->pennies = *p++;
  new_obj->loc = *p++;
  new_obj->contents = *p++;
  new_obj->exits = *p++;
#ifdef TIMESTAMPS
  new_obj->timestamp = *p++;
#endif

  /* Variable length field integer field. */

  new_obj->lock = snarf_lock(&p);

  q = (char *) p;
  len -= (q - (char *) obj_work);	/* Subtract what we've chopped off */

  /* Variable length string fields. */

  new_obj->suc = snarf_str_field(&q, &len);
  new_obj->osuc = snarf_str_field(&q, &len);
  new_obj->fail = snarf_str_field(&q, &len);
  new_obj->ofail = snarf_str_field(&q, &len);
#ifdef DROP_FIELDS
  new_obj->drop = snarf_str_field(&q, &len);
  new_obj->odrop = snarf_str_field(&q, &len);
#endif
  new_obj->desc = snarf_str_field(&q, &len);
  new_obj->gender = snarf_str_field(&q, &len);

  /* Update the descriptor */

  DSC_DATA(d) = new_obj;
  DSC_FLAGS(d) |= IN_MEMORY;
  DSC_FLAGS(d) &= ~DIRTY;

  return (new_obj);
}

/*
 * Copy an array of our integers into new fresh memory. 
 *
 * src had damn well better be aligned enough. 
 *
 */

int            *
snarf_lock(src)

  int           **src;

{
  int             len;
  int            *p;

  len = *(*src);		/* First thing is how many there are to
				 * follow */
  if (len == -1)
  {				/* No lock, empty */
    (*src)++;
    return (NULL);
  }
  len++;			/* Add one for the length field, of course     */

  p = (int *) ty_malloc((len * sizeof(int)), "snarf_lock");

  /* Ignore returned value, we care not. */

  (void) stuff_lock(p, *src);
  *src += len;
  return (p);
}

/*
 * Grabs a variable length string field out of a buffer. String should be
 * zero terminated. 
 */

char           *
snarf_str_field(src, remaining)

  char          **src;
  int            *remaining;

{
  int             len;
  char           *ret;

  if (*remaining <= 0)
  {
    warning("snarf_str_field", "disk data out of synch");
    return (NULL);
  }
  if (**src == '\0')
  {
    (*src)++;
    (*remaining)--;
    return (NULL);
  }
  len = strlen(*src) + 1;
  ret = ty_malloc(len, "snarf_str_field");
  *remaining -= len;
  strcpy(ret, *src);
  (*src) += len;
  return (ret);
}

/*
 * Open a chunkfile. 
 */

open_chunkfile(name)

  char           *name;

{
  if ((chunkfd = open(name, O_RDWR | O_CREAT, 0755)) == -1)
  {
    fatal("open_chunkfile", "could not open chunk file");
  }
}