2 seekdir -- reposition a directory stream
4 last edit: 24-May-1987 D A Gwyn
6 An unsuccessful seekdir() will in general alter the current
7 directory position; beware.
9 NOTE: 4.nBSD directory compaction makes seekdir() & telldir()
10 practically impossible to do right. Avoid using them!
14 #include <sys/errno.h>
15 #include <sys/types.h>
18 extern off_t _lseek(int d, int offset, int whence);
28 typedef int bool; /* Boolean data type */
33 seekdir(register DIR *dirp, register off_t loc)
34 /* loc == position from telldir() */
36 register bool rewind; /* "start over when stymied" flag */
38 if ( dirp == NULL || dirp->dd_buf == NULL )
41 return; /* invalid pointer */
44 /* A (struct dirent)'s d_off is an invented quantity on 4.nBSD
45 NFS-supporting systems, so it is not safe to lseek() to it. */
47 /* Monotonicity of d_off is heavily exploited in the following. */
49 /* This algorithm is tuned for modest directory sizes. For
50 huge directories, it might be more efficient to read blocks
51 until the first d_off is too large, then back up one block,
52 or even to use binary search on the directory blocks. I
53 doubt that the extra code for that would be worthwhile. */
55 if ( dirp->dd_loc >= dirp->dd_size /* invalid index */
56 || ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off > loc
57 /* too far along in buffer */
59 dirp->dd_loc = 0; /* reset to beginning of buffer */
60 /* else save time by starting at current dirp->dd_loc */
62 for ( rewind = true; ; )
64 register struct dirent *dp;
66 /* See whether the matching entry is in the current buffer. */
68 if ( (dirp->dd_loc < dirp->dd_size /* valid index */
69 || readdir( dirp ) != NULL /* next buffer read */
70 && (dirp->dd_loc = 0, true) /* beginning of buffer set */
72 && (dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off
73 <= loc /* match possible in this buffer */
75 for ( /* dp initialized above */ ;
76 (char *)dp < &dirp->dd_buf[dirp->dd_size];
77 dp = (struct dirent *)((char *)dp + dp->d_reclen)
79 if ( dp->d_off == loc )
82 (char *)dp - dirp->dd_buf;
86 rewind = false; /* no point in backing up later */
87 dirp->dd_loc = dirp->dd_size; /* set end of buffer */
89 else /* whole buffer past matching entry */
91 { /* no point in searching further */
93 return; /* no entry at specified loc */
95 else { /* rewind directory and start over */
96 rewind = false; /* but only once! */
98 dirp->dd_loc = dirp->dd_size = 0;
100 if ( _lseek( dirp->dd_fd, (off_t)0, SEEK_SET )
103 return; /* errno already set (EBADF) */
106 return; /* save time */