/*  
 *  The following routines: opendir, closedir, readdir, seekdir, and telldir
 *  are required for SysV Systems. They are are not to be used by
 *  Xenix systems.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <fcntl.h>

extern int errno;
long lseek();


/*
 * close a directory.
 */

int
closedir(dirp)
	register DIR *dirp;
{
	register int result;
	register int err;


	if (dirp == 0) {
		errno = EINVAL;
		return(-1);
	}

	if (result = close(dirp->dd_fd)) err = errno;

	dirp->dd_fd = -1;
	dirp->dd_loc = 0;
	dirp->dd_size = 0;

	free(dirp);

	if (result) errno = err;

	return(result);
}




/*
 * open a directory.
 */

DIR *
opendir(name)
	char *name;
{
	register DIR *dirp;
	register int fd;
	struct stat sbuf;
	int err;
	extern char *malloc();


	if ((fd = open(name,O_RDONLY,0)) == -1) return(0);

	if ((dirp = (DIR *)malloc(sizeof(DIR))) == 0) {
		close(fd);
		errno = ENOMEM;
		return(0);
	}

	if (fstatx(fd,&sbuf,sizeof(struct stat),0) == -1) {
		err = errno;
		close(fd);
		free(dirp);
		errno = err;
		return(0);
	}

	if (!S_ISDIR(sbuf.st_mode)) {
		close(fd);
		free(dirp);
		errno = ENOTDIR;
		return(0);
	}

	dirp->dd_fd = fd;
	dirp->dd_loc = 0;
	dirp->dd_size = 0;

	return(dirp);
}



/*
 * get next entry in a directory
 */

struct dirent *
readdir(dirp)
	DIR *dirp;
{
	register DIR *dp = dirp;
	register struct dirent *np;
	register long loc;
	int size;
	int fd;


	if (dp == 0) {
		errno = EINVAL;
		return(0);
	}

	fd = dp->dd_fd;
	loc = dp->dd_loc;
	size = dp->dd_size;

	while (1) {
		if (loc >= size) loc = 0;

		if (loc == 0) {
			if ((size = read(fd,dp->dd_buf,DIRBLKSIZ,1)) < 1) {
				if (size == 0) errno = 0;
				dp->dd_size = 0;
				return(0);
			}
		}

		np = (struct dirent *)(dp->dd_buf + loc);

		if ((np->d_reclen <= 0) || (np->d_reclen > (size-loc))) {
			dp->dd_size = 0;
			errno = EXDEV;
			return(0);
		}

		loc += np->d_reclen;

		if (np->d_ino == 0) continue;

		dp->dd_loc = loc;
		dp->dd_size = size;
		return(np);
	}
}


/*
 * seek to an entry in a directory.
 * Only values returned by telldir() should be passed to seekdir().
 */

seekdir(dirp, loc)
	register DIR *dirp;
	long loc;
{
	long now, block, offset;


	if (dirp == 0) {
		errno = EINVAL;
		return(-1) ;
	}

	if ((now = telldir(dirp)) == -1L) return(-1);

	if (loc == now) return(0);

	block = loc &~ (DIRBLKSIZ-1);
	offset = loc & (DIRBLKSIZ-1);

	if (offset && dirp->dd_loc && ((now &~ (DIRBLKSIZ-1)) == block)) {
		dirp->dd_loc = offset;
		return(0);
	}

	if (lseek(dirp->dd_fd,block,0) == -1L) return(-1);

	dirp->dd_loc = 0;
	dirp->dd_size = 0;

	while (dirp->dd_loc < offset) {
		if (readdir(dirp) == 0) return /* (-1) */ ;
	}

	return(0);
}


/*
 * return a pointer into a directory
 */

long
telldir(dirp)
	register DIR *dirp;
{
	register long ofs;


	if (dirp == 0) {
		errno = EINVAL;
		return(-1L);
	}

	if ((ofs = lseek(dirp->dd_fd,0L,1)) == -1L) return(ofs);

	return(ofs - dirp->dd_size + dirp->dd_loc);
}
