333 lines · 5.7 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * du - print disk usage */#include <u.h>#include <libc.h>#include <String.h> extern	vlong	du(char*, Dir*);extern	void	err(char*);extern	vlong	blkmultiple(vlong);extern	int	seen(Dir*);extern	int	warn(char*); enum {	Vkilo = 1024LL,}; /* rounding up, how many units does amt occupy? */#define HOWMANY(amt, unit)	(((amt)+(unit)-1) / (unit))#define ROUNDUP(amt, unit)	(HOWMANY(amt, unit) * (unit)) int	aflag;int	autoscale;int	fflag;int	fltflag;int	qflag;int	readflg;int	sflag;int	tflag;int	uflag; char	*fmt = "%llud\t%q\n";char	*readbuf;vlong	blocksize = Vkilo;	/* actually more likely to be 4K or 8K */vlong	unit;			/* scale factor for output */ static char *pfxes[] = {	/* SI prefixes for units > 1 */	"",	"k", "M", "G",	"T", "P", "E",	"Z", "Y",	nil,}; voidusage(void){	fprint(2, "usage: du [-aefhnqstu] [-b size] [-p si-pfx] [file ...]\n");	exits("usage");} voidprintamt(vlong amt, char *name){	if (readflg)		return;	if (autoscale) {		int scale = 0;		double val = (double)amt/unit; 		while (fabs(val) >= 1024 && scale < nelem(pfxes)-1) {			scale++;			val /= 1024;		}		print("%.6g%s\t%q\n", val, pfxes[scale], name);	} else if (fltflag)		print("%.6g\t%q\n", (double)amt/unit, name);	else		print(fmt, HOWMANY(amt, unit), name);} voidmain(int argc, char *argv[]){	int i, scale;	char *s, *ss, *name; 	doquote = needsrcquote;	quotefmtinstall(); 	ARGBEGIN {	case 'a':	/* all files */		aflag = 1;		break;	case 'b':	/* block size */		s = ARGF();		if(s) {			blocksize = strtoul(s, &ss, 0);			if(s == ss)				blocksize = 1;			while(*ss++ == 'k')				blocksize *= 1024;		}		break;	case 'e':	/* print in %g notation */		fltflag = 1;		break;	case 'f':	/* don't print warnings */		fflag = 1;		break;	case 'h':	/* similar to -h in bsd but more precise */		autoscale = 1;		break;	case 'n':	/* all files, number of bytes */		aflag = 1;		blocksize = 1;		unit = 1;		break;	case 'p':		s = ARGF();		if(s) {			for (scale = 0; pfxes[scale] != nil; scale++)				if (cistrcmp(s, pfxes[scale]) == 0)					break;			if (pfxes[scale] == nil)				sysfatal("unknown suffix %s", s);			unit = 1;			while (scale-- > 0)				unit *= Vkilo;		}		break;	case 'q':	/* qid */		fmt = "%.16llux\t%q\n";		qflag = 1;		break;	case 'r':		/* undocumented: just read & ignore every block of every file */		readflg = 1;		break;	case 's':	/* only top level */		sflag = 1;		break;	case 't':	/* return modified/accessed time */		tflag = 1;		break;	case 'u':	/* accessed time */		uflag = 1;		break;	default:		usage();	} ARGEND 	if (unit == 0)		if (qflag || tflag || uflag || autoscale)			unit = 1;		else			unit = Vkilo;	if (blocksize < 1)		blocksize = 1; 	if (readflg) {		readbuf = malloc(blocksize);		if (readbuf == nil)			sysfatal("out of memory");	}	if(argc==0)		printamt(du(".", dirstat(".")), ".");	else		for(i=0; i<argc; i++) {			name = argv[i];			printamt(du(name, dirstat(name)), name);		}	exits(0);} vlongdirval(Dir *d, vlong size){	if(qflag)		return d->qid.path;	else if(tflag) {		if(uflag)			return d->atime;		return d->mtime;	} else		return size;} voidreadfile(char *name){	int n, fd = open(name, OREAD); 	if(fd < 0) {		warn(name);		return;	}	while ((n = read(fd, readbuf, blocksize)) > 0)		continue;	if (n < 0)		warn(name);	close(fd);} vlongdufile(char *name, Dir *d){	vlong t = blkmultiple(d->length); 	if(aflag || readflg) {		String *file = s_copy(name); 		s_append(file, "/");		s_append(file, d->name);		if (readflg)			readfile(s_to_c(file));		t = dirval(d, t);		printamt(t, s_to_c(file));		s_free(file);	}	return t;} vlongdu(char *name, Dir *dir){	int fd, i, n;	Dir *buf, *d;	String *file;	vlong nk, t; 	if(dir == nil)		return warn(name); 	if((dir->qid.type&QTDIR) == 0)		return dirval(dir, blkmultiple(dir->length)); 	fd = open(name, OREAD);	if(fd < 0)		return warn(name);	nk = 0;	while((n=dirread(fd, &buf)) > 0) {		d = buf;		for(i = n; i > 0; i--, d++) {			if((d->qid.type&QTDIR) == 0) {				nk += dufile(name, d);				continue;			} 			if(strcmp(d->name, ".") == 0 ||			   strcmp(d->name, "..") == 0 ||			   /* !readflg && */ seen(d))				continue;	/* don't get stuck */ 			file = s_copy(name);			s_append(file, "/");			s_append(file, d->name); 			t = du(s_to_c(file), d); 			nk += t;			t = dirval(d, t);			if(!sflag)				printamt(t, s_to_c(file));			s_free(file);		}		free(buf);	}	if(n < 0)		warn(name);	close(fd);	return dirval(dir, nk);} #define	NCACHE	256	/* must be power of two */ typedef struct{	Dir*	cache;	int	n;	int	max;} Cache;Cache cache[NCACHE]; intseen(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){		if (c->max == 0)			c->max = 8;		else			c->max += c->max/2;		c->cache = realloc(c->cache, c->max*sizeof(Dir));		if(c->cache == nil)			err("malloc failure");	}	c->cache[c->n++] = *dir;	return 0;} voiderr(char *s){	fprint(2, "du: %s: %r\n", s);	exits(s);} intwarn(char *s){	if(fflag == 0)		fprint(2, "du: %s: %r\n", s);	return 0;} /* round up n to nearest block */vlongblkmultiple(vlong n){	if(blocksize == 1)		/* no quantization */		return n;	return ROUNDUP(n, blocksize);}