Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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);
}