/
umud/DOC/
umud/DOC/examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
/*
	Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/

#ifndef	lint
static	char	RCSid[] = "$Header: /usr/users/mjr/hacks/umud/DB/RCS/dbmchunk.c,v 1.7 91/08/18 21:49:38 mjr Exp $";
#endif


/* configure all options BEFORE including system stuff. */
#include	"config.h"

#ifdef	DB_DBMFILE

#ifdef VMS
#include        <stdio.h>
#include        <types.h>
#include        <file.h>
#include        <unixio.h>
#include        "vms_dbm.h"
#else
#include	<stdio.h>
#include	<sys/param.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<sys/file.h>
#ifdef	FCNTL_H
#include	<fcntl.h>
#endif

#ifdef	OLDDBM
#include	<dbm.h>
#include	<sys/errno.h>
extern	int	errno;
#else
#include	<ndbm.h>
#endif
#endif VMS

#include	"mud.h"
#include	"sbuf.h"

/*
#define		DBMCHUNK_DEBUG
*/

/* default block size to use for bitmapped chunks */
#define	DDDB_BLOCK	256


/* bitmap growth increment in BLOCKS not bytes (512 is 64 BYTES) */
#define	DDDB_BITBLOCK	512


#define	LOGICAL_BLOCK(off)	(off/bsiz)
#define	BLOCK_OFFSET(block)	(block*bsiz)
#define	BLOCKS_NEEDED(siz)	(siz % bsiz ? (siz / bsiz) + 1 : (siz / bsiz))



/*
dbm-based object storage routines. Somewhat trickier than the default
directory hash. a free space list is maintained as a bitmap of free
blocks, and an object-pointer list is maintained in a dbm database.
*/
struct	hrec	{
	off_t	off;
	int	siz;
};

static	int dddb_mark();

static	char	*dbfile = DEFAULT_DBMCHUNKFILE;
static	int	bsiz = DDDB_BLOCK;
static	int	db_initted = 0;
static	int	last_free = 0;	/* last known or suspected free block */

#ifdef	OLDDBM
static	int	dbp = 0;
#else
static	DBM	*dbp = (DBM *)0;
#endif

static	FILE	*dbf = (FILE *)0;
static	struct	hrec	hbuf;
static	int	startrav = 0;

static	char	*bitm = (char *)0;
static	int	bitblox = 0;
static	datum	dat;
static	datum	key;

static	void	growbit();

int
dddb_init()
{
	static	char	*copen = "db_init cannot open ";
	char	fnam[MAXPATHLEN];
	struct	stat	sbuf;
	struct	hrec	*hp;

#ifdef	COMPRESS_OIF
	comp_init();
	comp_on(1);
#endif

	/* now open chunk file */
	sprintf(fnam,"%s.db",dbfile);
	if((dbf = fopen(fnam,FOPEN_BINARY_RW)) == (FILE *)0) {
		logf(copen,fnam," ",(char *)-1,"\n",(char *)0);
		return(1);
	}


	/* open hash table */
#ifdef	OLDDBM
	if((dbp = dbminit(dbfile)) < 0) {
		int	fxp;

		/* dbm (old) is stupid about nonexistent files */
		if(errno != ENOENT) {
			logf(copen,dbfile," ",(char *)-1,"\n",(char *)0);
			return(1);
		}

		sprintf(fnam,"%s.dir",dbfile);
		fxp = open(fnam,O_CREAT|O_EXCL|O_WRONLY,0600);
		if(fxp < 0) {
			logf(copen,fnam," ",(char *)-1,"\n",(char *)0);
			return(1);
		}
		(void)close(fxp);

		sprintf(fnam,"%s.pag",dbfile);
		fxp = open(fnam,O_CREAT|O_EXCL|O_WRONLY,0600);
		if(fxp < 0) {
			logf(copen,fnam," ",(char *)-1,"\n",(char *)0);
			return(1);
		}
		(void)close(fxp);

		/* one MORE try */
		if((dbp = dbminit(dbfile)) < 0) {
			logf(copen,dbfile," ",(char *)-1,"\n",(char *)0);
			return(1);
		}
	}
#else
	if((dbp = dbm_open(dbfile,O_RDWR|O_CREAT,0600)) == (DBM *)0) {
		logf(copen,dbfile," ",(char *)-1,"\n",(char *)0);
		return(1);
	}
#endif

	/* determine size of chunk file for allocation bitmap */
	sprintf(fnam,"%s.db",dbfile);
	if(stat(fnam,&sbuf)) {
		logf("db_init cannot stat ",fnam," ",(char *)-1,"\n",(char *)0);
		return(1);
	}

	/* allocate bitmap */
	growbit(LOGICAL_BLOCK(sbuf.st_size) + DDDB_BITBLOCK);


#ifdef	OLDDBM
	key = firstkey();
#else
	key = dbm_firstkey(dbp);
#endif
	while(key.dptr != (char *)0) {
#ifdef	OLDDBM
		dat = fetch(key);
#else
		dat = dbm_fetch(dbp,key);
#endif
		if(dat.dptr == (char *)0) {
			logf("db_init index inconsistent\n",(char *)0);
			return(1);
		}
		bcopy(dat.dptr,&hbuf,sizeof(hbuf));	/* alignment */


		/* mark it as busy in the bitmap */
		dddb_mark(LOGICAL_BLOCK(hbuf.off),hbuf.siz,1);


#ifdef	OLDDBM
		key = nextkey(key);
#else
		key = dbm_nextkey(dbp);
#endif
	}

	db_initted = 1;
	return(0);
}




dddb_initted()
{
	return(db_initted);
}



dddb_setbsiz(nbsiz)
int	nbsiz;
{
	return(bsiz = nbsiz);
}



dddb_setfile(fil)
char	*fil;
{
	char	*xp;

	if(db_initted)
		return(1);

	/* KNOWN memory leak. can't help it. it's small */
	xp = (char *)malloc((unsigned)strlen(fil) + 1);
	if(xp == (char *)0)
		return(1);
	(void)strcpy(xp,fil);
	dbfile = xp;
	return(0);
}



int
dddb_close()
{
	if(dbf != (FILE *)0) {
		fclose(dbf);
		dbf = (FILE *)0;
	}

#ifndef	OLDDBM
	if(dbp != (DBM *)0) {
		dbm_close(dbp);
		dbp = (DBM *)0;
	}
#endif
	if(bitm != (char *)0) {
		free((mall_t)bitm);
		bitm = (char *)0;
		bitblox = 0;
	}
	db_initted = 0;
	return(0);
}




/* grow the bitmap to given size */
static	void
growbit(maxblok)
int	maxblok;
{
	int	nsiz;
	char	*nbit;

	/* round up to eight and then some */
	nsiz = (maxblok + 8) + (8 - (maxblok % 8));

	if(nsiz <= bitblox)
		return;

	/* this done because some old realloc()s are busted */
	nbit = (char *)malloc((unsigned)(nsiz / 8));
	if(bitm != (char *)0) {
		bcopy(bitm,nbit,(bitblox / 8) - 1);
		free((mall_t)bitm);
	}
	bitm = nbit;

	if(bitm == (char *)0)
		fatal("db_init cannot grow bitmap ",(char *)-1,"\n",(char *)0);


	bzero(bitm + (bitblox / 8),((nsiz / 8) - (bitblox / 8)) - 1);
	bitblox = nsiz - 8;
}



static	int
dddb_mark(lbn,siz,taken)
off_t	lbn;
int	siz;
int	taken;
{
	int	bcnt;

	bcnt = BLOCKS_NEEDED(siz);

	/* remember a free block was here */
	if(!taken)
		last_free = lbn;

	while(bcnt--) {
		if(lbn >= bitblox - 32)
			growbit(lbn + DDDB_BITBLOCK);

		if(taken)
			bitm[lbn >> 3] |= (1 << (lbn & 7));
		else
			bitm[lbn >> 3] &= ~(1 << (lbn & 7));
		lbn++;
	}
}





static	int
dddb_alloc(siz)
int	siz;
{
	int	bcnt;	/* # of blocks to operate on */
	int	lbn;	/* logical block offset */
	int	tbcnt;
	int	slbn;
	int	overthetop = 0;

	lbn = last_free;
	bcnt = siz % bsiz ? (siz / bsiz) + 1 : (siz / bsiz);

	while(1) {
		if(lbn >= bitblox - 32) {
			/* only check here. can't break around the top */
			if(!overthetop) {
				lbn = 0;
				overthetop++;
			} else {
				growbit(lbn + DDDB_BITBLOCK);
			}
		}

		slbn = lbn;
		tbcnt = bcnt;

		while(1) {
			if((bitm[lbn >> 3] & (1 << (lbn & 7))) != 0)
				break;

			/* enough free blocks - mark and done */
			if(--tbcnt == 0) {
				for(tbcnt = slbn; bcnt > 0; tbcnt++, bcnt--)
					bitm[tbcnt >> 3] |= (1 << (tbcnt & 7));

				last_free = lbn;
				return(slbn);
			}

			lbn++;
			if(lbn >= bitblox - 32)
				growbit(lbn + DDDB_BITBLOCK);
		}
		lbn++;
	}
}




Obj	*
dddb_get(nam)
char	*nam;
{
	Obj		*ret;

	if(!db_initted)
		return((Obj *)0);

	key.dptr = nam;
	key.dsize = strlen(nam) + 1;
#ifdef	OLDDBM
	dat = fetch(key);
#else
	dat = dbm_fetch(dbp,key);
#endif

	if(dat.dptr == (char *)0)
		return((Obj *)0);
	bcopy(dat.dptr,&hbuf,sizeof(hbuf));

	/* seek to location */
	if(fseek(dbf,(long)hbuf.off,0))
		return((Obj *)0);

	/* if the file is badly formatted, ret == Obj * 0 */
	if((ret = oiffromFILE(dbf,(char *)0)) == (Obj *)0)
		logf("db_get: cannot decode ",nam,"\n",(char *)0);
	return(ret);
}




int
dddb_put(obj,nam)
Obj	*obj;
char	*nam;
{
	int	nsiz;

	if(!db_initted)
		return(1);

	nsiz = oif_objsiz(obj,nam);

	key.dptr = nam;
	key.dsize = strlen(nam) + 1;

#ifdef	OLDDBM
	dat = fetch(key);
#else
	dat = dbm_fetch(dbp,key);
#endif

	if(dat.dptr != (char *)0) {

		bcopy(dat.dptr,&hbuf,sizeof(hbuf));	/* align */

		if(BLOCKS_NEEDED(nsiz) > BLOCKS_NEEDED(hbuf.siz)) {

#ifdef	DBMCHUNK_DEBUG
printf("put: %s old %d < %d - free %d\n",nam,hbuf.siz,nsiz,hbuf.off);
#endif
			/* mark free in bitmap */
			dddb_mark(LOGICAL_BLOCK(hbuf.off),hbuf.siz,0);

			hbuf.off = BLOCK_OFFSET(dddb_alloc(nsiz));
			hbuf.siz = nsiz;
#ifdef	DBMCHUNK_DEBUG
printf("put: %s moved to offset %d, size %d\n",nam,hbuf.off,hbuf.siz);
#endif
		} else {
			hbuf.siz = nsiz;
#ifdef	DBMCHUNK_DEBUG
printf("put: %s replaced within offset %d, size %d\n",nam,hbuf.off,hbuf.siz);
#endif
		}
	} else {
		hbuf.off = BLOCK_OFFSET(dddb_alloc(nsiz));
		hbuf.siz = nsiz;
#ifdef	DBMCHUNK_DEBUG
printf("put: %s (new) at offset %d, size %d\n",nam,hbuf.off,hbuf.siz);
#endif
	}


	/* make table entry */
	dat.dptr = (char *)&hbuf;
	dat.dsize = sizeof(hbuf);

#ifdef	OLDDBM
	if(store(key,dat)) {
		logf("db_put: can't store ",nam," ",(char *)-1,"\n",(char *)0);
		return(1);
	}
#else
	if(dbm_store(dbp,key,dat,DBM_REPLACE)) {
		logf("db_put: can't dbm_store ",nam," ",(char *)-1,"\n",(char *)0);
		return(1);
	}
#endif

#ifdef	DBMCHUNK_DEBUG
printf("%s offset %d size %d\n",nam,hbuf.off,hbuf.siz);
#endif
	if(fseek(dbf,(long)hbuf.off,0)) {
		logf("db_put: can't seek ",nam," ",(char *)-1,"\n",(char *)0);
		return(1);
	}

	if(oiftoFILE(obj,dbf,nam) != 0 || fflush(dbf) != 0) {
		logf("db_put: can't save ",nam," ",(char *)-1,"\n",(char *)0);
		return(1);
	}
	return(0);
}




int
dddb_check(nam)
char	*nam;
{

	if(!db_initted)
		return(0);

	key.dptr = nam;
	key.dsize = strlen(nam) + 1;
#ifdef	OLDDBM
	dat = fetch(key);
#else
	dat = dbm_fetch(dbp,key);
#endif

	if(dat.dptr == (char *)0)
		return(0);
	return(1);
}




int
dddb_del(nam,flg)
char	*nam;
int	flg;
{

	if(!db_initted)
		return(-1);

	key.dptr = nam;
	key.dsize = strlen(nam) + 1;
#ifdef	OLDDBM
	dat = fetch(key);
#else
	dat = dbm_fetch(dbp,key);
#endif


	/* not there? */
	if(dat.dptr == (char *)0)
		return(0);
	bcopy(dat.dptr,&hbuf,sizeof(hbuf));

	/* drop key from db */
#ifdef	OLDDBM
	if(delete(key)) {
#else
	if(dbm_delete(dbp,key)) {
#endif
		logf("db_del: can't delete key ",nam,"\n",(char *)0);
		return(1);
	}

	/* mark free space in bitmap */
	dddb_mark(LOGICAL_BLOCK(hbuf.off),hbuf.siz,0);
#ifdef	DBMCHUNK_DEBUG
printf("del: %s free offset %d, size %d\n",nam,hbuf.off,hbuf.siz);
#endif

	/* mark object dead in file */
	if(fseek(dbf,(long)hbuf.off,0))
		logf("db_del: can't seek ",nam," ",(char *)-1,"\n",(char *)0);
	else {
		fprintf(dbf,"delobj");
		fflush(dbf);
	}

	return(0);
}



int
dddb_travstart()
{
	startrav = 0;
	return(0);
}



int
dddb_traverse(obuf)
char	*obuf;
{
	if(!db_initted)
		return(0);

	if(!startrav) {
#ifdef	OLDDBM
		key = firstkey();
#else
		key = dbm_firstkey(dbp);
#endif
		startrav = 1;
	} else
#ifdef	OLDDBM
		key = nextkey(key);
#else
		key = dbm_nextkey(dbp);
#endif
	if(key.dptr == (char *)0)
		return(0);
	strncpy(obuf,key.dptr,MAXOID);
	return(1);
}



int
dddb_travend()
{
	startrav = 0;
	return(0);
}





int
dddb_backup(out)
char	*out;
{
	FILE	*ou;
	char	vuf[BUFSIZ];
	int	tor;
	char	*xx;

	if(!db_initted)
		return(1);

	if((ou = fopen(out,"w")) == (FILE *)0) {
		logf("db_backup: ",out," ",(char *)-1,"\n",(char *)0);
		return(1);
	}


#ifdef	OLDDBM
	key = firstkey();
#else
	key = dbm_firstkey(dbp);
#endif
	while(key.dptr != (char *)0) {
#ifdef	OLDDBM
		dat = fetch(key);
#else
		dat = dbm_fetch(dbp,key);
#endif
		if(dat.dptr == (char *)0) {
			logf("db_init index inconsistent\n",(char *)0);
			continue;
		}

		bcopy(dat.dptr,&hbuf,sizeof(hbuf));

		if(fseek(dbf,(long)hbuf.off,0))
			logf("db_backup: can't seek ",key.dptr," ",(char *)-1,"\n",(char *)0);
		else {

			/* copy out the object, sans interpretation */
			while(hbuf.siz > 0) {
				if((tor = hbuf.siz) > sizeof(vuf))
					tor = sizeof(vuf);
				if(fread(vuf,sizeof(char),tor,dbf) <= 0) {
					logf("db_backup: can't read ",key.dptr," ",(char *)-1,"\n",(char *)0);
					break;
				}

				/* what are we gonna do if it fails, anyhow? */
				fwrite(vuf,sizeof(char),tor,ou);
				hbuf.siz -= tor;
			}
		}

#ifdef	OLDDBM
		key = nextkey(key);
#else
		key = dbm_nextkey(dbp);
#endif
	}

	fclose(ou);
	return(0);
}

#endif	DB_DBMFILE