/
teeny/db/
teeny/dbm/
teeny/doc/
teeny/includes/
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/uio.h>

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

/*
Copyright(C) 1990, Andrew Molitor, All Rights Reserved.
This software may be freely used, modified, and redistributed,
as long as this copyright message is left intact, and this
software is not used to develop any commercial product, or used
in any product that is provided on a pay-for-use basis.

No warranties whatsoever. This is not guaranteed to compile, nor,
in the event that it does compile to binaries, are these binaries
guaranteed to perform any function whatsoever.
*/

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

*/
/*
#define DISKDEBUG
*/

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->home_drpto;
	*p++ = dat->owner;
	*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);
	q = stuff_str_field(q,dat->desc);

	/* 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);
	ty_free(obj->desc);

	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->home_drpto = *p++;
	new_obj->owner = *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);
	new_obj->desc = 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");
	}
}