325 lines
5.3 KiB
C
325 lines
5.3 KiB
C
/*
|
|
* 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,
|
|
};
|
|
|
|
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; i<argc; i++) {
|
|
name = argv[i];
|
|
printamt(du(name, dirstat(name)), name);
|
|
}
|
|
exits(0);
|
|
}
|
|
|
|
vlong
|
|
dirval(Dir *d, vlong size)
|
|
{
|
|
if(qflag)
|
|
return d->qid.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; 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;
|
|
}
|
|
|
|
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);
|
|
}
|