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