/* * du - print disk usage */ #include #include #include 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, }; void usage(void) { fprint(2, "usage: du [-aefhnqstu] [-b size] [-p si-pfx] [file ...]\n"); exits("usage"); } void printamt(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); } void main(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; iqid.path; else if(tflag) { if(uflag) return d->atime; return d->mtime; } else return size; } void readfile(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); } vlong dufile(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; } vlong du(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]; int seen(Dir *dir) { Dir *dp; int i; Cache *c; c = &cache[dir->qid.path&(NCACHE-1)]; dp = c->cache; for(i=0; in; 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; } 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; } /* round up n to nearest block */ vlong blkmultiple(vlong n) { if(blocksize == 1) /* no quantization */ return n; return ROUNDUP(n, blocksize); }