ack/lang/cem/libcc.ansi/misc/seekdir.c

108 lines
2.8 KiB
C
Raw Normal View History

1989-12-18 14:40:54 +00:00
/*
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!
*/
2018-06-21 20:33:47 +00:00
#include <errno.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <dirent.h>
1989-12-18 14:40:54 +00:00
extern off_t _lseek(int d, int offset, int whence);
1989-12-18 14:40:54 +00:00
#ifndef NULL
2018-06-21 20:33:47 +00:00
#define NULL 0
1989-12-18 14:40:54 +00:00
#endif
#ifndef SEEK_SET
2018-06-21 20:33:47 +00:00
#define SEEK_SET 0
1989-12-18 14:40:54 +00:00
#endif
2018-06-21 20:33:47 +00:00
typedef int bool; /* Boolean data type */
#define false 0
#define true 1
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
void seekdir(register DIR* dirp, register off_t loc)
1989-12-18 14:40:54 +00:00
/* loc == position from telldir() */
{
2018-06-21 20:33:47 +00:00
register bool rewind; /* "start over when stymied" flag */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if (dirp == NULL || dirp->dd_buf == NULL)
{
1989-12-18 14:40:54 +00:00
errno = EFAULT;
2018-06-21 20:33:47 +00:00
return; /* invalid pointer */
}
1989-12-18 14:40:54 +00:00
/* 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. */
2018-06-21 20:33:47 +00:00
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 */
1989-12-18 14:40:54 +00:00
/* else save time by starting at current dirp->dd_loc */
2018-06-21 20:33:47 +00:00
for (rewind = true;;)
{
register struct dirent* dp;
1989-12-18 14:40:54 +00:00
/* See whether the matching entry is in the current buffer. */
2018-06-21 20:33:47 +00:00
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;
1989-12-18 14:40:54 +00:00
return;
}
2018-06-21 20:33:47 +00:00
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! */
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
dirp->dd_loc = dirp->dd_size = 0;
1989-12-18 14:40:54 +00:00
2018-06-21 20:33:47 +00:00
if (_lseek(dirp->dd_fd, (off_t)0, SEEK_SET)
!= 0)
return; /* errno already set (EBADF) */
if (loc == 0)
return; /* save time */
1989-12-18 14:40:54 +00:00
}
2018-06-21 20:33:47 +00:00
}
1989-12-18 14:40:54 +00:00
}