aboutsummaryrefslogtreecommitdiff
path: root/src/lib9/dirread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib9/dirread.c')
-rw-r--r--src/lib9/dirread.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/lib9/dirread.c b/src/lib9/dirread.c
new file mode 100644
index 00000000..f4610f68
--- /dev/null
+++ b/src/lib9/dirread.c
@@ -0,0 +1,155 @@
+#include <u.h>
+#include <libc.h>
+
+#undef asctime
+#undef ctime
+#undef gmtime
+#undef localtime
+
+#include <sys/stat.h>
+#include <dirent.h>
+
+extern int _p9dir(struct stat*, char*, Dir*, char**, char*);
+
+static int
+countde(char *p, int n)
+{
+ char *e;
+ int m;
+ struct dirent *de;
+
+ e = p+n;
+ m = 0;
+ while(p < e){
+ de = (struct dirent*)p;
+ if(de->d_reclen <= 4+2+2+1 || p+de->d_reclen > e)
+ break;
+ if(de->d_namlen == 1 && de->d_name[0]=='.')
+ de->d_namlen = 0;
+ else if(de->d_namlen == 2 && de->d_name[0]=='.' && de->d_name[1]=='.')
+ de->d_namlen = 0;
+ else
+ m++;
+ p += de->d_reclen;
+ }
+ return m;
+}
+
+static int
+dirpackage(int fd, char *buf, int n, Dir **dp)
+{
+ int oldwd;
+ char *p, *str, *estr;
+ int i, nstr, m;
+ struct dirent *de;
+ struct stat st;
+ Dir *d;
+
+ n = countde(buf, n);
+ if(n <= 0)
+ return n;
+
+ if((oldwd = open(".", O_RDONLY)) < 0)
+ return -1;
+ if(fchdir(fd) < 0)
+ return -1;
+
+ p = buf;
+ nstr = 0;
+ for(i=0; i<n; i++){
+ de = (struct dirent*)p;
+ if(stat(de->d_name, &st) < 0)
+ de->d_namlen = 0;
+ else
+ nstr += _p9dir(&st, de->d_name, nil, nil, nil);
+ p += de->d_reclen;
+ }
+
+ d = malloc(sizeof(Dir)*n+nstr);
+ if(d == nil){
+ fchdir(oldwd);
+ close(oldwd);
+ return -1;
+ }
+ str = (char*)&d[n];
+ estr = str+nstr;
+
+ p = buf;
+ m = 0;
+ for(i=0; i<n; i++){
+ de = (struct dirent*)p;
+ if(de->d_namlen != 0 && stat(de->d_name, &st) >= 0)
+ _p9dir(&st, de->d_name, &d[m++], &str, estr);
+ p += de->d_reclen;
+ }
+
+ fchdir(oldwd);
+ close(oldwd);
+ *dp = d;
+ return m;
+}
+
+long
+dirread(int fd, Dir **dp)
+{
+ char *buf;
+ struct stat st;
+ int n;
+
+ *dp = 0;
+
+ if(fstat(fd, &st) < 0)
+ return -1;
+
+ if(st.st_blksize < 8192)
+ st.st_blksize = 8192;
+
+ buf = malloc(st.st_blksize);
+ if(buf == nil)
+ return -1;
+
+ n = getdents(fd, buf, st.st_blksize);
+ if(n < 0){
+ free(buf);
+ return -1;
+ }
+ n = dirpackage(fd, buf, n, dp);
+ free(buf);
+ return n;
+}
+
+
+long
+dirreadall(int fd, Dir **d)
+{
+ uchar *buf, *nbuf;
+ long n, ts;
+ struct stat st;
+
+ if(fstat(fd, &st) < 0)
+ return -1;
+
+ if(st.st_blksize < 8192)
+ st.st_blksize = 8192;
+
+ buf = nil;
+ ts = 0;
+ for(;;){
+ nbuf = realloc(buf, ts+st.st_blksize);
+ if(nbuf == nil){
+ free(buf);
+ return -1;
+ }
+ buf = nbuf;
+ n = getdents(fd, buf+ts, st.st_blksize);
+ if(n <= 0)
+ break;
+ ts += n;
+ }
+ if(ts >= 0)
+ ts = dirpackage(fd, buf, ts, d);
+ free(buf);
+ if(ts == 0 && n < 0)
+ return -1;
+ return ts;
+}