123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- /*
- seekdir -- reposition a directory stream
- last edit: 24-May-1987 D A Gwyn
- An unsuccessful seekdir() will in general alter the current
- directory position; beware.
- NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
- practically impossible to do right. Avoid using them!
- */
- #include <errno.h>
- #include <sys/errno.h>
- #include <sys/types.h>
- #include <dirent.h>
- extern off_t _lseek(int d, int offset, int whence);
- #ifndef NULL
- #define NULL 0
- #endif
- #ifndef SEEK_SET
- #define SEEK_SET 0
- #endif
- typedef int bool; /* Boolean data type */
- #define false 0
- #define true 1
- void
- seekdir(register DIR *dirp, register off_t loc)
- /* loc == position from telldir() */
- {
- register bool rewind; /* "start over when stymied" flag */
- if ( dirp == NULL || dirp->dd_buf == NULL )
- {
- errno = EFAULT;
- 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 ) != 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 */
- }
- }
- }
|