summaryrefslogtreecommitdiff
path: root/appl/lib/readdir.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/readdir.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/lib/readdir.b')
-rw-r--r--appl/lib/readdir.b123
1 files changed, 123 insertions, 0 deletions
diff --git a/appl/lib/readdir.b b/appl/lib/readdir.b
new file mode 100644
index 00000000..8b8ad276
--- /dev/null
+++ b/appl/lib/readdir.b
@@ -0,0 +1,123 @@
+implement Readdir;
+
+include "sys.m";
+ sys: Sys;
+ Dir: import sys;
+include "readdir.m";
+
+init(path: string, sortkey: int): (array of ref Dir, int)
+{
+ sys = load Sys Sys->PATH;
+ fd := sys->open(path, sys->OREAD);
+ if(fd == nil)
+ return (nil, -1);
+ return readall(fd, sortkey);
+}
+
+readall(fd: ref Sys->FD, sortkey: int): (array of ref Dir, int)
+{
+ sys = load Sys Sys->PATH;
+ dl: list of array of Dir;
+ n := 0;
+ for(;;){
+ (nr, b) := sys->dirread(fd);
+ if(nr <= 0){
+ # any error makes the whole directory unreadable
+ if(nr < 0)
+ return (nil, -1);
+ break;
+ }
+ dl = b :: dl;
+ n += nr;
+ }
+ rl := dl;
+ for(dl = nil; rl != nil; rl = tl rl)
+ dl = hd rl :: dl;
+ a := makerefs(dl, n, sortkey & COMPACT);
+ sortkey &= ~COMPACT;
+ if((sortkey & ~DESCENDING) == NONE)
+ return (a, len a);
+ return sortdir(a, sortkey);
+}
+
+makerefs(dl: list of array of Dir, n: int, compact: int): array of ref Dir
+{
+ a := array[n] of ref Dir;
+ ht: array of list of string;
+ if(compact)
+ ht = array[41] of list of string;
+ j := 0;
+ for(; dl != nil; dl = tl dl){
+ d := hd dl;
+ for(i := 0; i < len d; i++)
+ if(ht == nil || hashadd(ht, d[i].name))
+ a[j++] = ref d[i];
+ }
+ if(j != n)
+ a = a[0:j];
+ return a;
+}
+
+sortdir(a: array of ref Dir, key: int): (array of ref Dir, int)
+{
+ mergesort(a, array[len a] of ref Dir, key);
+ return (a, len a);
+}
+
+# mergesort because it's stable.
+mergesort(a, b: array of ref Dir, key: int)
+{
+ r := len a;
+ if (r > 1) {
+ m := (r-1)/2 + 1;
+ mergesort(a[0:m], b[0:m], key);
+ mergesort(a[m:], b[m:], key);
+ b[0:] = a;
+ for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
+ if (greater(b[i], b[j], key))
+ a[k] = b[j++];
+ else
+ a[k] = b[i++];
+ }
+ if (i < m)
+ a[k:] = b[i:m];
+ else if (j < r)
+ a[k:] = b[j:r];
+ }
+}
+
+greater(x, y: ref Dir, sortkey: int): int
+{
+ case (sortkey) {
+ NAME => return(x.name > y.name);
+ ATIME => return(x.atime < y.atime);
+ MTIME => return(x.mtime < y.mtime);
+ SIZE => return(x.length > y.length);
+ NAME|DESCENDING => return(x.name < y.name);
+ ATIME|DESCENDING => return(x.atime > y.atime);
+ MTIME|DESCENDING => return(x.mtime > y.mtime);
+ SIZE|DESCENDING => return(x.length < y.length);
+ }
+ return 0;
+}
+
+# from tcl_strhash.b
+hashfn(key: string, n : int): int
+{
+ h := 0;
+ for(i := 0; i < len key; i++){
+ h = 10*h + key[i];
+ h = h%n;
+ }
+ return h%n;
+}
+
+hashadd(ht: array of list of string, nm: string): int
+{
+ idx := hashfn(nm, len ht);
+ for (ent := ht[idx]; ent != nil; ent = tl ent)
+ if (hd ent == nm)
+ return 0;
+ ht[idx] = nm :: ht[idx];
+ return 1;
+}