Minix1.5/lib/other/seekdir.c
#include <lib.h>
/* seekdir -- reposition a directory stream Author: D.A. Gwyn */
/* last edit: 24-May-1987 D A Gwyn */
#include <errno.h>
#include <sys/types.h>
#include <limits.h>
#include <dirent.h>
#include <unistd.h>
#define DULL (DIR *) 0
#define DE_NULL (struct dirent *) 0
typedef int bool; /* Boolean data type */
#define false 0
#define true 1
void seekdir(dirp, loc)
register DIR *dirp; /* stream from opendir() */
register off_t loc; /* position from telldir() */
{
register bool rewind; /* "start over when stymied" flag */
if (dirp == DULL || dirp->dd_buf == (char *)NULL || dirp->dd_magic != _DIR_MAGIC) {
errno = EBADF;
return; /* invalid pointer */
}
/* A (struct dirent)'s d_off is an invented quantity on 4.nBSD
* NFS-supporting systems, so it is not safe to lseek() to it. */
/* Monotonicity of d_off is heavily exploited in the following. */
/* This algorithm is tuned for modest directory sizes. For huge
* directories, it might be more efficient to read blocks until the
* first d_off is too large, then back up one block, or even to use
* binary search on the directory blocks. I doubt that the extra
* code for that would be worthwhile. */
if (dirp->dd_loc >= dirp->dd_size /* invalid index */
|| ((struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off > loc
/* Too far along in buffer */
)
dirp->dd_loc = 0; /* reset to beginning of buffer */
/* Else save time by starting at current dirp->dd_loc */
for (rewind = true;;) {
register struct dirent *dp;
/* See whether the matching entry is in the current buffer. */
if ((dirp->dd_loc < dirp->dd_size /* valid index */
|| readdir(dirp) != DE_NULL /* next buffer read */
&& (dirp->dd_loc = 0, true) /* beginning of buffer set */
)
&& (dp = (struct dirent *) & dirp->dd_buf[dirp->dd_loc])->d_off
<= loc /* match possible in this buffer */
) {
for ( /* dp initialized above */ ;
(char *) dp < &dirp->dd_buf[dirp->dd_size];
dp = (struct dirent *) ((char *) dp + dp->d_reclen)
)
if (dp->d_off == loc) { /* found it! */
dirp->dd_loc =
(char *) dp - dirp->dd_buf;
return;
}
rewind = false; /* no point in backing up later */
dirp->dd_loc = dirp->dd_size; /* set end of buffer */
} else
/* Whole buffer past matching entry */ if (!rewind) { /* no point in searching
* further */
errno = EINVAL;
return; /* no entry at specified loc */
} else { /* rewind directory and start over */
rewind = false; /* but only once! */
dirp->dd_loc = dirp->dd_size = 0;
if (lseek(dirp->dd_fd, (off_t) 0, SEEK_SET) != 0
)
return; /* errno already set (EBADF) */
if (loc == 0) return; /* save time */
}
}
}