109 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			109 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
	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 */
 | 
						|
				}
 | 
						|
		}
 | 
						|
}
 |