#include	<sys/types.h>
#include	<sys/file.h>
#include	<sys/stat.h>
#include	<varargs.h>
#include	<stdio.h>
#include	"btconf.h"
#include	"btree.h"
#include	"btintern.h"

/*
         (C) Copyright, 1988, 1989 Marcus J. Ranum
                    All rights reserved


          This software, its documentation,  and  supporting
          files  are  copyrighted  material  and may only be
          distributed in accordance with the terms listed in
          the COPYRIGHT document.

	$Log:	btoopen.c,v $
 * Revision 1.1  90/06/08  16:11:48  mjr
 *  
 * 
 * Revision 1.1  90/06/06  15:03:48  mjr
 *  
 * 
 * Revision 1.1  90/06/03  16:23:13  mjr
 *  
 * 
 * Revision 1.1  90/05/18  23:20:39  mjr
 *  
 * 
 * Revision 1.1  90/05/15  13:18:33  mjr
 * Initial revision
 * 
 * Revision 1.1  90/05/05  15:04:36  mjr
 * Initial revision
 * 
 * Revision 1.1  90/03/23  15:03:47  mjr
 * Initial revision
 * 
*/


#ifndef	lint
static char *rcsid = "$Header: /atreus/mjr/hacks/mud/btlib/RCS/btoopen.c,v 1.1 90/06/08 16:11:48 mjr Exp $";
#endif

/*	this way of handling opening and configuration is probably	*/
/*	unnecessarily large and awkward, but I think it gives a pretty	*/
/*	high degree of #ifdefability to make it portable across OS 	*/
/*	types, as well as giving a lot of flexibility for those who	*/
/*	never trust default options. 					*/

extern	char	*malloc();
extern	off_t	lseek();


BT_INDEX	*
bt_optopen(va_alist)
va_dcl
{
	va_list		ap;
	BT_INDEX	*ret;
	struct bt_cache *cp1;
	struct bt_cache *cp2;
	int		dtyp = BT_DFLTDTYPE;
	int		csiz = BT_DFLTCACHE;
	int		cflg = BT_DFLTCFLAG;
	int		psiz = BT_DFLTPSIZ;
	int		operm = S_IREAD|S_IWRITE;
	int		omode = O_RDWR;
	off_t		magic = BT_DFLTMAGIC;
	bt_chrp		labp = NULL;
	int		labl;
	int		r;
	char		*path = NULL;

	if((ret = (BT_INDEX *)malloc(sizeof(BT_INDEX))) == NULL)
		return(NULL);

	/* clear error */
	bt_errno(ret) = BT_NOERROR;

	/* zero this just in case */
	bt_cmpfn(ret) = 0;

	va_start(ap);
	while((r = va_arg(ap,int)) != 0) {
		switch(r) {
			case	BT_PATH:
				path = va_arg(ap,char *);
				break;

			case	BT_PSIZE:
				psiz = va_arg(ap,int);
				if(psiz < sizeof(struct bt_super))
					psiz = sizeof(struct bt_super);
				break;

			case	BT_CACHE:
				csiz = va_arg(ap,int);
				if(csiz < 0)
					csiz = 0;
				break;

			case	BT_CFLAG:
				cflg = va_arg(ap,int);
				break;

			case	BT_OMODE:
				omode = va_arg(ap,int) | O_RDWR;
				break;

			case	BT_OPERM:
				operm = va_arg(ap,int);
				break;

			case	BT_MAGIC:
				magic = va_arg(ap,off_t);
				break;

			case	BT_LABEL:
				labp = va_arg(ap,bt_chrp);
				labl = va_arg(ap,int);
				break;

			case	BT_DTYPE:
				dtyp = va_arg(ap,int);
				/* next arg BETTER be a valid funcp ! */
				if(dtyp == BT_USRDEF)
					bt_cmpfn(ret) = va_arg(ap,FUNCP);
				break;

			default:
				goto bombout;
		}
	}

	if(path == NULL || (bt_fileno(ret) = open(path,omode,operm)) < 0)
		goto bombout;

	r = read(bt_fileno(ret),(char *)&ret->sblk,sizeof(struct bt_super));

	/* failure to read anything - initialize tree */
	if(r == 0) {
		char	*jnk;
		if((jnk = malloc((unsigned)psiz)) != NULL) {
			ret->sblk.magic = magic;
			bt_pagesiz(ret) = psiz;
			bt_dtype(ret) = dtyp;
			ret->sblk.levs = 1;
			ret->sblk.root = (off_t)psiz;
			ret->sblk.free = BT_NULL;
			ret->sblk.high = (off_t)(2 * psiz);

			/* mark super block as dirty and sync */
			ret->dirt = 1;

			/* write and pretend we read a legit superblock */
			if(bt_wsuper(ret) == BT_OK)
				r = sizeof(struct bt_super);

			/* now make jnk into an empty first page */
			KEYCNT(jnk) = 0;
			KEYLEN(jnk) = 0;
			LSIB(jnk) = RSIB(jnk) = BT_NULL;
			HIPT(jnk) = BT_NULL;

			/* now, since the cache is not set up yet, we */
			/* must directly write the page. */
			if(lseek(bt_fileno(ret),(off_t)psiz,SEEK_SET) != (off_t)psiz ||
				write(bt_fileno(ret),jnk,psiz) != psiz)
				r = 0;
			(void)free(jnk);
		}
	}

	/* yet another sanity check */
	if(r != sizeof(struct bt_super) || ret->sblk.magic != magic)
		goto bombout;

	/* label it if we are supposed to */
	if(labp && bt_wlabel(ret,labp,labl) == BT_ERR)
		goto bombout;

	/* initialize locator stack */
	ret->shih = ret->sblk.levs + 2;
	ret->stack = (struct bt_stack *)malloc((unsigned)(ret->shih * sizeof(struct bt_stack)));
	if(ret->stack == NULL)
		goto bombout;

	/* initialize cache */
	cp2 = ret->lru = NULL;
	csiz += BT_MINCACHE;

	/* just in case some bonehead asks for tons of unused cache */
	if(cflg == BT_NOCACHE)
		csiz = BT_MINCACHE;

	while(csiz--) {
		cp1 = (struct bt_cache *)malloc(sizeof(struct bt_cache)); 
		if(cp1 == NULL)
			goto bombout;

		if(ret->lru == NULL)
			ret->lru = cp1;
		cp1->prev = cp2;
		cp1->num = BT_NULL;
		cp1->next = NULL;
		cp1->flags = BT_CHE_CLEAN;
		if((cp1->p = (bt_chrp)malloc((unsigned)bt_pagesiz(ret))) == NULL)
			goto bombout;
		if(cp2 != NULL)
			cp2->next = cp1;

		cp2 = cp1;
	}
	ret->mru = cp1;

	/* set cache type flag */
	ret->cflg = cflg;

	/* no valid current key */
	ret->cpag = BT_NULL;

	/* all is well */
	return(ret);

bombout:
	(void)bt_close(ret);
	return(NULL);
}