summaryrefslogtreecommitdiff
path: root/appl/cmd/du.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/du.b')
-rw-r--r--appl/cmd/du.b163
1 files changed, 163 insertions, 0 deletions
diff --git a/appl/cmd/du.b b/appl/cmd/du.b
new file mode 100644
index 00000000..45b8ee1b
--- /dev/null
+++ b/appl/cmd/du.b
@@ -0,0 +1,163 @@
+implement Du;
+
+include "sys.m";
+ sys: Sys;
+ sprint: import sys;
+include "draw.m";
+include "string.m";
+ strmod: String;
+include "readdir.m";
+ readdir: Readdir;
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+include "arg.m";
+
+aflag := 0; # all files, not just directories
+nflag := 0; # names only (but see -t); implies -a
+sflag := 0; # summary of top level names
+tflag := 0; # use modification time, not size; netlib format if -n also given
+uflag := 0; # use last use (access) time, not size
+blocksize := big 1024; # quantise length to this block size (still displayed in kb)
+bout: ref Iobuf;
+
+Du: module
+{
+ init: fn(nil: ref Draw->Context, arg: list of string);
+};
+
+kb(b: big): big
+{
+ return (((b + blocksize - big 1)/blocksize)*blocksize)/big 1024;
+}
+
+report(name: string, mtime: int, atime: int, l: big, chksum: int)
+{
+ t := mtime;
+ if(uflag)
+ t = atime;
+ if(nflag){
+ if(tflag)
+ bout.puts(sprint("%q %ud %bd %d\n", name, t, l, chksum));
+ else
+ bout.puts(sprint("%q\n", name));
+ }else{
+ if(tflag)
+ bout.puts(sprint("%ud %q\n", t, name));
+ else
+ bout.puts(sprint("%-4bd %q\n", kb(l), name));
+ }
+}
+
+# Avoid loops in tangled namespaces.
+NCACHE: con 1024; # must be power of two
+cache := array[NCACHE] of list of ref sys->Dir;
+
+seen(dir: ref sys->Dir): int
+{
+ h := int dir.qid.path & (NCACHE-1);
+ for(c := cache[h]; c!=nil; c = tl c){
+ t := hd c;
+ if(dir.qid.path==t.qid.path && dir.dtype==t.dtype && dir.dev==t.dev)
+ return 1;
+ }
+ cache[h] = dir :: cache[h];
+ return 0;
+}
+
+dir(dirname: string): big
+{
+ prefix := dirname+"/";
+ if(dirname==".")
+ prefix = nil;
+ sum := big 0;
+ (de, nde) := readdir->init(dirname, readdir->NAME);
+ if(nde < 0)
+ warn("can't read", dirname);
+ for(i := 0; i < nde; i++) {
+ s := prefix+de[i].name;
+ if(de[i].mode & Sys->DMDIR){
+ if(!seen(de[i])){ # arguably should apply to files as well
+ size := dir(s);
+ sum += size;
+ if(!sflag && !nflag)
+ report(s, de[i].mtime, de[i].atime, size, 0);
+ }
+ }else{
+ l := de[i].length;
+ sum += l;
+ if(aflag)
+ report(s, de[i].mtime, de[i].atime, l, 0);
+ }
+ }
+ return sum;
+}
+
+du(name: string)
+{
+ (rc, d) := sys->stat(name);
+ if(rc < 0){
+ warn("can't stat", name);
+ return;
+ }
+ if(d.mode & Sys->DMDIR){
+ d.length = dir(name);
+ if(nflag && !sflag)
+ return;
+ }
+ report(name, d.mtime, d.atime, d.length, 0);
+}
+
+warn(why: string, f: string)
+{
+ sys->fprint(sys->fildes(2), "du: %s %q: %r\n", why, f);
+}
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ bufio = load Bufio Bufio->PATH;
+ strmod = load String String->PATH;
+ readdir = load Readdir Readdir->PATH;
+ arg := load Arg Arg->PATH;
+ if(arg == nil || bufio==nil || arg==nil || readdir==nil || readdir==nil){
+ sys->fprint(sys->fildes(2), "du: load Error: %r\n");
+ raise "fail:can't load";
+ }
+ sys->pctl(Sys->FORKFD, nil);
+ bout = bufio->fopen(sys->fildes(1), bufio->OWRITE);
+ arg->init(args);
+ arg->setusage("du [-anstu] [-b bsize] [file ...]");
+ while((o := arg->opt()) != 0)
+ case o {
+ 'a' =>
+ aflag = 1;
+ 'b' =>
+ s := arg->earg();
+ blocksize = big s;
+ if(len s > 0 && s[len s-1] == 'k')
+ blocksize *= big 1024;
+ if(blocksize <= big 0)
+ blocksize = big 1;
+ 'n' =>
+ nflag = 1;
+ aflag = 1;
+ 's' =>
+ sflag = 1;
+ 't' =>
+ tflag = 1;
+ 'u' =>
+ uflag = 1;
+ tflag = 1;
+ * =>
+ arg->usage();
+ }
+ args = arg->argv();
+ arg = nil;
+
+ if(args==nil)
+ args = "." :: nil;
+ for(; args!=nil; args = tl args)
+ du(hd args);
+ bout.close();
+}