sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

du.c (2902B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/stat.h>
      3 #include <sys/types.h>
      4 
      5 #include <errno.h>
      6 #include <fcntl.h>
      7 #include <limits.h>
      8 #include <search.h>
      9 #include <stdint.h>
     10 #include <stdlib.h>
     11 #include <stdio.h>
     12 #include <unistd.h>
     13 
     14 #include "fs.h"
     15 #include "util.h"
     16 
     17 static size_t maxdepth = SIZE_MAX;
     18 static size_t blksize  = 512;
     19 
     20 static int aflag = 0;
     21 static int sflag = 0;
     22 static int hflag = 0;
     23 
     24 struct file {
     25 	dev_t devno;
     26 	ino_t inode;
     27 };
     28 
     29 static void
     30 printpath(off_t n, const char *path)
     31 {
     32 	if (hflag)
     33 		printf("%s\t%s\n", humansize(n * blksize), path);
     34 	else
     35 		printf("%jd\t%s\n", (intmax_t)n, path);
     36 }
     37 
     38 static off_t
     39 nblks(blkcnt_t blocks)
     40 {
     41 	return (512 * blocks + blksize - 1) / blksize;
     42 }
     43 
     44 static int
     45 cmp(const void *p1, const void *p2)
     46 {
     47 	const struct file *f1 = p1, *f2 = p2;
     48 
     49 	if (f1->devno > f2->devno)
     50 		return -1;
     51 	if (f1->devno < f2->devno)
     52 		return 1;
     53 
     54 	/* f1->devno == f2->devno */
     55 	if (f1->inode < f2->inode)
     56 		return -1;
     57 	if (f1->inode > f2->inode)
     58 		return 1;
     59 
     60 	return 0;
     61 }
     62 
     63 static int
     64 duplicated(dev_t dev, ino_t ino)
     65 {
     66 	static void *tree;
     67 	struct file **fpp, *fp, file = {dev, ino};
     68 
     69 	if ((fpp = tsearch(&file, &tree, cmp)) == NULL)
     70 		eprintf("%s:", argv0);
     71 
     72 	if (*fpp != &file)
     73 		return 1;
     74 
     75 	/* new file added */
     76 	fp = emalloc(sizeof(*fp));
     77 	*fp = file;
     78 	*fpp = fp;
     79 
     80 	return 0;
     81 }
     82 
     83 static void
     84 du(int dirfd, const char *path, struct stat *st, void *data, struct recursor *r)
     85 {
     86 	off_t *total = data, subtotal;
     87 
     88 	subtotal = nblks(st->st_blocks);
     89 	if (S_ISDIR(st->st_mode)) {
     90 		recurse(dirfd, path, &subtotal, r);
     91 	} else if (r->follow != 'P' || st->st_nlink > 1) {
     92 		if (duplicated(st->st_dev, st->st_ino))
     93 			goto print;
     94 	}
     95 
     96 	*total += subtotal;
     97 
     98 print:
     99 	if (!r->depth)
    100 		printpath(*total, r->path);
    101 	else if (!sflag && r->depth <= maxdepth && (S_ISDIR(st->st_mode) || aflag))
    102 		printpath(subtotal, r->path);
    103 }
    104 
    105 static void
    106 usage(void)
    107 {
    108 	eprintf("usage: %s [-a | -s] [-d depth] [-h] [-k] [-H | -L | -P] [-x] [file ...]\n", argv0);
    109 }
    110 
    111 int
    112 main(int argc, char *argv[])
    113 {
    114 	struct recursor r = { .fn = du, .follow = 'P' };
    115 	off_t n = 0;
    116 	int kflag = 0, dflag = 0;
    117 	char *bsize;
    118 
    119 	ARGBEGIN {
    120 	case 'a':
    121 		aflag = 1;
    122 		break;
    123 	case 'd':
    124 		dflag = 1;
    125 		maxdepth = estrtonum(EARGF(usage()), 0, MIN(LLONG_MAX, SIZE_MAX));
    126 		break;
    127 	case 'h':
    128 		hflag = 1;
    129 		break;
    130 	case 'k':
    131 		kflag = 1;
    132 		break;
    133 	case 's':
    134 		sflag = 1;
    135 		break;
    136 	case 'x':
    137 		r.flags |= SAMEDEV;
    138 		break;
    139 	case 'H':
    140 	case 'L':
    141 	case 'P':
    142 		r.follow = ARGC();
    143 		break;
    144 	default:
    145 		usage();
    146 	} ARGEND
    147 
    148 	if ((aflag && sflag) || (dflag && sflag))
    149 		usage();
    150 
    151 	bsize = getenv("BLOCKSIZE");
    152 	if (bsize)
    153 		blksize = estrtonum(bsize, 1, MIN(LLONG_MAX, SIZE_MAX));
    154 	if (kflag)
    155 		blksize = 1024;
    156 
    157 	if (!argc) {
    158 		recurse(AT_FDCWD, ".", &n, &r);
    159 	} else {
    160 		for (; *argv; argc--, argv++) {
    161 			n = 0;
    162 			recurse(AT_FDCWD, *argv, &n, &r);
    163 		}
    164 	}
    165 
    166 	return fshut(stdout, "<stdout>") || recurse_status;
    167 }