|  | #include <u.h> | 
|  | #include <libc.h> | 
|  |  | 
|  | extern	vlong	du(char*, Dir*); | 
|  | extern	vlong	k(vlong); | 
|  | extern	void	err(char*); | 
|  | extern	int	warn(char*); | 
|  | extern	int	seen(Dir*); | 
|  |  | 
|  | int	aflag; | 
|  | int	fflag; | 
|  | int	nflag; | 
|  | int	sflag; | 
|  | int	tflag; | 
|  | int	uflag; | 
|  | int	qflag; | 
|  | char	*fmt = "%llud\t%s\n"; | 
|  | vlong	blocksize = 1024LL; | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int i; | 
|  | char *s, *ss; | 
|  |  | 
|  | ARGBEGIN { | 
|  | case 'a':	/* all files */ | 
|  | aflag = 1; | 
|  | break; | 
|  | case 's':	/* only top level */ | 
|  | sflag = 1; | 
|  | break; | 
|  | case 'f':	/* ignore errors */ | 
|  | fflag = 1; | 
|  | break; | 
|  | case 'n':	/* all files, number of bytes */ | 
|  | aflag = 1; | 
|  | nflag = 1; | 
|  | break; | 
|  | case 't':	/* return modified/accessed time */ | 
|  | tflag = 1; | 
|  | break; | 
|  | case 'u':	/* accessed time */ | 
|  | uflag = 1; | 
|  | break; | 
|  | case 'q':	/* qid */ | 
|  | fmt = "%.16llux\t%s\n"; | 
|  | qflag = 1; | 
|  | break; | 
|  | case 'b':	/* block size */ | 
|  | s = ARGF(); | 
|  | if(s) { | 
|  | blocksize = strtoul(s, &ss, 0); | 
|  | if(s == ss) | 
|  | blocksize = 1; | 
|  | if(*ss == 'k') | 
|  | blocksize *= 1024; | 
|  | } | 
|  | break; | 
|  | } ARGEND | 
|  | if(argc==0) | 
|  | print(fmt, du(".", dirstat(".")), "."); | 
|  | else | 
|  | for(i=0; i<argc; i++) | 
|  | print(fmt, du(argv[i], dirstat(argv[i])), argv[i]); | 
|  | exits(0); | 
|  | } | 
|  |  | 
|  | vlong | 
|  | du(char *name, Dir *dir) | 
|  | { | 
|  | int fd, i, n; | 
|  | Dir *buf, *d; | 
|  | char file[256]; | 
|  | vlong nk, t; | 
|  |  | 
|  | if(dir == nil) | 
|  | return warn(name); | 
|  |  | 
|  | fd = open(name, OREAD); | 
|  | if(fd < 0) | 
|  | return warn(name); | 
|  |  | 
|  | if((dir->qid.type&QTDIR) == 0) | 
|  | nk = k(dir->length); | 
|  | else{ | 
|  | nk = 0; | 
|  | while((n=dirread(fd, &buf)) > 0) { | 
|  | d = buf; | 
|  | for(i=0; i<n; i++, d++) { | 
|  | if((d->qid.type&QTDIR) == 0) { | 
|  | t = k(d->length); | 
|  | nk += t; | 
|  | if(aflag) { | 
|  | sprint(file, "%s/%s", name, d->name); | 
|  | if(tflag) { | 
|  | t = d->mtime; | 
|  | if(uflag) | 
|  | t = d->atime; | 
|  | } | 
|  | if(qflag) | 
|  | t = d->qid.path; | 
|  | print(fmt, t, file); | 
|  | } | 
|  | continue; | 
|  | } | 
|  | if(strcmp(d->name, ".") == 0 || | 
|  | strcmp(d->name, "..") == 0 || | 
|  | seen(d)) | 
|  | continue; | 
|  | sprint(file, "%s/%s", name, d->name); | 
|  | t = du(file, d); | 
|  | nk += t; | 
|  | if(tflag) { | 
|  | t = d->mtime; | 
|  | if(uflag) | 
|  | t = d->atime; | 
|  | } | 
|  | if(qflag) | 
|  | t = d->qid.path; | 
|  | if(!sflag) | 
|  | print(fmt, t, file); | 
|  | } | 
|  | free(buf); | 
|  | } | 
|  | if(n < 0) | 
|  | warn(name); | 
|  | } | 
|  | close(fd); | 
|  | if(tflag) { | 
|  | if(uflag) | 
|  | return dir->atime; | 
|  | return dir->mtime; | 
|  | } | 
|  | if(qflag) | 
|  | return dir->qid.path; | 
|  | return nk; | 
|  | } | 
|  |  | 
|  | #define	NCACHE	128	/* must be power of two */ | 
|  | typedef	struct	Cache	Cache; | 
|  | struct	Cache | 
|  | { | 
|  | Dir*	cache; | 
|  | int	n; | 
|  | int	max; | 
|  | } cache[NCACHE]; | 
|  |  | 
|  | int | 
|  | seen(Dir *dir) | 
|  | { | 
|  | Dir *dp; | 
|  | int i; | 
|  | Cache *c; | 
|  |  | 
|  | c = &cache[dir->qid.path&(NCACHE-1)]; | 
|  | dp = c->cache; | 
|  | for(i=0; i<c->n; i++, dp++) | 
|  | if(dir->qid.path == dp->qid.path && | 
|  | dir->type == dp->type && | 
|  | dir->dev == dp->dev) | 
|  | return 1; | 
|  | if(c->n == c->max){ | 
|  | c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir)); | 
|  | if(c->cache == 0) | 
|  | err("malloc failure"); | 
|  | } | 
|  | c->cache[c->n++] = *dir; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | err(char *s) | 
|  | { | 
|  | fprint(2, "du: %s: %r\n", s); | 
|  | exits(s); | 
|  | } | 
|  |  | 
|  | int | 
|  | warn(char *s) | 
|  | { | 
|  | if(fflag == 0) | 
|  | fprint(2, "du: %s: %r\n", s); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | vlong | 
|  | k(vlong n) | 
|  | { | 
|  | if(nflag) | 
|  | return n; | 
|  | n = (n+blocksize-1)/blocksize; | 
|  | return n*blocksize/1024LL; | 
|  | } |