| #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; | 
 | } |