home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume9 / gwyn-dir-lib / seekdir.c < prev    next >
C/C++ Source or Header  |  1987-04-30  |  3KB  |  112 lines

  1. /*
  2.     seekdir -- reposition a directory stream
  3.  
  4.     last edit:    25-Apr-1987    D A Gwyn
  5.  
  6.     An unsuccessful seekdir() will in general alter the current
  7.     directory position; beware.
  8.  
  9.     NOTE:    4.nBSD directory compaction makes seekdir() & telldir()
  10.         practically impossible to do right.  Avoid using them!
  11. */
  12.  
  13. #include    <sys/errno.h>
  14. #include    <sys/types.h>
  15. #include    <dirent.h>
  16.  
  17. extern off_t    lseek();
  18.  
  19. extern int    errno;
  20.  
  21. #ifndef NULL
  22. #define    NULL    0
  23. #endif
  24.  
  25. #ifndef SEEK_SET
  26. #define    SEEK_SET    0
  27. #endif
  28.  
  29. typedef int    bool;            /* Boolean data type */
  30. #define    false    0
  31. #define    true    1
  32.  
  33. void
  34. seekdir( dirp, loc )
  35.     register DIR        *dirp;    /* stream from opendir() */
  36.     register off_t        loc;    /* position from telldir() */
  37.     {
  38.     register bool        rewind;    /* "start over when stymied" flag */
  39.  
  40.     if ( dirp == NULL || dirp->dd_buf == NULL )
  41.         {
  42.         errno = EFAULT;
  43.         return;            /* invalid pointer */
  44.         }
  45.  
  46.     /* A (struct dirent)'s d_off is an invented quantity on 4.nBSD
  47.        NFS-supporting systems, so it is not safe to lseek() to it. */
  48.  
  49.     /* Monotonicity of d_off is heavily exploited in the following. */
  50.  
  51.     /* This algorithm is tuned for modest directory sizes.  For
  52.        huge directories, it might be more efficient to read blocks
  53.        until the first d_off is too large, then back up one block,
  54.        or even to use binary search on the directory blocks.  I
  55.        doubt that the extra code for that would be worthwhile. */
  56.  
  57.     if ( dirp->dd_loc >= dirp->dd_size    /* invalid index */
  58.       || ((struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off > loc
  59.                     /* too far along in buffer */
  60.        )
  61.         dirp->dd_loc = 0;    /* reset to beginning of buffer */
  62.     /* else save time by starting at current dirp->dd_loc */
  63.  
  64.     for ( rewind = true; ; )
  65.         {
  66.         register struct dirent    *dp;
  67.  
  68.         /* See whether the matching entry is in the current buffer. */
  69.  
  70.         if ( (dirp->dd_loc < dirp->dd_size    /* valid index */
  71.            || readdir( dirp ) != NULL    /* next buffer read */
  72.            && (dirp->dd_loc = 0, true)    /* beginning of buffer set */
  73.              )
  74.           && (dp = (struct dirent *)&dirp->dd_buf[dirp->dd_loc])->d_off
  75.             <= loc        /* match possible in this buffer */
  76.            )    {
  77.             for ( /* dp initialized above */ ;
  78.                   (char *)dp < &dirp->dd_buf[dirp->dd_size];
  79.                   dp = (struct dirent *)((char *)dp + dp->d_reclen)
  80.                 )
  81.                 if ( dp->d_off == loc )
  82.                     {    /* found it! */
  83.                     dirp->dd_loc =
  84.                         (char *)dp - dirp->dd_buf;
  85.                     return;
  86.                     }
  87.  
  88.             rewind = false;    /* no point in backing up later */
  89.             dirp->dd_loc = dirp->dd_size;    /* set end of buffer */
  90.             }
  91.         else            /* whole buffer past matching entry */
  92.             if ( !rewind )
  93.                 {    /* no point in searching further */
  94.                 errno = EINVAL;
  95.                 return;    /* no entry at specified loc */
  96.                 }
  97.             else    {    /* rewind directory and start over */
  98.                 rewind = false;    /* but only once! */
  99.  
  100.                 dirp->dd_loc = dirp->dd_size = 0;
  101.  
  102.                 if ( lseek( dirp->dd_fd, (off_t)0, SEEK_SET )
  103.                     != 0
  104.                    )
  105.                     return;    /* errno already set (EBADF) */
  106.  
  107.                 if ( loc == 0 )
  108.                     return; /* save time for rewinddir() */
  109.                 }
  110.         }
  111.     }
  112.