diff options
Diffstat (limited to 'appl/cmd/du.b')
| -rw-r--r-- | appl/cmd/du.b | 163 |
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(); +} |
