du.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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 }