[comp.sources.misc] v03i049: btree library

mjr@welchsun2.UUCP (Marcus J. Ranum) (06/02/88)

comp.sources.misc: Volume 3, Issue 49
Submitted-By: "Marcus J. Ranum" <mjr@welchsun2.UUCP>
Archive-Name: btree

Poor Man's Btree Library

This is a library that maintains a simple balanced btree index. Nothing
more is provided than routines to insert, set, find (specific, next,
and previous), and delete keys. Each key, however, has a spare long
value that can be used to contain an offset to a data file. A library
to handle fixed-length records based on these pointers should be
trivial. (Can you say 'dBASEIII'?) Another failing of this library is
its total inability to cope with having several programs modifying
indices at the same time. (it *CAN*, but I won't vouch for the result)
The good solutions to that particular problem are OS dependent,
unfortunately, and I am not a database guru anyhow.

---mangle------mangle------mangle------mangle------mangle------mangle---
#!/bin/sh
#	This is a shell archive.
#	run the file through sh.
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	Makefile
#	btree.c
#	btree.h
#	btree.3
#	bench.c
#	loadtree.c
#	treedump.c
#	sizes.c
#	example.c
# This archive created: Wed Jun  1 22:09:41 1988
echo shar: extracting README '(3153 characters)'
sed 's/^XX//' << \SHAR_EOF > README
XXPoor Man's Btree Library
XX
XXThis is a library that maintains a simple balanced btree index. Nothing
XXmore is provided than routines to insert, set, find (specific, next,
XXand previous), and delete keys. Each key, however, has a spare long
XXvalue that can be used to contain an offset to a data file. A library
XXto handle fixed-length records based on these pointers should be
XXtrivial. (Can you say 'dBASEIII'?) Another failing of this library is
XXits total inability to cope with having several programs modifying
XXindices at the same time. (it *CAN*, but I won't vouch for the result)
XXThe good solutions to that particular problem are OS dependent,
XXunfortunately, and I am not a database guru anyhow.
XX
XXThe code originally came from a Public Domain package written by Ray
XXSwartz in 1983. I have almost totally re-written it; a proverbial 'no
XXline remains untouched' job that included removal of globals,
XXrearranging most of the data structures, and use of read/write for file
XXI/O instead of stdio.  I also made sure that return values are all
XXchecked properly, so they can be properly ignored in the user code :-)
XXAnother addition is a simple cache, which boosted performance a great
XXdeal. The cache is maintained through a sort of a hash. I had a fairly
XXnifty LRU cache set up, originally, but it turned out to be marginally
XXslower, according to gprof, time, and anything else I could bring to
XXbear on the problem.  If a real database guru takes a look at this,
XXperhaps they can come up with a better way of doing things.
XX
XXSome aspects of this package are a bit primitive - mostly those that
XXdeal with deleting nodes. Deleted nodes are simply flagged as such, and
XXare ignored when searching for data. This effectively means that the
XXindex will continue to grow. Fixing this is left as an exercise,
XXetc...  :-)  (actually, keeping the stuff around has its uses, too.)
XXThere is a hook in the super block structure designed to allow a
XX'free-list' to be implemented, but no hooks in the btdelete() code for
XXjoining nodes, etc, etc.
XX
XXThe original code was placed in the public domain. This is not, since I
XXwish to retain some control over it. No restrictions are placed on
XXmodification, or use of this code, as long as the copyrights are not
XXremoved.  There are some areas that really could use improving (like
XXhandling free nodes better) and I hope that if someone makes
XXimprovements they will keep me up to date on them (e-mail please, I am
XXnot on usenet).
XX
XXByte-order and portability: There are #ifdefs for BYTEORDER, which make
XXthe program store data in network byte order (at least the data
XXstructures that drive the library - user data is the user's problem.)
XXThere are several unsolved problems with this approach. It works fine
XXbetween my Sun and my VAX, but the way the structures are written to
XXdisk is also going to depend on your compiler.  The BYTEORDER code is
XXnot guaranteed to work, and if it doesn't, your best bet is to look at
XXthe output of sizes.c and to check to see if your compiler assembles
XXthe structures in the same ORDER.
XX
XX	Marcus J. Ranum, William Welch Medical Library, 1988
XX	mjr@jhuigf.BITNET || uunet!mimsy!aplcen!osiris!welchvax!mjr
SHAR_EOF
if test 3153 -ne "`wc -c README`"
then
echo shar: error transmitting README '(should have been 3153 characters)'
fi
echo shar: extracting Makefile '(1880 characters)'
sed 's/^XX//' << \SHAR_EOF > Makefile
XX# Makefile for btree library 
XX# Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX# 
XX# 
XX# $Header: Makefile,v 1.1 88/06/01 21:36:05 mjr Rel $: Makefile 
XX# 
XX# $Log:	Makefile,v $
XX# Revision 1.1  88/06/01  21:36:05  mjr
XX# Initial revision
XX# 
XX# 
XX# 
XX
XX# define SYSV or BSD (only for purposes of test code) - btree.c doesn't care.
XX# 
XX# define BYTEORDER to store the
XX# data files in network byte order. On systems that are already in the
XX# right byteorder, defining this will waste a few cycles here and there,
XX# but the slowdown is I/O anyway.
XXCFLAGS=-O -DBSD -DBYTEORDER
XXLFLAGS=-s
XXLINTFLAGS=-h -x -u -DBSD -DBYTEORDER
XX
XXLIB=	libbtree.a 
XXLIBDIR=	/usr/local/lib
XXHDR=	btree.h
XXHDRDIR=	/usr/include/local
XXMAN=	btree.3
XXMANDIR=	/usr/man/manl
XX
XXall:	example loadtree treedump bench sizes
XX
XX$(LIB):	btree.o
XX	ar rc $(LIB) btree.o
XX	ranlib $(LIB)
XX
XXexample:	$(LIB) example.o
XX	cc $(LFLAGS) -o example example.o $(LIB)
XX
XXloadtree:	$(LIB) loadtree.o
XX	cc $(LFLAGS) -o loadtree loadtree.o $(LIB)
XX
XXtreedump:	$(LIB) treedump.o
XX	cc $(LFLAGS) -o treedump treedump.o $(LIB)
XX
XXbench:		$(LIB) bench.o
XX	cc $(LFLAGS) -o bench bench.o $(LIB)
XX
XXsizes:		sizes.o
XX	cc $(LFLAGS) -o sizes sizes.o
XX	@sizes
XX
XXbtree.o:	$(HDR) Makefile
XXexample.o:	$(HDR) Makefile
XXloadtree.o:	$(HDR) Makefile
XXtreedump.o:	$(HDR) Makefile
XXbench.o:	$(HDR) Makefile
XXsizes.o:	$(HDR) Makefile
XX
XXinstall:	$(LIB) $(MAN)
XX	cp $(LIB) $(LIBDIR)/$(LIB)
XX	chmod 644 $(LIBDIR)/$(LIB)
XX	cp $(HDR) $(HDRDIR)/$(HDR)
XX	chmod 644 $(HDRDIR)/$(HDR)
XX	ranlib $(LIBDIR)/$(LIB)
XX	cp $(MAN) $(MANDIR)/$(MAN)
XX	chmod 644 $(MANDIR)/$(MAN)
XX
XXclean:
XX	rm -f $(LIB) *.o example bench loadtree treedump core mon.out \
XX		sizes llib-lbtree.ln btr.shar
XX
XXlint:
XX	lint $(LINTFLAGS) btree.c
XX
XXdiction:
XX	style $(MAN)
XX	diction $(MAN)
XX
XXlintlib:
XX	lint -Cbtree btree.c
XX
XXshar:
XX	shar -a README Makefile btree.c $(HDR) $(MAN) bench.c \
XX		loadtree.c treedump.c sizes.c example.c > btr.shar
SHAR_EOF
if test 1880 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 1880 characters)'
fi
echo shar: extracting btree.c '(17883 characters)'
sed 's/^XX//' << \SHAR_EOF > btree.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: btree.c,v 1.1 88/06/01 21:35:07 mjr Rel $: btree.c";
XX#endif
XX
XX/*
XX * $Log:	btree.c,v $
XX * Revision 1.1  88/06/01  21:35:07  mjr
XX * Initial revision
XX * 
XX */
XX
XX#include	<stdio.h>
XX#include	"btree.h"
XX
XX/* if we wish to store our disk data in network byte order */
XX#ifdef BYTEORDER
XX#include	<sys/types.h>
XX#include	<netinet/in.h>
XX#endif
XX
XX#define	BT_NSIZ	(sizeof(struct btnode))
XX#define	BT_SSIZ	(sizeof(struct btsuper))
XX
XX
XX/* the errno for btree problems. we use negative # - */
XX/* so btperror can use the real UNIX errno */
XXint	bterrno = 0;
XXchar	*bterrs[] = {
XX	"No btree error",
XX	"Bad index magic number",
XX	"History stack overflow",
XX	"Cannot delete node zero",
XX	0
XX};
XX
XX#ifdef vax
XXextern	void	bcopy();
XXextern	void	free();
XXextern	void	exit();
XXextern	void	perror();
XX#endif
XX
XX
XX/* write the btree superblock to disk */
XXstatic int
XXwsuper(bt)
XXstruct	btree	*bt;
XX{
XX#ifdef BYTEORDER
XX	struct	btsuper	boge;
XX#endif
XX	extern	long	lseek();
XX
XX	if (lseek(bt->fd, 0L, 0) < 0)
XX		return (-1);
XX
XX#ifdef BYTEORDER
XX	boge.magic = htonl(bt->sblk.magic);
XX	boge.free = htonl(bt->sblk.free);
XX	boge.root = htonl(bt->sblk.root);
XX	boge.list = htonl(bt->sblk.list);
XX
XX	if (write(bt->fd, (char *) &boge, BT_SSIZ) != BT_SSIZ)
XX		return (-1);
XX#else
XX	if (write(bt->fd, (char *) &bt->sblk, BT_SSIZ) != BT_SSIZ)
XX		return (-1);
XX#endif
XX
XX	return (0);
XX}
XX
XX
XX
XX/* dynamically allocate a control structure for an open btree */
XXstruct btree	*
XXbtopen(path, flags, mode)
XXchar	*path;
XXint	flags;
XXint	mode;
XX{
XX	struct	btree	*bt;
XX	int	r;
XX	extern	char	*malloc();
XX
XX	/* lets be dynamic, shall we ? */
XX#ifndef lint
XX	/* this to avoid the possible pointer alignment lint message */
XX	if ((bt = (struct btree *) malloc(sizeof(struct btree))) == NULL)
XX		return (NULL);
XX#else
XX	bt = (struct btree *)0;
XX#endif
XX
XX	if ((bt->fd = open(path, flags, mode)) > -1) {
XX
XX		r = read(bt->fd, (char *) &bt->sblk, BT_SSIZ);
XX
XX		/* if read nothing, must be a new guy, right ? */
XX		if (r == 0) {
XX			bt->sblk.magic = BT_MAGIC;
XX			bt->sblk.free = 1L;
XX			bt->sblk.root = 0L;
XX			bt->sblk.list = 0L;
XX
XX			if (wsuper(bt) == 0)
XX				r = BT_SSIZ;
XX		}
XX#ifdef BYTEORDER
XX		else {
XX			/* read something, decode the numbers */
XX			bt->sblk.magic = ntohl(bt->sblk.magic);
XX			bt->sblk.free = ntohl(bt->sblk.free);
XX			bt->sblk.root = ntohl(bt->sblk.root);
XX			bt->sblk.list = ntohl(bt->sblk.list);
XX		}
XX#endif
XX
XX		/* cleverly check ret value from either read or write */
XX		if (r != BT_SSIZ) {
XX			(void) close(bt->fd);
XX			(void) free((char *) bt);
XX			return (NULL);
XX		}
XX
XX		/* check that ole magic number */
XX		if (bt->sblk.magic != BT_MAGIC) {
XX			bterrno = BT_BAD_MAGIC;
XX			(void) close(bt->fd);
XX			(void) free((char *) bt);
XX			return (NULL);
XX		}
XX	} else {
XX		/* couldnt even open the bloody file */
XX		(void) free((char *) bt);
XX		return (NULL);
XX	}
XX
XX	/* zero the cache - record numbers will never be -1, */
XX	/* so the cache will load as activity takes place */
XX	for(r = 0; r < BT_CSIZ; r++)
XX		bt->cache[r].recno = -1L;
XX
XX	return (bt);
XX}
XX
XX
XX
XX/* close and deallocate the control structure */
XXbtclose(bt)
XXstruct	btree	*bt;
XX{
XX	int	t;
XX
XX	t = wsuper(bt);
XX	(void) close(bt->fd);
XX	(void) free((char *) bt);
XX	return (t);
XX}
XX
XX
XX
XX/* write a node to disk */
XXstatic	int
XXwnode(nbr, npt, bt)
XXlong	nbr;
XXstruct	btnode	*npt;
XXstruct	btree	*bt;
XX{
XX	extern	long	lseek();
XX	extern	char	*strncpy();
XX#ifdef	BYTEORDER
XX	struct	btnode	boge;
XX#endif
XX
XX	if (lseek(bt->fd, (long) ((nbr * BT_NSIZ) + BT_SSIZ), 0) == -1)
XX		return (-1);
XX
XX
XX#ifdef	BYTEORDER
XX	boge.recno = htonl(npt->recno);
XX	boge.lptr = htonl(npt->lptr);
XX	boge.rptr = htonl(npt->rptr);
XX	boge.deleted = htons(npt->deleted);
XX	boge.balance = htons(npt->balance);
XX	(void) strncpy(boge.key, npt->key, BT_KSIZ - 1);
XX	if (write(bt->fd, (char *) &boge, BT_NSIZ) != BT_NSIZ)
XX		return (-1);
XX#else
XX	if (write(bt->fd, (char *) npt, BT_NSIZ) != BT_NSIZ)
XX		return (-1);
XX#endif
XX
XX	/* update cache -  if the write succeeded */
XX	(void)bcopy((char *)npt,(char *)&bt->cache[(int)nbr % BT_CSIZ],BT_NSIZ);
XX	return (0);
XX}
XX
XX
XX
XX/* read a node from disk */
XXstatic	int
XXrnode(nbr, npt, bt)
XXlong	nbr;
XXstruct	btnode	*npt;
XXstruct	btree	*bt;
XX{
XX	extern	long	lseek();
XX	extern	char	*strncpy();
XX	int	hash;
XX#ifdef	BYTEORDER
XX	struct	btnode	boge;
XX#endif
XX	hash = (int)nbr % BT_CSIZ;
XX
XX	/* check for cache hit - simple hash - braindead, really */
XX	if(bt->cache[hash].recno != nbr) {
XX
XX		/* if no cache hit, load from disk */
XX		if (lseek(bt->fd, (long) ((nbr * BT_NSIZ) + BT_SSIZ), 0) == -1)
XX			return (-1);
XX#ifndef BYTEORDER
XX		if (read(bt->fd, (char *) &bt->cache[hash], BT_NSIZ) != BT_NSIZ)
XX			return (-1);
XX#else
XX		if (read(bt->fd, (char *) &boge, BT_NSIZ) != BT_NSIZ)
XX			return (-1);
XX
XX		bt->cache[hash].recno = ntohl(boge.recno);
XX		bt->cache[hash].lptr = ntohl(boge.lptr);
XX		bt->cache[hash].rptr = ntohl(boge.rptr);
XX		bt->cache[hash].deleted = ntohs(boge.deleted);
XX		bt->cache[hash].balance = ntohs(boge.balance);
XX		(void) strncpy(bt->cache[hash].key, boge.key, BT_KSIZ - 1);
XX#endif
XX	}
XX
XX	/* loaded OK, now copy the updated cached data to the target */
XX	(void)bcopy((char *) &bt->cache[hash],(char *)npt,BT_NSIZ);
XX
XX	return (0);
XX}
XX
XX
XX
XXstatic	int
XXpushl(bt, nbr)
XXstruct	btree	*bt;
XXlong	nbr;
XX{
XX	if (++(bt->lstak.sptr) >= STACK_LENGTH) {
XX		bterrno = BT_BAD_STACK;
XX		exit(0);
XX	}
XX	bt->lstak.ele[bt->lstak.sptr] = nbr;
XX	bt->lstak.lev[bt->lstak.sptr] = ++bt->slev;
XX	return;
XX}
XX
XX
XXstatic	int
XXpushr(bt, nbr)
XXstruct	btree	*bt;
XXlong	nbr;
XX{
XX	if (++(bt->rstak.sptr) >= STACK_LENGTH) {
XX		bterrno = BT_BAD_STACK;
XX		exit(0);
XX	}
XX	bt->rstak.ele[bt->rstak.sptr] = nbr;
XX	bt->rstak.lev[bt->rstak.sptr] = ++bt->slev;
XX	return;
XX}
XX
XX
XX
XXstatic	long
XXpopr(bt)
XXstruct	btree	*bt;
XX{
XX
XX	bt->slev = bt->rstak.lev[bt->rstak.sptr];
XX
XX	while (bt->lstak.lev[bt->lstak.sptr] > bt->slev)
XX		(bt->lstak.sptr)--;
XX
XX	if (bt->rstak.sptr == 0)
XX		return (0);
XX
XX	bt->slev--;
XX	return (bt->rstak.ele[(bt->rstak.sptr)--]);
XX}
XX
XX
XX
XXstatic	long
XXpopl(bt)
XXstruct	btree	*bt;
XX{
XX
XX	bt->slev = bt->lstak.lev[bt->lstak.sptr];
XX
XX	while (bt->rstak.lev[bt->rstak.sptr] > bt->slev)
XX		(bt->rstak.sptr)--;
XX
XX	if (bt->lstak.sptr == 0)
XX		return (0);
XX
XX	bt->slev--;
XX	return (bt->lstak.ele[(bt->lstak.sptr)--]);
XX}
XX
XX
XX
XXstatic	void
XXbt_link(alpha1, node1, alpha2, node2)
XXint	alpha1;
XXint	alpha2;
XXstruct	btnode	*node1;
XXstruct	btnode	*node2;
XX{
XX	if (alpha1 == -1 && alpha2 == -1)
XX		node1->lptr = node2->lptr;
XX	else if (alpha1 == -1 && alpha2 == 1)
XX		node1->lptr = node2->rptr;
XX	else if (alpha1 == 1 && alpha2 == -1)
XX		node1->rptr = node2->lptr;
XX	else
XX		node1->rptr = node2->rptr;
XX}
XX
XX
XX
XX/* number a link according to alpha */
XXstatic	void
XXnbr_link(nbr, alpha, node1)
XXlong		*nbr;
XXint		alpha;
XXstruct	btnode	*node1;
XX{
XX	*nbr = (alpha == 1) ? node1->rptr : node1->lptr;
XX}
XX
XX
XX
XX/* set a link according to alpha */
XXstatic	void
XXlink_nbr(alpha, node1, nbr)
XXint		alpha;
XXstruct	btnode	*node1;
XXlong		nbr;
XX{
XX
XX	if (alpha == 1)
XX		node1->rptr = nbr;
XX	else
XX		node1->lptr = nbr;
XX}
XX
XX
XX
XXstatic	void
XXnode_bal(alpha, node1, node2, node3)
XXint	alpha;
XXstruct	btnode	*node1;
XXstruct	btnode	*node2;
XXstruct	btnode	*node3;
XX{
XX
XX	if (node1->balance == alpha) {
XX		node2->balance = -alpha;
XX		node3->balance = 0;
XX	} else if (node1->balance == 0)
XX		node2->balance = node3->balance = 0;
XX	else {
XX		node2->balance = 0;
XX		node3->balance = alpha;
XX	}
XX}
XX
XX
XX
XX/* change the record number in a node */
XXbtsetrec(nbr, newrec, bt)
XXlong	nbr;
XXlong	newrec;
XXstruct	btree	*bt;
XX{
XX	struct	btnode	tmpnode;
XX
XX	if(rnode(nbr, &tmpnode, bt) <0)
XX		return(-1);
XX
XX	tmpnode.recno = newrec;
XX
XX	if(wnode(nbr, &tmpnode, bt) <0)
XX		return(-1);
XX
XX
XX	return(0);
XX}
XX
XX
XX
XX/* insert the node into the tree */
XXbtinsert(argkey, recnbr, bt)
XXchar	*argkey;
XXlong	recnbr;
XXstruct	btree	*bt;
XX{
XX	long	top;
XX	long	p_rec;
XX	long	s_rec;
XX	long	q_rec;
XX	long	r_rec;
XX	struct	btnode	q_node;
XX	struct	btnode	s_node;
XX	struct	btnode	r_node;
XX	struct	btnode	p_node;
XX	int	compare;
XX	extern	char	*strncpy();
XX
XX
XX	/* the very first node gets inserted specially */
XX	if (bt->sblk.root == 0) {
XX		bt->sblk.root = 1;
XX		p_node.balance = p_node.rptr = p_node.lptr = 0;
XX		p_node.deleted = BT_ACTIVE;
XX		p_node.recno = recnbr;
XX
XX		(void) strncpy(p_node.key, argkey, BT_KSIZ - 1);
XX		p_node.key[BT_KSIZ - 1] = '\0';
XX		if(wnode(1L, &p_node, bt) < 0)
XX			return(-1);
XX
XX		bt->sblk.free++;
XX		bt->sblk.list++;
XX		if(wsuper(bt) <0)
XX			return(-1);
XX		return (0);
XX	}
XX	top = -1;
XX	p_rec = bt->sblk.root;
XX	s_rec = bt->sblk.root;
XX	while (1) {
XX		if(rnode(p_rec, &p_node, bt) <0)
XX			return(-1);
XX		if ((compare = strcmp(argkey, p_node.key)) < 0) {
XX
XX			/* (move left) */
XX			q_rec = p_node.lptr;
XX
XX			if (q_rec == 0) {
XX				/* insert here */
XX				q_rec = bt->sblk.free++;
XX				p_node.lptr = q_rec;
XX				break;	/* loop exit */
XX			} else {
XX				/* look again from this node */
XX				if(rnode(q_rec, &q_node, bt) <0)
XX					return(-1);
XX				if (q_node.balance != 0) {
XX					top = p_rec;
XX					s_rec = q_rec;
XX				}
XX			}
XX			p_rec = q_rec;
XX
XX		} else {
XX			/* (move right) */
XX			q_rec = p_node.rptr;
XX
XX			if (q_rec == 0) {
XX				/* insert here */
XX				q_rec = bt->sblk.free++;
XX				p_node.rptr = q_rec;
XX				break;	/* loop exit */
XX			} else {
XX				/* look again from this node */
XX				if(rnode(q_rec, &q_node, bt) <0)
XX					return(-1);
XX				if (q_node.balance != 0) {
XX					top = p_rec;
XX					s_rec = q_rec;
XX				}
XX				p_rec = q_rec;
XX			}
XX		}
XX	}
XX
XX	/* Step 5 (insert key at q_node) */
XX	q_node.lptr = q_node.rptr = 0;
XX	q_node.balance = 0;
XX	q_node.deleted = BT_ACTIVE;
XX	q_node.recno = recnbr;
XX	(void) strncpy(q_node.key, argkey, BT_KSIZ);
XX	q_node.key[BT_KSIZ - 1] = '\0';
XX
XX	if (wnode(q_rec, &q_node, bt) == -1)
XX		return (-1);
XX
XX	if(wnode(p_rec, &p_node, bt) <0)
XX		return(-1);
XX	if(rnode(s_rec, &s_node, bt) <0)
XX		return(-1);
XX	if(wsuper(bt) <0)
XX		return(-1);
XX
XX	/* (adjust balance factors) */
XX	if (strcmp(argkey, s_node.key) < 0) {
XX		r_rec = p_rec = s_node.lptr;
XX	} else {
XX		r_rec = p_rec = s_node.rptr;
XX	}
XX
XX	while (p_rec != q_rec) {
XX		if(rnode(p_rec, &p_node, bt) <0)
XX			return(-1);
XX		if (strcmp(argkey, p_node.key) < 0) {
XX			p_node.balance = -1;
XX			if(wnode(p_rec, &p_node, bt) < 0)
XX				return(-1);
XX			p_rec = p_node.lptr;
XX		} else {
XX			p_node.balance = 1;
XX			if(wnode(p_rec, &p_node, bt) < 0)
XX				return(-1);
XX			p_rec = p_node.rptr;
XX		}
XX	}
XX
XX	compare = (strcmp(argkey, s_node.key) < 0) ? -1 : 1;
XX	if (s_node.balance == 0) {
XX		bt->sblk.list++;
XX		s_node.balance = compare;
XX		if(wnode(s_rec, &s_node, bt) < 0)
XX			return(-1);
XX		if(wsuper(bt) <0)
XX			return(-1);
XX		return (0);
XX	} else if (s_node.balance == -compare) {
XX		bt->sblk.list++;
XX		s_node.balance = 0;
XX		if(wnode(s_rec, &s_node, bt) < 0)
XX			return(-1);
XX		if(wsuper(bt) <0)
XX			return(-1);
XX		return (0);
XX	} else {
XX		/* (tree out of balance) */
XX		bt->sblk.list++;
XX		if(rnode(s_rec, &s_node, bt) <0)
XX			return(-1);
XX		if(rnode(r_rec, &r_node, bt) <0)
XX			return(-1);
XX		if (r_node.balance == compare) {
XX			/* (single rotate) */
XX			p_rec = r_rec;
XX			bt_link(compare, &s_node, -compare, &r_node);
XX			link_nbr(-compare, &r_node, s_rec);
XX			s_node.balance = r_node.balance = 0;
XX		} else {
XX			/* (double rotate) */
XX			nbr_link(&p_rec, -compare, &r_node);
XX			if(rnode(p_rec, &p_node, bt) <0)
XX				return(-1);
XX			bt_link(-compare, &r_node, compare, &p_node);
XX			link_nbr(compare, &p_node, r_rec);
XX			bt_link(compare, &s_node, -compare, &p_node);
XX			link_nbr(-compare, &p_node, s_rec);
XX			node_bal(compare, &p_node, &s_node, &r_node);
XX			p_node.balance = 0;
XX			if(wnode(p_rec, &p_node, bt) <0)
XX				return(-1);
XX		}
XX
XX		if (top == -1) {
XX			bt->sblk.root = p_rec;
XX		} else {
XX			/* balanced at top of a sub-tree */
XX			if(rnode(top, &q_node, bt) < 0)
XX				return(-1);
XX			if (s_rec == q_node.rptr)
XX				q_node.rptr = p_rec;
XX			else
XX				q_node.lptr = p_rec;
XX			if(wnode(top, &q_node, bt) <0)
XX				return(-1);
XX		}
XX		if(wnode(s_rec, &s_node, bt) <0)
XX			return(-1);
XX		if(wnode(r_rec, &r_node, bt) <0)
XX			return(-1);
XX		if(wsuper(bt) <0)
XX			return(-1);
XX		return (0);
XX	}
XX}
XX
XX
XX
XX/* drop a node */
XXbtdelete(node_nbr, bt)
XXlong	node_nbr;
XXstruct	btree	*bt;
XX{
XX	struct	btnode	cno;
XX
XX	if (node_nbr == 0) {
XX		bterrno = BT_BAD_ROOT;
XX		return (-1);
XX	} else {
XX		if (rnode(node_nbr, &cno, bt) == -1) 
XX			return(-1);
XX		cno.deleted = BT_DELETED;
XX		if (wnode(node_nbr, &cno, bt) == -1) {
XX			return (-1);
XX		} else {
XX			bt->sblk.list--;
XX			if(wsuper(bt) <0)
XX				return(-1);
XX		}
XX	}
XX	return (0);
XX}
XX
XX
XX
XX/* find the next node */
XXbtnext(node_nbr, cno, bt)
XXlong	*node_nbr;
XXstruct	btnode	*cno;
XXstruct	btree	*bt;
XX{
XX	long	popl();
XX	long	popr();
XX	long	s_nod;
XX
XX	s_nod = *node_nbr;
XX
XX	if (*node_nbr == 0) {
XX		/* undefined current node - wind to beginning of file */
XX		return (bthead(node_nbr,cno,bt));
XX	}
XX	do {
XX		if (cno->rptr == 0) {
XX			/* can't move right */
XX			if (bt->lstak.sptr == 0) {
XX				/* none in stack */
XX				if(rnode(*node_nbr, cno, bt) <0)
XX					return(-1);
XX				return (BT_EOF);
XX			} else {
XX				/* can't go right & stack full (pop stack) */
XX				s_nod = popl(bt);
XX				if(rnode(s_nod, cno, bt) < 0)
XX					return(-1);
XX			}
XX		} else {
XX			/* move right */
XX			pushr(bt, s_nod);
XX			s_nod = cno->rptr;
XX			if(rnode(s_nod, cno, bt) <0 )
XX				return(-1);
XX
XX			while (cno->lptr != 0) {
XX				/* bottom left */
XX				pushl(bt, s_nod);
XX				/* of this sub-tree */
XX				s_nod = cno->lptr;
XX				if(rnode(s_nod, cno, bt) <0)
XX					return(-1);
XX			}
XX		}
XX	} while (cno->deleted == BT_DELETED);
XX
XX	*node_nbr = s_nod;
XX	return (0);
XX}
XX
XX
XX
XX/* go to the tail of the file */
XXbttail(node_nbr, cno, bt)
XXstruct	btree	*bt;
XXlong	*node_nbr;
XXstruct	btnode	*cno;
XX{
XX	long	s_nod;
XX
XX	bt->rstak.sptr = 0;
XX	bt->lstak.sptr = 0;
XX	bt->rstak.ele[0] = 0;
XX	bt->lstak.ele[0] = 0;
XX
XX	/* begin at list head */
XX	s_nod = bt->sblk.root;
XX
XX	if(rnode(s_nod, cno, bt) <0)
XX		return(-1);
XX	while (1) {
XX		/* search to right */
XX		if (cno->rptr != 0) {
XX			pushr(bt, s_nod);
XX			s_nod = cno->rptr;
XX			if(rnode(s_nod, cno, bt) <0)
XX				return(-1);
XX		} else {
XX			if (cno->deleted == BT_DELETED) {
XX				/* skip all deleted nodes */
XX				while (cno->deleted == BT_DELETED) {
XX					if (btnext(&s_nod, cno, bt) == BT_EOF) {
XX						if(btprevious(&s_nod, cno, bt)<0)
XX							return(-1);
XX						*node_nbr = s_nod;
XX						return (0);
XX					}
XX				}
XX				*node_nbr = s_nod;
XX				return (0);
XX			} else {
XX				/* at end of a branch */
XX				*node_nbr = s_nod;
XX				return (0);
XX			}
XX		}
XX	}
XX}
XX
XX
XX/* go to the head of the file */
XXbthead(node_nbr, cno, bt)
XXstruct	btree	*bt;
XXlong	*node_nbr;
XXstruct	btnode	*cno;
XX{
XX	long	s_nod;
XX
XX	bt->rstak.sptr = 0;
XX	bt->lstak.sptr = 0;
XX	bt->rstak.ele[0] = 0;
XX	bt->lstak.ele[0] = 0;
XX
XX	/* begin at list head */
XX	s_nod = bt->sblk.root;
XX
XX	if(rnode(s_nod, cno, bt) <0)
XX		return(-1);
XX	while (1) {
XX		/* search to left */
XX		if (cno->lptr != 0) {
XX			pushl(bt, s_nod);
XX			s_nod = cno->lptr;
XX			if(rnode(s_nod, cno, bt) <0)
XX				return(-1);
XX		} else {
XX			if (cno->deleted == BT_DELETED) {
XX				/* skip all deleted nodes */
XX				while (cno->deleted == BT_DELETED) {
XX					if (btprevious(&s_nod, cno, bt) == BT_EOF) {
XX						if(btnext(&s_nod, cno, bt)<0)
XX							return(-1);
XX						*node_nbr = s_nod;
XX						return (0);
XX					}
XX				}
XX				*node_nbr = s_nod;
XX				return (0);
XX			} else {
XX				/* at end of a branch */
XX				*node_nbr = s_nod;
XX				return (0);
XX			}
XX		}
XX	}
XX}
XX
XX
XX
XX/* find a key */
XXbtfind(key1, node_nbr, cno, bt)
XXchar	*key1;
XXstruct	btree	*bt;
XXlong	*node_nbr;
XXstruct	btnode	*cno;
XX{
XX	int	direction;
XX	long	s_nod;
XX
XX	bt->rstak.sptr = 0;
XX	bt->lstak.sptr = 0;
XX	bt->rstak.ele[0] = 0;
XX	bt->lstak.ele[0] = 0;
XX
XX	bt->slev = 0;		/* tree level at start of search */
XX
XX	/* begin at list head */
XX	s_nod = bt->sblk.root;
XX
XX	if(rnode(s_nod, cno, bt) <0)
XX		return(-1);
XX	while ((direction = strcmp(key1, cno->key)) != 0 ||
XX		cno->deleted == BT_DELETED) {
XX
XX		if (direction > 0) {
XX			/* search to right */
XX			if (cno->rptr != 0) {
XX				pushr(bt, s_nod);
XX				s_nod = cno->rptr;
XX				if(rnode(s_nod, cno, bt) <0)
XX					return(-1);
XX			} else if (cno->deleted == BT_DELETED) {
XX				/* skip all deleted nodes */
XX				while (cno->deleted == BT_DELETED) {
XX					if (btnext(&s_nod, cno, bt) == BT_EOF) {
XX						if(btprevious(&s_nod, cno, bt)<0)
XX							return(-1);
XX						*node_nbr = s_nod;
XX						return (BT_NREC);
XX					}
XX				}
XX				*node_nbr = s_nod;
XX				return (BT_NREC);
XX			} else {
XX				/* at end of a branch */
XX				*node_nbr = s_nod;
XX				return (BT_NREC);
XX			}
XX		} else {
XX			/* search to left */
XX			if (cno->lptr != 0) {
XX				pushl(bt, s_nod);
XX				s_nod = cno->lptr;
XX				if(rnode(s_nod, cno, bt) <0)
XX					return(-1);
XX			} else if (cno->deleted == BT_DELETED) {
XX				while (cno->deleted == BT_DELETED) {
XX					if (btnext(&s_nod, cno, bt) == BT_EOF) {
XX						if(btprevious(&s_nod, cno, bt)< 0)
XX							return(-1);
XX						*node_nbr = s_nod;
XX						return (BT_NREC);
XX					}
XX				}
XX				*node_nbr = s_nod;
XX				return (BT_NREC);
XX			} else {
XX				*node_nbr = s_nod;
XX				return (BT_NREC);
XX			}
XX		}
XX	}
XX	*node_nbr = s_nod;
XX	return (0);
XX}
XX
XX
XX
XX/* get the previous node */
XXbtprevious(node_nbr, cno, bt)
XXlong	*node_nbr;
XXstruct	btnode	*cno;
XXstruct	btree	*bt;
XX{
XX	long	popr();
XX	long	popl();
XX	long	s_nod;
XX
XX	s_nod = *node_nbr;
XX
XX	/* if we are called without a node, wind to the end of file */
XX	if (*node_nbr == 0) {
XX		return (bttail(node_nbr,cno,bt));
XX	}
XX
XX
XX	do {
XX		if (cno->lptr == 0) {
XX			/* can't move left */
XX			if (bt->rstak.sptr == 0) {
XX				/* none in stack */
XX				if(rnode(*node_nbr, cno, bt) <0)
XX					return(-1);
XX				return (BT_EOF);
XX				/* don't reset node_nbr */
XX			} else {
XX				/* can't go left & stack full (pop stack) */
XX				s_nod = popr(bt);
XX				if(rnode(s_nod, cno, bt) <0)
XX					return(-1);
XX			}
XX		} else {
XX			/* left then to bottom right - is previous */
XX			pushl(bt, s_nod);
XX			s_nod = cno->lptr;
XX			if(rnode(s_nod, cno, bt) <0)
XX				return(-1);
XX			while (cno->rptr != 0) {
XX				/* bottom right */
XX				pushr(bt, s_nod);
XX				/* of this sub-tree */
XX				s_nod = cno->rptr;
XX				if(rnode(s_nod, cno, bt) <0)
XX					return(-1);
XX			}
XX		}
XX	} while (cno->deleted == BT_DELETED);
XX
XX	*node_nbr = s_nod;
XX	return (0);
XX}
XX
XX
XX
XX/* print the btree error message */
XXvoid
XXbtperror(str)
XXchar	*str;
XX{
XX	extern	int	errno;
XX
XX	/* is it ours ?? */
XX	if (errno == 0) {
XX		if (str[strlen(str) - 1] != ':')
XX			(void) fprintf(stderr, "%s: %s\n", str, bterrs[bterrno]);
XX		else
XX			(void) fprintf(stderr, "%s %s\n", str, bterrs[bterrno]);
XX		bterrno = 0;
XX	} else {
XX		perror(str);
XX	}
XX}
SHAR_EOF
if test 17883 -ne "`wc -c btree.c`"
then
echo shar: error transmitting btree.c '(should have been 17883 characters)'
fi
echo shar: extracting btree.h '(2161 characters)'
sed 's/^XX//' << \SHAR_EOF > btree.h
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX *
XX * $Header: btree.h,v 1.1 88/06/01 21:35:55 mjr Rel $: btree.h
XX *
XX * $Log:	btree.h,v $
XX * Revision 1.1  88/06/01  21:35:55  mjr
XX * Initial revision
XX * 
XX */
XX
XX#ifndef _INCL_BTREE_H
XX
XX#define BT_NREC	1	/* no such record */
XX#define BT_EOF	2	/* end of the tree (either end) */
XX#define BT_ERR	-1	/* something went wrong */
XX
XX#define BT_KSIZ	40	/* size of keys to store (or trunc) */
XX#define BT_CSIZ	30	/* # of nodes to cache readonly */
XX			/* current cache alg gives ~59% hits */
XX
XX/* this indicates a node deleted */
XX#define	BT_DELETED	1
XX#define	BT_ACTIVE	0
XX
XX#define	BT_MAGIC	0x72251
XX
XX/* btree stack */
XX#define STACK_LENGTH 30		/* length of history stacks */
XX
XXstruct btstack {
XX	long	ele[STACK_LENGTH];	/* stack elements */
XX	int	lev[STACK_LENGTH];	/* stack levels */
XX	int	sptr;			/* stack pointer */
XX};
XX
XX/* a disk resident btree super block */
XXstruct	btsuper {
XX	long	magic;		/* generic magic number */
XX	long	free;		/* pointer to next free (basically, EOF) */
XX	long	root;		/* pointer to root node */
XX	long	list;		/* number of active records */
XX};
XX
XXstruct btnode {
XX	long	recno;		/* index to external file, or whatever */
XX	long	lptr;		/* left-pointer */
XX	long	rptr;		/* right-pointer */
XX	char	key[BT_KSIZ];	/* datum */
XX	short	deleted;	/* deleted flag */
XX	short	balance;	/* balance flag */
XX};
XX#define	BTNODE	struct	btnode
XX
XX/* a memory resident btree super block */
XX/* including room to hold a disk super block */
XXstruct btree {
XX	int	fd;		/* file descriptor */
XX	int	slev;		/* history stack level */
XX	struct	btsuper	sblk;	/* copy of superblock */
XX	struct	btstack	rstak;	/* history stack */
XX	struct	btstack	lstak;	/* history stack */
XX	struct	btnode	cache[BT_CSIZ];	/* read-only cache */
XX};
XX#define	BTREE	struct	btree
XX
XXBTREE	*btopen();
XXvoid	btperror();
XX
XXextern	int	bterrno;	/* btree error number/flag */
XXextern	char	*bterrs[];	/* error message list */
XX/* error codes - match bterrs */
XX#define	BT_BAD_MAGIC	1	/* bad index file magic number */
XX#define	BT_BAD_STACK	2	/* history stack overflow */
XX#define	BT_BAD_ROOT	3	/* failed attempt to delete node 0 */
XX
XX#define _INCL_BTREE_H
XX#endif
SHAR_EOF
if test 2161 -ne "`wc -c btree.h`"
then
echo shar: error transmitting btree.h '(should have been 2161 characters)'
fi
echo shar: extracting btree.3 '(7582 characters)'
sed 's/^XX//' << \SHAR_EOF > btree.3
XX.\" btree.3l (C)1988 Marcus J. Ranum, Welch Medical Library
XX.\" $Header: btree.3,v 1.1 88/06/01 21:36:01 mjr Rel $
XX.TH BTREE 3l
XX.SH NAME
XXbtopen, btclose, btfind, btinsert, btdelete, bthead, bttail,
XXbtnext, btprevious, btsetrec, btperror
XX.br
XX\- the poor man's b-tree index library 
XX.SH SYNTAX
XX.B #include <local/btree.h>
XX.PP
XX.B BTREE *btopen(path,flags,mode)
XX.br
XX.SM
XX.B char *path;
XX.br
XX.B int flags, mode;
XX.PP
XX.B int btclose(btree)
XX.br
XX.SM
XX.B BTREE *btree;
XX.PP
XX.B int btfind(key,node_num,nodebuf,btree)
XX.br
XX.SM
XX.B char *key;
XX.br
XX.B long *node_num;
XX.br
XX.B BTNODE *nodebuf;
XX.br
XX.B BTREE btree;
XX.PP
XX.B int btinsert(key,recno,btree)
XX.br
XX.SM
XX.B char *key;
XX.br
XX.B long recno;
XX.br
XX.B BTREE btree;
XX.PP
XX.B int btdelete(number,btree)
XX.br
XX.SM
XX.B long number;
XX.br
XX.B BTREE btree;
XX.PP
XX.B int bthead(node_num,nodebuf,btree)
XX.br
XX.SM
XX.B long *node_num;
XX.br
XX.B BTNODE *nodebuf;
XX.br
XX.B BTREE *btree;
XX.PP
XX.B int bttail(node_num,nodebuf,btree)
XX.br
XX.SM
XX.B long *node_num;
XX.br
XX.B BTNODE *nodebuf;
XX.br
XX.B BTREE *btree;
XX.PP
XX.B int btnext(node_num,nodebuf,btree)
XX.br
XX.SM
XX.B long *node_num;
XX.br
XX.B BTNODE *nodebuf;
XX.br
XX.B BTREE *btree;
XX.PP
XX.B int btprevious(node_num,nodebuf,btree)
XX.br
XX.SM
XX.B long *node_num;
XX.br
XX.B BTNODE *nodebuf;
XX.br
XX.B BTREE *btree;
XX.PP
XX.B int btsetrec(number,newrec,btree)
XX.br
XX.SM
XX.B long number, newrec;
XX.br
XX.B BTREE *btree;
XX.PP
XX.B void btperror(message)
XX.br
XX.SM
XX.B char *message;
XX.SH DESCRIPTION
XX.PP
XXThe poor man's btree library is a set of routines to manage a balanced
XXbtree index file. No provisions are made for storing any data other 
XXthan the key in the btree nodes, except for a single long int that
XXcan be used as a pointer to storage elsewhere. No provisions are made
XXfor multi-user access. Currently, deleted keys are not handled
XXelegantly. They are marked as deleted, but are not re-used, which will
XXcause an index file to grow constantly, which can waste 
XXdisk space if the data is frequently modified. A read cache is 
XXmaintained, but write requests are not cached. Multiple entries for
XXthe same key are permitted, so it is the user's responsibility to specify
XXthe node number to delete. Keys are fixed-length, defined
XXin
XX.B btree.h
XXand overlong keys are silently truncated during inserts.
XXAll functions return -1 on error, 0 for success, but occasionally
XXother codes defined in
XX.B btree.h
XXfor special conditions (no record found, end of tree, etc).
XX.PP
XXThe
XX.B btopen
XXsubroutine allocates a btree control structure, using the path name
XXprovided in
XX.B path.
XXThe
XX.B flags
XXand 
XX.B mode
XXarguments are passed to open(2) to control creation and permissions.
XXIf the data file is successfully opened with 0 size (either through
XXopen(2) flags O_TRUNC or O_CREAT on a nonexistent file) a new btree
XXis initialized automatically.
XX.B Btopen
XXchecks a magic number stored in the index superblock, and will fail
XXunless the magic number is correct, to prevent accidentally using a
XXdamaged file, or a non-index file. If
XX.B btopen
XXcannot open the requested file, or encounters other problems,
XXit returns NULL.
XX.PP
XXThe
XX.B btclose
XXsubroutine closes an opened btree, and deallocates the memory that
XXwas allocated in btopen.
XX.PP
XXThe 
XX.B btfind
XXsubroutine searches for a key within the index. The argument 
XX.B key
XXis
XXsearched for, and the results of the search are placed in 
XX.B nodebuf.
XXThe number of the "found" node is placed in 
XX.B node_num.
XX.B Btree 
XXis expected to be an open, valid, btree index.
XX.B Btfind
XXreturns 0 if the requested key is located, -1 for error, or BT_NREC if
XXthe requested key was not found. If the requested key is not found,
XXthe contents of
XX.B nodebuf
XXand 
XX.b node_num
XXwill be loaded with the node that held the place in the tree directly
XXbefore where the requested node would have been. This gives
XX.B btfind
XXa limited ability for finding the nearest "like" node (though it is a
XXtoss-up whether the preceding node is "closer" than the
XXfollowing one). 
XX.PP
XXThe
XX.B btinsert
XXsubroutine creates a new node for the given
XX.B key
XXand numbers it with the requested 
XX.B recno,
XXwhich can be used to link the index to additional data elsewhere.
XXIf
XX.B btinsert
XXfails, it returns -1, otherwise it returns 0.
XX.PP
XXThe
XX.B btdelete
XXsubroutine marks node number
XX.B number
XXas deleted, which causes it to become "invisible" during future searches.
XXThe disk space is not reclaimed.
XX.B Btdelete
XXreturns 0 on success, -1 on failure. Since the node to delete is
XXreferenced only by node number,
XX.B btdelete
XXis typically used with something like
XX.B btfind
XXthat provides the node number.
XX.PP
XXThe
XX.B bthead
XXsubroutine searches to the first entry in the btree, returning the entry's
XXnode number in
XX.B node_num
XXand its data in
XX.B nodebuf.
XX.B Bthead
XXreturns 0 on success, -1 on failure.
XX.PP
XXThe
XX.B bttail
XXsubroutine searches to the last entry in the btree, but is otherwise
XXexactly the same as 
XX.B bthead.
XX.PP
XXThe
XX.B btnext
XXsubroutine finds the next node in the tree after
XX.B node_nbr
XXand places its data in
XX.B nodebuf.
XX.B Btnext
XXreturns 0 on success, -1 on failure, ande
XX.B BT_EOF
XXif it cannot search any further because it has hit the end of the tree.
XXNote that
XX.B BT_EOF
XXis a positive value, so tests like:
XX.sp
XX.ti 1i
XXwhile(btnext(&node_nbr,&nodebuf,bt) >= 0);
XX.sp
XXwill loop forever. Something like:
XX.sp
XX.ti 1i
XXwhile(btnext(&node_nbr,&nodebuf,bt) == 0);
XX.sp
XXshould be used instead. If 
XX.B node_nbr 
XXis 0L (undefined)
XX.B btnext
XXwill automatically search to the beginning of the index file, rather
XXthan starting at a random location.
XX.PP
XXThe
XX.B btprevious
XXsubroutine finds the next node in the tree before 
XX.B node_nbr
XXbut is otherwise exactly the same as 
XX.B bthead. If invoked with
XX.B node_nbr
XX0, 
XX.B btprevious
XXautomatically searches to to the end of the index file.
XX.PP
XXThe 
XX.B btsetrec
XXsubroutine allows the user to modify node number
XX.B node_nbr
XXrecord number, for whatever reason. Typically
XX.B recno
XXis used for linking the index to additional data elsewhere.
XX.PP
XXThe
XX.B btperror
XXsubroutine will print any applicable btree error messages, similarly to 
XX.B perror.
XXIf the btree error number
XX.B bterrno
XXis 0, and the UNIX
XX.B errno
XXis set, perror is called instead, to print the UNIX error. The btree
XXerror messages are accessible to the user, as an array of strings
XX.B bterrs.
XXA call to 
XX.B btperror
XXresets 
XX.B bterrno
XXautomatically.
XX.SH EXAMPLES
XX.PP
XXThe following routine inputs strings into an index:
XX.nf
XX.na
XX.ft C
XX#include <local/btree.h>
XX
XXmain(ac,av)
XXint	ac;
XXchar	*av[];
XX{
XX	BTREE	*bt;
XX	char	buf[BUFSIZ];
XX	long	recno =0L;
XX
XX	if((bt = btopen(av[1],O_CREAT,0600)) ==NULL) {
XX		btperror(av[1]);
XX		exit(1);
XX	}
XX
XX	while(gets(buf)) {
XX		if(btinsert(buf,++recno,bt)< 0) {
XX			btperror("insert");
XX			(void)btclose(bt);
XX			exit(1);
XX		}
XX	}
XX	exit(btclose(bt));
XX}
XX.ft P
XX.sp
XX.PP
XXThe following example dumps an index:
XX.ft C
XX#include <local/btree.h>
XX
XXmain(ac,av)
XXint	ac;
XXchar	*av[];
XX{
XX
XX	BTREE	*bt;
XX	BTNODE	cnode;
XX	long	nbr =0L;
XX
XX	if((bt = btopen(av[1],O_RDWR,0600)) == NULL) {
XX		btperror(av[1]);
XX		exit(1);
XX	}
XX
XX	while((btnext(&nbr, &cnode, bt) == 0) {
XX		(void)printf("%s\\n",cnode.key);
XX	}
XX	exit(btclose(bt));
XX}
XX.ft P
XX.ad
XX.SH RESTRICTIONS
XX.PP
XXFiles can get large unless copied out occasionally.
XX.PP
XXIt is not permitted to delete node number 0.
XX.PP
XX.SH DIAGNOSTICS
XXThese functions return -1 on error, 0 on success, and occasionally other
XXvalues that can signal end of file or failure to find a record.
XX.SH AUTHOR
XX.PP
XXThe original code was from a PD library for IBM PCs written by Ray Swartz.
XX90% of the code was totally re-written and restructured to make it into
XXa usable library.
XX.PP
XXMarcus J. Ranum, Welch Medical Library.
XX.br
XX.ti 1i
XXuunet!aplcen!osiris!welchvax!mjr
XX.br
XX.ti 1i
XXmjr@jhuigf.BITNET
XX.SH SEE ALSO
SHAR_EOF
if test 7582 -ne "`wc -c btree.3`"
then
echo shar: error transmitting btree.3 '(should have been 7582 characters)'
fi
echo shar: extracting bench.c '(2003 characters)'
sed 's/^XX//' << \SHAR_EOF > bench.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: bench.c,v 1.1 88/06/01 21:34:55 mjr Rel $: bench.c";
XX#endif
XX
XX/*
XX * $Log:	bench.c,v $
XX * Revision 1.1  88/06/01  21:34:55  mjr
XX * Initial revision
XX * 
XX */
XX
XX
XX/* grosso hack to load a database and perform a bunch of */
XX/* operations on it - a sort of benchmark, but not in any */
XX/* real sense of the word */
XX
XX#ifdef BSD
XX#include <sys/file.h>
XX#endif
XX#ifdef SYSV
XX#include <sys/fcntl.h>
XX#endif
XX#include <stdio.h>
XX#include "btree.h"
XX
XXmain(ac,av)
XXint	ac;
XXchar	*av[];
XX{
XX	BTREE	*bt;
XX	BTNODE	cnod;
XX	int	fo;
XX	int	xo;
XX	long	nbr;
XX	long	time();
XX	long	random();
XX	long	start;
XX	long	end;
XX	char	buf[120];
XX
XX	if(ac < 2) {
XX		(void)fprintf(stderr,"usage: %s <file>\n",av[0]);
XX		exit(1);
XX	}
XX
XX	(void)srandom(getpid());
XX	if((bt = btopen(av[1],O_CREAT|O_TRUNC,0600)) == NULL) {
XX		btperror(av[1]);
XX		exit(1);
XX	}
XX
XX	(void)time(&start);
XX
XX	(void)fprintf(stderr,"insert 5000\n");
XX	/* insert 5000 random records */
XX	for(fo = 0; fo < 5000; fo++) {
XX		for(xo = 0; xo < ((int)random()%15)+1; xo++)
XX			buf[xo] = (int)((random()%25)+97);
XX		buf[xo] = '\0';
XX		if(btinsert(buf, bt->sblk.free, bt)<0) {
XX			btperror("insert");
XX			exit(1);
XX		}
XX	}
XX
XX	(void)fprintf(stderr,"find 5000\n");
XX	/* find 5000 random records */
XX	for(fo = 0; fo < 5000; fo++) {
XX		for(xo = 0; xo < ((int)random()%15)+1; xo++)
XX			buf[xo] = (int)((random()%25)+97);
XX		buf[xo] = '\0';
XX		if(btfind(buf, &nbr, &cnod, bt)<0) {
XX			btperror("find");
XX			exit(1);
XX		}
XX	}
XX
XX	(void)fprintf(stderr,"delete 500\n");
XX	for(fo = 0; fo < 500; fo++) {
XX		for(xo = 0; xo < ((int)random()%15)+1; xo++)
XX			buf[xo] = (int)(random()%25)+97;
XX		buf[xo] = '\0';
XX		if(btfind(buf, &nbr, &cnod, bt)<0) {
XX			btperror("find");
XX			exit(1);
XX		}
XX		if(nbr != 0L && btdelete(nbr, bt)<0) {
XX			btperror("delete");
XX			(void)fprintf(stderr,"node=%d\n",nbr);
XX			exit(1);
XX		}
XX	}
XX
XX	(void)btclose(bt);
XX	(void)time(&end);
XX	(void)fprintf(stderr,"total secs:%ld\n",end-start);
XX	exit(0);
XX}
SHAR_EOF
if test 2003 -ne "`wc -c bench.c`"
then
echo shar: error transmitting bench.c '(should have been 2003 characters)'
fi
echo shar: extracting loadtree.c '(1264 characters)'
sed 's/^XX//' << \SHAR_EOF > loadtree.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: loadtree.c,v 1.1 88/06/01 21:35:24 mjr Rel $: loadtree.c";
XX#endif
XX
XX/*
XX * $Log:	loadtree.c,v $
XX * Revision 1.1  88/06/01  21:35:24  mjr
XX * Initial revision
XX * 
XX */
XX
XX
XX/* used to load a tree with data from the standard input - */
XX/* primarily for testing purposes - to load a file with some */
XX/* "known" data, which can then be dumped and checked, etc */
XX
XX#ifdef BSD
XX#include <sys/file.h>
XX#endif
XX#ifdef SYSV
XX#include <sys/fcntl.h>
XX#endif
XX#include <stdio.h>
XX#include "btree.h"
XX
XXmain(ac,av)
XXint	ac;
XXchar	*av[];
XX{
XX	BTREE	*bt;
XX	long	time();
XX	long	start;
XX	long	end;
XX	long	lded = 0L;
XX	char	buf[BUFSIZ];
XX
XX	if(ac < 2) {
XX		(void)fprintf(stderr,"usage: %s <file>\n",av[0]);
XX		exit(1);
XX	}
XX
XX	(void)srandom(getpid());
XX
XX	if((bt = btopen(av[1],O_CREAT,0600)) ==NULL) {
XX		btperror(av[1]);
XX		exit(1);
XX	}
XX
XX	(void)time(&start);
XX
XX	while(gets(buf)) {
XX		/* note - btinsert() truncates overlong keys */
XX		if(btinsert(buf, bt->sblk.free, bt)< 0) {
XX			btperror("insert");
XX			(void)btclose(bt);
XX			exit(1);
XX		} else 
XX			lded++;
XX	}
XX	(void)btclose(bt);
XX	(void)time(&end);
XX	(void)fprintf(stderr,"total secs:%ld - %ld records\n",end-start,lded);
XX	exit(0);
XX}
SHAR_EOF
if test 1264 -ne "`wc -c loadtree.c`"
then
echo shar: error transmitting loadtree.c '(should have been 1264 characters)'
fi
echo shar: extracting treedump.c '(1064 characters)'
sed 's/^XX//' << \SHAR_EOF > treedump.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: treedump.c,v 1.1 88/06/01 21:35:46 mjr Rel $: treedump.c";
XX#endif
XX
XX/*
XX * $Log:	treedump.c,v $
XX * Revision 1.1  88/06/01  21:35:46  mjr
XX * Initial revision
XX * 
XX */
XX
XX
XX/* dump a tree to the standard output */
XX
XX#ifdef BSD
XX#include <sys/file.h>
XX#endif
XX#ifdef SYSV
XX#include <sys/fcntl.h>
XX#endif
XX#include <stdio.h>
XX#include "btree.h"
XX
XXmain(ac,av)
XXint	ac;
XXchar	*av[];
XX{
XX
XX	BTREE	*bt;
XX	BTNODE	cnode;
XX	long	nbr =0L;
XX
XX	if(ac < 2) {
XX		(void)fprintf(stderr,"usage: %s <file>\n",av[0]);
XX		exit(1);
XX	}
XX
XX	if((bt = btopen(av[1],O_RDWR,0600)) == NULL) {
XX		btperror(av[1]);
XX		exit(1);
XX	}
XX
XX	/* if btnext is called with nbr = 0, it automatically winds to the */
XX	/* front of the file - as in this example */
XX	while (1) {
XX		switch (btnext(&nbr, &cnode, bt)) {
XX		case (-1):
XX			btperror("btnext");
XX			(void)btclose(bt);
XX			exit(1);
XX
XX		case (0):
XX			(void)printf("%s\n",cnode.key);
XX			break;
XX
XX		case (BT_EOF):
XX			(void)btclose(bt);
XX			exit(0);
XX		}
XX	}
XX}
SHAR_EOF
if test 1064 -ne "`wc -c treedump.c`"
then
echo shar: error transmitting treedump.c '(should have been 1064 characters)'
fi
echo shar: extracting sizes.c '(592 characters)'
sed 's/^XX//' << \SHAR_EOF > sizes.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: sizes.c,v 1.1 88/06/01 21:35:34 mjr Rel $: sizes.c";
XX#endif
XX
XX/*
XX * $Log:	sizes.c,v $
XX * Revision 1.1  88/06/01  21:35:34  mjr
XX * Initial revision
XX * 
XX */
XX
XX
XX/* provides some minimally useful info about how big your */
XX/* data structures are */
XX
XX#include "btree.h"
XX
XXmain()
XX{
XX	(void)printf("a btree control structure is %d bytes",sizeof(BTREE));
XX	(void)printf(" (cache of %d nodes)\n",BT_CSIZ);
XX	(void)printf("a btree node is %d bytes\n",sizeof(BTNODE));
XX}
SHAR_EOF
if test 592 -ne "`wc -c sizes.c`"
then
echo shar: error transmitting sizes.c '(should have been 592 characters)'
fi
echo shar: extracting example.c '(3261 characters)'
sed 's/^XX//' << \SHAR_EOF > example.c
XX/*
XX * Copyright (C) 1988, Marcus J. Ranum, William Welch Medical Library
XX * $Author: mjr $
XX */
XX
XX#ifndef lint
XXstatic char *RCSid="$Header: example.c,v 1.1 88/06/01 21:35:15 mjr Rel $: example.c";
XX#endif
XX
XX/*
XX * $Log:	example.c,v $
XX * Revision 1.1  88/06/01  21:35:15  mjr
XX * Initial revision
XX * 
XX */
XX
XX
XX/* nobody ever writes nice test programs. in fact, this one is pretty gross */
XX/* basically, this allows exercising all the various functions of the btree */
XX/* library */
XX
XX#ifdef BSD
XX#include <sys/file.h>
XX#endif
XX#ifdef SYSV
XX#include <sys/fcntl.h>
XX#endif
XX#include <stdio.h>
XX#include "btree.h"
XX
XXextern	char	*strncpy();
XXextern	char	*strcpy();
XX
XXmain(ac,av)
XXint	ac;
XXchar	*av[];
XX{
XX	BTREE	*h1;
XX	BTNODE	cnode;
XX	char	instr[BUFSIZ];
XX	long	node_nbr;
XX
XX	if(ac < 2) {
XX		(void)fprintf(stderr,"usage: %s <file>\n",av[0]);
XX		exit(1);
XX	}
XX
XX	if((h1 = btopen(av[1],O_CREAT|O_RDWR,0600)) ==NULL) {
XX		btperror(av[1]);
XX		exit(1);
XX	}
XX
XX
XX	while (1) {
XX		(void)printf("Find  Next  Tail  Head  Prev  Insrt  Del  Quit:");
XX		if(gets(instr) == NULL)
XX			exit(btclose(h1));
XX
XX		switch (*instr) {
XX
XX		case 'f':
XX		case 'F':
XX			(void)printf("\nKey to Find: ");
XX			(void)gets(instr);
XX			(void)strncpy(instr, instr, BT_KSIZ - 1);
XX			instr[BT_KSIZ - 1] = '\0';
XX			switch(btfind(instr, &node_nbr, &cnode, h1)) {
XX			case BT_NREC:
XX				(void)printf("not found: closest before=%s\n",cnode.key);
XX				break;
XX			case -1:
XX				btperror("find");
XX				break;
XX			default:
XX				(void)printf("current=%s\n", cnode.key);
XX				break;
XX			}
XX			break;
XX
XX		case 'h':
XX		case 'H':
XX			switch(bthead(&node_nbr, &cnode, h1)) {
XX			case (-1):
XX				btperror("bthead() returns -1");
XX				continue;
XX				break;
XX			default:
XX				(void)printf("current=%s\n", cnode.key);
XX			}
XX			break;
XX
XX		case 't':
XX		case 'T':
XX			switch(bttail(&node_nbr, &cnode, h1)) {
XX			case (-1):
XX				btperror("bttail() returns -1");
XX				continue;
XX				break;
XX			default:
XX				(void)printf("current=%s\n", cnode.key);
XX			}
XX			break;
XX
XX		case 'd':
XX		case 'D':
XX			if(btdelete(node_nbr, h1) < 0) {
XX				btperror("delete failed");
XX			} else {
XX				(void)printf("...deleted\n");
XX			}
XX			break;
XX
XX		case 'n':
XX		case 'N':
XX			switch (btnext(&node_nbr, &cnode, h1)) {
XX			case (-1):
XX				btperror("btnext() returns -1");
XX				continue;
XX				break;
XX
XX			case (0):
XX				(void)printf("current=%s\n", cnode.key);
XX				break;
XX
XX			case (BT_EOF):
XX				(void)printf("end of file: current=%s\n",cnode.key);
XX				break;
XX			}
XX			break;
XX
XX		case 'p':
XX		case 'P':
XX			switch (btprevious(&node_nbr, &cnode, h1)) {
XX			case (-1):
XX				btperror("btprevious() returns -1");
XX				continue;
XX				break;
XX			case (0):
XX				(void)printf("current=%s\n", cnode.key);
XX				break;
XX			case (BT_EOF):
XX				(void)printf("start of file: current=%s\n",cnode.key);
XX			}
XX			break;
XX
XX		case 'i':
XX		case 'I':
XX			(void)printf("Enter a key: ");
XX			(void)gets(instr);
XX			(void)strncpy(cnode.key, instr, BT_KSIZ);
XX			cnode.key[BT_KSIZ - 1] = '\0';
XX
XX			/* h1->sblk.free is used here as arbitrary record # */
XX			/* typical use would be to set this to the recno */
XX			/* for whatever it is that is being indexed into */
XX			if(btinsert(cnode.key, h1->sblk.free, h1) < 0)
XX				btperror("insert failed");
XX			else
XX				(void)printf("...inserted\n");
XX			break;
XX
XX		case 'q':
XX		case 'Q':
XX			exit(btclose(h1));
XX
XX		default:
XX			(void)printf("huh?\n");
XX		}
XX	}
XX}
SHAR_EOF
if test 3261 -ne "`wc -c example.c`"
then
echo shar: error transmitting example.c '(should have been 3261 characters)'
fi
#	End of shell archive
exit 0