BSD 4_3_Reno release
[unix-history] / usr / src / lib / libc / gen / telldir.c
index 66a747a..0eef315 100644 (file)
@@ -1,9 +1,54 @@
-/* Copyright (c) 1982 Regents of the University of California */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
 
 
-static char sccsid[] = "@(#)telldir.c 4.3 %G%";
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)telldir.c  5.8 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
 
 #include <sys/param.h>
 
 #include <sys/param.h>
-#include <dir.h>
+#include <dirent.h>
+
+/*
+ * The option SINGLEUSE may be defined to say that a telldir
+ * cookie may be used only once before it is freed. This option
+ * is used to avoid having memory usage grow without bound.
+ */
+#define SINGLEUSE
+
+/*
+ * One of these structures is malloced to describe the current directory
+ * position each time telldir is called. It records the current magic 
+ * cookie returned by getdirentries and the offset within the buffer
+ * associated with that return value.
+ */
+struct ddloc {
+       struct  ddloc *loc_next;/* next structure in list */
+       long    loc_index;      /* key associated with structure */
+       long    loc_seek;       /* magic cookie returned by getdirentries */
+       long    loc_loc;        /* offset of entry in buffer */
+};
+
+#define        NDIRHASH        32      /* Num of hash lists, must be a power of 2 */
+#define        LOCHASH(i)      ((i)&(NDIRHASH-1))
+
+static long    dd_loccnt;      /* Index of entry for sequential readdir's */
+static struct  ddloc *dd_hash[NDIRHASH];   /* Hash list heads for ddlocs */
 
 /*
  * return a pointer into a directory
 
 /*
  * return a pointer into a directory
@@ -12,5 +57,57 @@ long
 telldir(dirp)
        DIR *dirp;
 {
 telldir(dirp)
        DIR *dirp;
 {
-       return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc);
+       register int index;
+       register struct ddloc *lp;
+
+       if ((lp = (struct ddloc *)malloc(sizeof(struct ddloc))) == NULL)
+               return (-1);
+       index = dd_loccnt++;
+       lp->loc_index = index;
+       lp->loc_seek = dirp->dd_seek;
+       lp->loc_loc = dirp->dd_loc;
+       lp->loc_next = dd_hash[LOCHASH(index)];
+       dd_hash[LOCHASH(index)] = lp;
+       return (index);
+}
+
+/*
+ * seek to an entry in a directory.
+ * Only values returned by "telldir" should be passed to seekdir.
+ */
+void
+_seekdir(dirp, loc)
+       register DIR *dirp;
+       long loc;
+{
+       register struct ddloc *lp;
+       register struct ddloc **prevlp;
+       struct dirent *dp;
+       extern long lseek();
+
+       prevlp = &dd_hash[LOCHASH(loc)];
+       lp = *prevlp;
+       while (lp != NULL) {
+               if (lp->loc_index == loc)
+                       break;
+               prevlp = &lp->loc_next;
+               lp = lp->loc_next;
+       }
+       if (lp == NULL)
+               return;
+       if (lp->loc_loc == dirp->dd_loc && lp->loc_seek == dirp->dd_seek)
+               goto found;
+       (void) lseek(dirp->dd_fd, lp->loc_seek, 0);
+       dirp->dd_seek = lp->loc_seek;
+       dirp->dd_loc = 0;
+       while (dirp->dd_loc < lp->loc_loc) {
+               dp = readdir(dirp);
+               if (dp == NULL)
+                       break;
+       }
+found:
+#ifdef SINGLEUSE
+       *prevlp = lp->loc_next;
+       free((caddr_t)lp);
+#endif
 }
 }