/*
	A Berkeley dynahash compatible interface for the UnterMud
database layer as slightly whacked by Andrew Molitor.

	NOTE: This code assumes that keys are all \0 terminated
strings. If they are not, all hell will break loose and
you will rapidly get many fine core dumps. So, always fill in your
key DBTs with 'data' pointing at a \0 terminated string, and size
strlen() of that string. Always.

*/

/* FIRST */

#include "udb_defs.h"

#include	<stdio.h>
#ifdef 	NOSYSTYPES_H
#include	<types.h>
#else
#include	<sys/types.h>
#endif

#include "udb.h"

static int dyna_close();
static int dyna_delete();
static int dyna_get();
static int dyna_put();
static int dyna_seq();
static int dyna_sync();

extern void tmp_sync();
extern void deferfree();

/*
	Open a database. Named this because the dynahash thang is.
We totally ignore most of the parameters. Everything except the
file name, actually.

*/

extern	DB *
hash_open(file, flags, mode, info)
const char	*file;
int	flags;
int	mode;
const HASHINFO	*info;		/* Special directives for create */
{
	DB	*dbp;

	cache_init();

	dddb_setfile(file);	/* Set up the name */
	DB_INIT();		/* Start that mother up */

	/* Get a DB struct */

	if((dbp = (DB *)malloc(sizeof(DB))) == (DB *)0){
		return ((DB *)0);
	}
	/* Fill it in */

	dbp->close = dyna_close;
	dbp->del = dyna_delete;
	dbp->get = dyna_get;
	dbp->put = dyna_put;
	dbp->seq = dyna_seq;
	dbp->sync = dyna_sync;

	return(dbp);
}

/*
	Internal DB access gear. All these do is translate for the
untermud DB layer.

*/

static int
dyna_close(dbp)
DB	*dbp;	/* Unused. */
{
	tmp_sync();	/* Sync out the temp memory allocator..*/
	cache_sync();	/* .. and the cache */
	DB_CLOSE();	/* Close it up. */
	return 0;
}

static int
dyna_delete(dbp,key,flags)
DB	*dbp;	/* Unused. */
DBT	*key;
u_long	flags;	/* Unused. */
{
	if(cache_del(key->data,key->size,0)){
		return(-1);
	}
	return(0);
}

static int
dyna_get(dbp,key,data,flags)
DB	*dbp;	/* Unused. */
DBT	*key;
DBT	*data;
u_long	flags;	/* Unused. */
{
	Attr	*a;

	if((a = cache_get(key->data, key->size)) == (Attr *)0){
		return(-1);
	}
	data->size = a->size;
	data->data = a->data;
	return(0);
}

static int
dyna_put(dbp,key,data,flag)
DB	*dbp;	/* Unused. */
DBT 	*key;
DBT	*data;
u_long	flag;	/* Unused. */
{
	Attr	*a;
	char	*p;
	int	existed = 1;

	/* This is complicated. If the attribute exists, we need a pointer
		to the in-cache Attr. */

	a = cache_get(key->data, key->size);
	if(a == (Attr *)0){
		/* New thing, looks like */
		
		a = (Attr *)malloc(sizeof(Attr));
		if(a == (Attr *)0){
			return(-1);
		}
		existed = 0;
	}
	p = (char *)malloc(data->size);
	if(p == (char *)0){
		return(-1);
	}
	bcopy(data->data,p,data->size);
	if(existed)
		/* free(a->data); */
		deferfree(a->data);
	a->data = p;
	a->size = data->size;

	/* Now stick it into the cache */

	if(cache_put(a,key->data, key->size))
		return(-1);
	return(0);
}

/*
	This is slightly sticky. It walks the *disk* copy of the
database, syncing the cache when you go get the first key. So going in
and fucking with the database while traversing it is probably *real* dumb.

*/

static int
dyna_seq(dbp,key,data,flag)
DB	*dbp;	/* Unused. */
DBT 	*key;
DBT	*data;
u_long flag;
{
	static	char	currkey[MAXKEY+1]; /* For luck */
	Attr	*a;

	if(flag ==  R_FIRST){
		/* Sync out the cache, first */

		cache_sync();
		DB_TRAVSTART();
	}
	if(DB_TRAVERSE(currkey) == 0)
		return(-1);

	if((a = cache_get(currkey, strlen(currkey)+1)) == (Attr *)0)
		return(-1);

	key->size = strlen(currkey);
	key->data = currkey;
	data->size = a->size;
	data->data = a->data;
	return(0);
}

/*
	Write modified data to disk. Return -1 on fail, 0 success.
*/

static int
dyna_sync(dbp)
DB	*dbp;	/* Unused. */
{
	/* This is probably a good time to do these two, as well. */

	tmp_sync();
	cache_reset();
	if(cache_sync())
		return(-1);
	return(0);
}