seekdir.c 2.9 KB

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