xref: /plan9/sys/src/cmd/du.c (revision a11951932d2253524199a703f676da87a88c9d72)
16c54378cSDavid du Colombier /*
26c54378cSDavid du Colombier  * du - print disk usage
36c54378cSDavid du Colombier  */
43e12c5d1SDavid du Colombier #include <u.h>
53e12c5d1SDavid du Colombier #include <libc.h>
64559052fSDavid du Colombier #include <String.h>
73e12c5d1SDavid du Colombier 
89a747e4fSDavid du Colombier extern	vlong	du(char*, Dir*);
97dd7cddfSDavid du Colombier extern	void	err(char*);
106c54378cSDavid du Colombier extern	vlong	blkmultiple(vlong);
117dd7cddfSDavid du Colombier extern	int	seen(Dir*);
126c54378cSDavid du Colombier extern	int	warn(char*);
136c54378cSDavid du Colombier 
146c54378cSDavid du Colombier enum {
156c54378cSDavid du Colombier 	Vkilo = 1024LL,
166c54378cSDavid du Colombier };
176c54378cSDavid du Colombier 
186c54378cSDavid du Colombier /* rounding up, how many units does amt occupy? */
196c54378cSDavid du Colombier #define HOWMANY(amt, unit)	(((amt)+(unit)-1) / (unit))
206c54378cSDavid du Colombier #define ROUNDUP(amt, unit)	(HOWMANY(amt, unit) * (unit))
213e12c5d1SDavid du Colombier 
227dd7cddfSDavid du Colombier int	aflag;
236c54378cSDavid du Colombier int	autoscale;
247dd7cddfSDavid du Colombier int	fflag;
256c54378cSDavid du Colombier int	fltflag;
266c54378cSDavid du Colombier int	qflag;
276c54378cSDavid du Colombier int	readflg;
287dd7cddfSDavid du Colombier int	sflag;
297dd7cddfSDavid du Colombier int	tflag;
307dd7cddfSDavid du Colombier int	uflag;
316c54378cSDavid du Colombier 
32e288d156SDavid du Colombier char	*fmt = "%llud\t%q\n";
336c54378cSDavid du Colombier char	*readbuf;
346c54378cSDavid du Colombier vlong	blocksize = Vkilo;	/* actually more likely to be 4K or 8K */
356c54378cSDavid du Colombier vlong	unit;			/* scale factor for output */
366c54378cSDavid du Colombier 
376c54378cSDavid du Colombier static char *pfxes[] = {	/* SI prefixes for units > 1 */
386c54378cSDavid du Colombier 	"",
396c54378cSDavid du Colombier 	"k", "M", "G",
406c54378cSDavid du Colombier 	"T", "P", "E",
416c54378cSDavid du Colombier 	"Z", "Y",
426c54378cSDavid du Colombier 	nil,
436c54378cSDavid du Colombier };
443e12c5d1SDavid du Colombier 
453e12c5d1SDavid du Colombier void
usage(void)460cc39a83SDavid du Colombier usage(void)
470cc39a83SDavid du Colombier {
486c54378cSDavid du Colombier 	fprint(2, "usage: du [-aefhnqstu] [-b size] [-p si-pfx] [file ...]\n");
490cc39a83SDavid du Colombier 	exits("usage");
500cc39a83SDavid du Colombier }
510cc39a83SDavid du Colombier 
520cc39a83SDavid du Colombier void
printamt(vlong amt,char * name)536c54378cSDavid du Colombier printamt(vlong amt, char *name)
546c54378cSDavid du Colombier {
556c54378cSDavid du Colombier 	if (readflg)
566c54378cSDavid du Colombier 		return;
576c54378cSDavid du Colombier 	if (autoscale) {
586c54378cSDavid du Colombier 		int scale = 0;
596c54378cSDavid du Colombier 		double val = (double)amt/unit;
606c54378cSDavid du Colombier 
616c54378cSDavid du Colombier 		while (fabs(val) >= 1024 && scale < nelem(pfxes)-1) {
626c54378cSDavid du Colombier 			scale++;
636c54378cSDavid du Colombier 			val /= 1024;
646c54378cSDavid du Colombier 		}
656c54378cSDavid du Colombier 		print("%.6g%s\t%q\n", val, pfxes[scale], name);
666c54378cSDavid du Colombier 	} else if (fltflag)
676c54378cSDavid du Colombier 		print("%.6g\t%q\n", (double)amt/unit, name);
686c54378cSDavid du Colombier 	else
696c54378cSDavid du Colombier 		print(fmt, HOWMANY(amt, unit), name);
706c54378cSDavid du Colombier }
716c54378cSDavid du Colombier 
726c54378cSDavid du Colombier void
main(int argc,char * argv[])733e12c5d1SDavid du Colombier main(int argc, char *argv[])
743e12c5d1SDavid du Colombier {
756c54378cSDavid du Colombier 	int i, scale;
766c54378cSDavid du Colombier 	char *s, *ss, *name;
773e12c5d1SDavid du Colombier 
78e288d156SDavid du Colombier 	doquote = needsrcquote;
79e288d156SDavid du Colombier 	quotefmtinstall();
80e288d156SDavid du Colombier 
813e12c5d1SDavid du Colombier 	ARGBEGIN {
827dd7cddfSDavid du Colombier 	case 'a':	/* all files */
837dd7cddfSDavid du Colombier 		aflag = 1;
847dd7cddfSDavid du Colombier 		break;
856c54378cSDavid du Colombier 	case 'b':	/* block size */
866c54378cSDavid du Colombier 		s = ARGF();
876c54378cSDavid du Colombier 		if(s) {
886c54378cSDavid du Colombier 			blocksize = strtoul(s, &ss, 0);
896c54378cSDavid du Colombier 			if(s == ss)
906c54378cSDavid du Colombier 				blocksize = 1;
91*a1195193SDavid du Colombier 			while(*ss++ == 'k')
926c54378cSDavid du Colombier 				blocksize *= 1024;
936c54378cSDavid du Colombier 		}
947dd7cddfSDavid du Colombier 		break;
956c54378cSDavid du Colombier 	case 'e':	/* print in %g notation */
966c54378cSDavid du Colombier 		fltflag = 1;
976c54378cSDavid du Colombier 		break;
986c54378cSDavid du Colombier 	case 'f':	/* don't print warnings */
997dd7cddfSDavid du Colombier 		fflag = 1;
1007dd7cddfSDavid du Colombier 		break;
1016c54378cSDavid du Colombier 	case 'h':	/* similar to -h in bsd but more precise */
1026c54378cSDavid du Colombier 		autoscale = 1;
1036c54378cSDavid du Colombier 		break;
1047dd7cddfSDavid du Colombier 	case 'n':	/* all files, number of bytes */
1057dd7cddfSDavid du Colombier 		aflag = 1;
1066c54378cSDavid du Colombier 		blocksize = 1;
1076c54378cSDavid du Colombier 		unit = 1;
1086c54378cSDavid du Colombier 		break;
1096c54378cSDavid du Colombier 	case 'p':
1106c54378cSDavid du Colombier 		s = ARGF();
1116c54378cSDavid du Colombier 		if(s) {
1126c54378cSDavid du Colombier 			for (scale = 0; pfxes[scale] != nil; scale++)
1136c54378cSDavid du Colombier 				if (cistrcmp(s, pfxes[scale]) == 0)
1146c54378cSDavid du Colombier 					break;
1156c54378cSDavid du Colombier 			if (pfxes[scale] == nil)
1166c54378cSDavid du Colombier 				sysfatal("unknown suffix %s", s);
1176c54378cSDavid du Colombier 			unit = 1;
1186c54378cSDavid du Colombier 			while (scale-- > 0)
1196c54378cSDavid du Colombier 				unit *= Vkilo;
1206c54378cSDavid du Colombier 		}
1216c54378cSDavid du Colombier 		break;
1226c54378cSDavid du Colombier 	case 'q':	/* qid */
1236c54378cSDavid du Colombier 		fmt = "%.16llux\t%q\n";
1246c54378cSDavid du Colombier 		qflag = 1;
1256c54378cSDavid du Colombier 		break;
1266c54378cSDavid du Colombier 	case 'r':
1276c54378cSDavid du Colombier 		/* undocumented: just read & ignore every block of every file */
1286c54378cSDavid du Colombier 		readflg = 1;
1296c54378cSDavid du Colombier 		break;
1306c54378cSDavid du Colombier 	case 's':	/* only top level */
1316c54378cSDavid du Colombier 		sflag = 1;
1327dd7cddfSDavid du Colombier 		break;
1337dd7cddfSDavid du Colombier 	case 't':	/* return modified/accessed time */
1347dd7cddfSDavid du Colombier 		tflag = 1;
1357dd7cddfSDavid du Colombier 		break;
1367dd7cddfSDavid du Colombier 	case 'u':	/* accessed time */
1377dd7cddfSDavid du Colombier 		uflag = 1;
1387dd7cddfSDavid du Colombier 		break;
1390cc39a83SDavid du Colombier 	default:
1400cc39a83SDavid du Colombier 		usage();
1413e12c5d1SDavid du Colombier 	} ARGEND
1426c54378cSDavid du Colombier 
1436c54378cSDavid du Colombier 	if (unit == 0)
1446c54378cSDavid du Colombier 		if (qflag || tflag || uflag || autoscale)
1456c54378cSDavid du Colombier 			unit = 1;
1463e12c5d1SDavid du Colombier 		else
1476c54378cSDavid du Colombier 			unit = Vkilo;
1486c54378cSDavid du Colombier 	if (blocksize < 1)
1496c54378cSDavid du Colombier 		blocksize = 1;
1506c54378cSDavid du Colombier 
1516c54378cSDavid du Colombier 	if (readflg) {
1526c54378cSDavid du Colombier 		readbuf = malloc(blocksize);
1536c54378cSDavid du Colombier 		if (readbuf == nil)
1546c54378cSDavid du Colombier 			sysfatal("out of memory");
1556c54378cSDavid du Colombier 	}
1566c54378cSDavid du Colombier 	if(argc==0)
1576c54378cSDavid du Colombier 		printamt(du(".", dirstat(".")), ".");
1586c54378cSDavid du Colombier 	else
1596c54378cSDavid du Colombier 		for(i=0; i<argc; i++) {
1606c54378cSDavid du Colombier 			name = argv[i];
1616c54378cSDavid du Colombier 			printamt(du(name, dirstat(name)), name);
1626c54378cSDavid du Colombier 		}
163bd389b36SDavid du Colombier 	exits(0);
1643e12c5d1SDavid du Colombier }
1653e12c5d1SDavid du Colombier 
1669a747e4fSDavid du Colombier vlong
dirval(Dir * d,vlong size)1676c54378cSDavid du Colombier dirval(Dir *d, vlong size)
1686c54378cSDavid du Colombier {
1696c54378cSDavid du Colombier 	if(qflag)
1706c54378cSDavid du Colombier 		return d->qid.path;
1716c54378cSDavid du Colombier 	else if(tflag) {
1726c54378cSDavid du Colombier 		if(uflag)
1736c54378cSDavid du Colombier 			return d->atime;
1746c54378cSDavid du Colombier 		return d->mtime;
1756c54378cSDavid du Colombier 	} else
1766c54378cSDavid du Colombier 		return size;
1776c54378cSDavid du Colombier }
1786c54378cSDavid du Colombier 
1796c54378cSDavid du Colombier void
readfile(char * name)1806c54378cSDavid du Colombier readfile(char *name)
1816c54378cSDavid du Colombier {
1826c54378cSDavid du Colombier 	int n, fd = open(name, OREAD);
1836c54378cSDavid du Colombier 
1846c54378cSDavid du Colombier 	if(fd < 0) {
1856c54378cSDavid du Colombier 		warn(name);
1866c54378cSDavid du Colombier 		return;
1876c54378cSDavid du Colombier 	}
1886c54378cSDavid du Colombier 	while ((n = read(fd, readbuf, blocksize)) > 0)
1896c54378cSDavid du Colombier 		continue;
1906c54378cSDavid du Colombier 	if (n < 0)
1916c54378cSDavid du Colombier 		warn(name);
1926c54378cSDavid du Colombier 	close(fd);
1936c54378cSDavid du Colombier }
1946c54378cSDavid du Colombier 
1956c54378cSDavid du Colombier vlong
dufile(char * name,Dir * d)1966c54378cSDavid du Colombier dufile(char *name, Dir *d)
1976c54378cSDavid du Colombier {
1986c54378cSDavid du Colombier 	vlong t = blkmultiple(d->length);
1996c54378cSDavid du Colombier 
2006c54378cSDavid du Colombier 	if(aflag || readflg) {
2016c54378cSDavid du Colombier 		String *file = s_copy(name);
2026c54378cSDavid du Colombier 
2036c54378cSDavid du Colombier 		s_append(file, "/");
2046c54378cSDavid du Colombier 		s_append(file, d->name);
2056c54378cSDavid du Colombier 		if (readflg)
2066c54378cSDavid du Colombier 			readfile(s_to_c(file));
2076c54378cSDavid du Colombier 		t = dirval(d, t);
2086c54378cSDavid du Colombier 		printamt(t, s_to_c(file));
2096c54378cSDavid du Colombier 		s_free(file);
2106c54378cSDavid du Colombier 	}
2116c54378cSDavid du Colombier 	return t;
2126c54378cSDavid du Colombier }
2136c54378cSDavid du Colombier 
2146c54378cSDavid du Colombier vlong
du(char * name,Dir * dir)2159a747e4fSDavid du Colombier du(char *name, Dir *dir)
2163e12c5d1SDavid du Colombier {
2173e12c5d1SDavid du Colombier 	int fd, i, n;
2189a747e4fSDavid du Colombier 	Dir *buf, *d;
2194559052fSDavid du Colombier 	String *file;
2209a747e4fSDavid du Colombier 	vlong nk, t;
2213e12c5d1SDavid du Colombier 
2229a747e4fSDavid du Colombier 	if(dir == nil)
2233e12c5d1SDavid du Colombier 		return warn(name);
2249a747e4fSDavid du Colombier 
2256c54378cSDavid du Colombier 	if((dir->qid.type&QTDIR) == 0)
2266c54378cSDavid du Colombier 		return dirval(dir, blkmultiple(dir->length));
2276c54378cSDavid du Colombier 
2283e12c5d1SDavid du Colombier 	fd = open(name, OREAD);
2293e12c5d1SDavid du Colombier 	if(fd < 0)
2303e12c5d1SDavid du Colombier 		return warn(name);
2313e12c5d1SDavid du Colombier 	nk = 0;
2329a747e4fSDavid du Colombier 	while((n=dirread(fd, &buf)) > 0) {
2339a747e4fSDavid du Colombier 		d = buf;
2346c54378cSDavid du Colombier 		for(i = n; i > 0; i--, d++) {
2359a747e4fSDavid du Colombier 			if((d->qid.type&QTDIR) == 0) {
2366c54378cSDavid du Colombier 				nk += dufile(name, d);
2373e12c5d1SDavid du Colombier 				continue;
2383e12c5d1SDavid du Colombier 			}
2396c54378cSDavid du Colombier 
2409a747e4fSDavid du Colombier 			if(strcmp(d->name, ".") == 0 ||
2419a747e4fSDavid du Colombier 			   strcmp(d->name, "..") == 0 ||
2426c54378cSDavid du Colombier 			   /* !readflg && */ seen(d))
2436c54378cSDavid du Colombier 				continue;	/* don't get stuck */
2446c54378cSDavid du Colombier 
2454559052fSDavid du Colombier 			file = s_copy(name);
2464559052fSDavid du Colombier 			s_append(file, "/");
2474559052fSDavid du Colombier 			s_append(file, d->name);
2486c54378cSDavid du Colombier 
2494559052fSDavid du Colombier 			t = du(s_to_c(file), d);
2506c54378cSDavid du Colombier 
2513e12c5d1SDavid du Colombier 			nk += t;
2526c54378cSDavid du Colombier 			t = dirval(d, t);
2539a747e4fSDavid du Colombier 			if(!sflag)
2546c54378cSDavid du Colombier 				printamt(t, s_to_c(file));
2554559052fSDavid du Colombier 			s_free(file);
2563e12c5d1SDavid du Colombier 		}
2579a747e4fSDavid du Colombier 		free(buf);
2583e12c5d1SDavid du Colombier 	}
2593e12c5d1SDavid du Colombier 	if(n < 0)
2607dd7cddfSDavid du Colombier 		warn(name);
2613e12c5d1SDavid du Colombier 	close(fd);
2626c54378cSDavid du Colombier 	return dirval(dir, nk);
2633e12c5d1SDavid du Colombier }
2643e12c5d1SDavid du Colombier 
2656c54378cSDavid du Colombier #define	NCACHE	256	/* must be power of two */
2666c54378cSDavid du Colombier 
2676c54378cSDavid du Colombier typedef struct
268219b2ee8SDavid du Colombier {
269219b2ee8SDavid du Colombier 	Dir*	cache;
270219b2ee8SDavid du Colombier 	int	n;
271219b2ee8SDavid du Colombier 	int	max;
2726c54378cSDavid du Colombier } Cache;
2736c54378cSDavid du Colombier Cache cache[NCACHE];
274219b2ee8SDavid du Colombier 
2753e12c5d1SDavid du Colombier int
seen(Dir * dir)2763e12c5d1SDavid du Colombier seen(Dir *dir)
2773e12c5d1SDavid du Colombier {
278bd389b36SDavid du Colombier 	Dir *dp;
2793e12c5d1SDavid du Colombier 	int i;
2807dd7cddfSDavid du Colombier 	Cache *c;
2813e12c5d1SDavid du Colombier 
282219b2ee8SDavid du Colombier 	c = &cache[dir->qid.path&(NCACHE-1)];
283219b2ee8SDavid du Colombier 	dp = c->cache;
284219b2ee8SDavid du Colombier 	for(i=0; i<c->n; i++, dp++)
2853e12c5d1SDavid du Colombier 		if(dir->qid.path == dp->qid.path &&
2867dd7cddfSDavid du Colombier 		   dir->type == dp->type &&
2877dd7cddfSDavid du Colombier 		   dir->dev == dp->dev)
2883e12c5d1SDavid du Colombier 			return 1;
289219b2ee8SDavid du Colombier 	if(c->n == c->max){
2906c54378cSDavid du Colombier 		if (c->max == 0)
2916c54378cSDavid du Colombier 			c->max = 8;
2926c54378cSDavid du Colombier 		else
2936c54378cSDavid du Colombier 			c->max += c->max/2;
2946c54378cSDavid du Colombier 		c->cache = realloc(c->cache, c->max*sizeof(Dir));
2956c54378cSDavid du Colombier 		if(c->cache == nil)
2963e12c5d1SDavid du Colombier 			err("malloc failure");
2973e12c5d1SDavid du Colombier 	}
298219b2ee8SDavid du Colombier 	c->cache[c->n++] = *dir;
2993e12c5d1SDavid du Colombier 	return 0;
3003e12c5d1SDavid du Colombier }
3013e12c5d1SDavid du Colombier 
3023e12c5d1SDavid du Colombier void
err(char * s)3033e12c5d1SDavid du Colombier err(char *s)
3043e12c5d1SDavid du Colombier {
3059a747e4fSDavid du Colombier 	fprint(2, "du: %s: %r\n", s);
3063e12c5d1SDavid du Colombier 	exits(s);
3073e12c5d1SDavid du Colombier }
3083e12c5d1SDavid du Colombier 
3093e12c5d1SDavid du Colombier int
warn(char * s)3103e12c5d1SDavid du Colombier warn(char *s)
3113e12c5d1SDavid du Colombier {
3129a747e4fSDavid du Colombier 	if(fflag == 0)
3139a747e4fSDavid du Colombier 		fprint(2, "du: %s: %r\n", s);
3143e12c5d1SDavid du Colombier 	return 0;
3153e12c5d1SDavid du Colombier }
3163e12c5d1SDavid du Colombier 
3176c54378cSDavid du Colombier /* round up n to nearest block */
3189a747e4fSDavid du Colombier vlong
blkmultiple(vlong n)3196c54378cSDavid du Colombier blkmultiple(vlong n)
3203e12c5d1SDavid du Colombier {
3216c54378cSDavid du Colombier 	if(blocksize == 1)		/* no quantization */
3227dd7cddfSDavid du Colombier 		return n;
3236c54378cSDavid du Colombier 	return ROUNDUP(n, blocksize);
3243e12c5d1SDavid du Colombier }
325