xref: /plan9/sys/src/cmd/du.c (revision a11951932d2253524199a703f676da87a88c9d72)
1 /*
2  * du - print disk usage
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <String.h>
7 
8 extern	vlong	du(char*, Dir*);
9 extern	void	err(char*);
10 extern	vlong	blkmultiple(vlong);
11 extern	int	seen(Dir*);
12 extern	int	warn(char*);
13 
14 enum {
15 	Vkilo = 1024LL,
16 };
17 
18 /* rounding up, how many units does amt occupy? */
19 #define HOWMANY(amt, unit)	(((amt)+(unit)-1) / (unit))
20 #define ROUNDUP(amt, unit)	(HOWMANY(amt, unit) * (unit))
21 
22 int	aflag;
23 int	autoscale;
24 int	fflag;
25 int	fltflag;
26 int	qflag;
27 int	readflg;
28 int	sflag;
29 int	tflag;
30 int	uflag;
31 
32 char	*fmt = "%llud\t%q\n";
33 char	*readbuf;
34 vlong	blocksize = Vkilo;	/* actually more likely to be 4K or 8K */
35 vlong	unit;			/* scale factor for output */
36 
37 static char *pfxes[] = {	/* SI prefixes for units > 1 */
38 	"",
39 	"k", "M", "G",
40 	"T", "P", "E",
41 	"Z", "Y",
42 	nil,
43 };
44 
45 void
usage(void)46 usage(void)
47 {
48 	fprint(2, "usage: du [-aefhnqstu] [-b size] [-p si-pfx] [file ...]\n");
49 	exits("usage");
50 }
51 
52 void
printamt(vlong amt,char * name)53 printamt(vlong amt, char *name)
54 {
55 	if (readflg)
56 		return;
57 	if (autoscale) {
58 		int scale = 0;
59 		double val = (double)amt/unit;
60 
61 		while (fabs(val) >= 1024 && scale < nelem(pfxes)-1) {
62 			scale++;
63 			val /= 1024;
64 		}
65 		print("%.6g%s\t%q\n", val, pfxes[scale], name);
66 	} else if (fltflag)
67 		print("%.6g\t%q\n", (double)amt/unit, name);
68 	else
69 		print(fmt, HOWMANY(amt, unit), name);
70 }
71 
72 void
main(int argc,char * argv[])73 main(int argc, char *argv[])
74 {
75 	int i, scale;
76 	char *s, *ss, *name;
77 
78 	doquote = needsrcquote;
79 	quotefmtinstall();
80 
81 	ARGBEGIN {
82 	case 'a':	/* all files */
83 		aflag = 1;
84 		break;
85 	case 'b':	/* block size */
86 		s = ARGF();
87 		if(s) {
88 			blocksize = strtoul(s, &ss, 0);
89 			if(s == ss)
90 				blocksize = 1;
91 			while(*ss++ == 'k')
92 				blocksize *= 1024;
93 		}
94 		break;
95 	case 'e':	/* print in %g notation */
96 		fltflag = 1;
97 		break;
98 	case 'f':	/* don't print warnings */
99 		fflag = 1;
100 		break;
101 	case 'h':	/* similar to -h in bsd but more precise */
102 		autoscale = 1;
103 		break;
104 	case 'n':	/* all files, number of bytes */
105 		aflag = 1;
106 		blocksize = 1;
107 		unit = 1;
108 		break;
109 	case 'p':
110 		s = ARGF();
111 		if(s) {
112 			for (scale = 0; pfxes[scale] != nil; scale++)
113 				if (cistrcmp(s, pfxes[scale]) == 0)
114 					break;
115 			if (pfxes[scale] == nil)
116 				sysfatal("unknown suffix %s", s);
117 			unit = 1;
118 			while (scale-- > 0)
119 				unit *= Vkilo;
120 		}
121 		break;
122 	case 'q':	/* qid */
123 		fmt = "%.16llux\t%q\n";
124 		qflag = 1;
125 		break;
126 	case 'r':
127 		/* undocumented: just read & ignore every block of every file */
128 		readflg = 1;
129 		break;
130 	case 's':	/* only top level */
131 		sflag = 1;
132 		break;
133 	case 't':	/* return modified/accessed time */
134 		tflag = 1;
135 		break;
136 	case 'u':	/* accessed time */
137 		uflag = 1;
138 		break;
139 	default:
140 		usage();
141 	} ARGEND
142 
143 	if (unit == 0)
144 		if (qflag || tflag || uflag || autoscale)
145 			unit = 1;
146 		else
147 			unit = Vkilo;
148 	if (blocksize < 1)
149 		blocksize = 1;
150 
151 	if (readflg) {
152 		readbuf = malloc(blocksize);
153 		if (readbuf == nil)
154 			sysfatal("out of memory");
155 	}
156 	if(argc==0)
157 		printamt(du(".", dirstat(".")), ".");
158 	else
159 		for(i=0; i<argc; i++) {
160 			name = argv[i];
161 			printamt(du(name, dirstat(name)), name);
162 		}
163 	exits(0);
164 }
165 
166 vlong
dirval(Dir * d,vlong size)167 dirval(Dir *d, vlong size)
168 {
169 	if(qflag)
170 		return d->qid.path;
171 	else if(tflag) {
172 		if(uflag)
173 			return d->atime;
174 		return d->mtime;
175 	} else
176 		return size;
177 }
178 
179 void
readfile(char * name)180 readfile(char *name)
181 {
182 	int n, fd = open(name, OREAD);
183 
184 	if(fd < 0) {
185 		warn(name);
186 		return;
187 	}
188 	while ((n = read(fd, readbuf, blocksize)) > 0)
189 		continue;
190 	if (n < 0)
191 		warn(name);
192 	close(fd);
193 }
194 
195 vlong
dufile(char * name,Dir * d)196 dufile(char *name, Dir *d)
197 {
198 	vlong t = blkmultiple(d->length);
199 
200 	if(aflag || readflg) {
201 		String *file = s_copy(name);
202 
203 		s_append(file, "/");
204 		s_append(file, d->name);
205 		if (readflg)
206 			readfile(s_to_c(file));
207 		t = dirval(d, t);
208 		printamt(t, s_to_c(file));
209 		s_free(file);
210 	}
211 	return t;
212 }
213 
214 vlong
du(char * name,Dir * dir)215 du(char *name, Dir *dir)
216 {
217 	int fd, i, n;
218 	Dir *buf, *d;
219 	String *file;
220 	vlong nk, t;
221 
222 	if(dir == nil)
223 		return warn(name);
224 
225 	if((dir->qid.type&QTDIR) == 0)
226 		return dirval(dir, blkmultiple(dir->length));
227 
228 	fd = open(name, OREAD);
229 	if(fd < 0)
230 		return warn(name);
231 	nk = 0;
232 	while((n=dirread(fd, &buf)) > 0) {
233 		d = buf;
234 		for(i = n; i > 0; i--, d++) {
235 			if((d->qid.type&QTDIR) == 0) {
236 				nk += dufile(name, d);
237 				continue;
238 			}
239 
240 			if(strcmp(d->name, ".") == 0 ||
241 			   strcmp(d->name, "..") == 0 ||
242 			   /* !readflg && */ seen(d))
243 				continue;	/* don't get stuck */
244 
245 			file = s_copy(name);
246 			s_append(file, "/");
247 			s_append(file, d->name);
248 
249 			t = du(s_to_c(file), d);
250 
251 			nk += t;
252 			t = dirval(d, t);
253 			if(!sflag)
254 				printamt(t, s_to_c(file));
255 			s_free(file);
256 		}
257 		free(buf);
258 	}
259 	if(n < 0)
260 		warn(name);
261 	close(fd);
262 	return dirval(dir, nk);
263 }
264 
265 #define	NCACHE	256	/* must be power of two */
266 
267 typedef struct
268 {
269 	Dir*	cache;
270 	int	n;
271 	int	max;
272 } Cache;
273 Cache cache[NCACHE];
274 
275 int
seen(Dir * dir)276 seen(Dir *dir)
277 {
278 	Dir *dp;
279 	int i;
280 	Cache *c;
281 
282 	c = &cache[dir->qid.path&(NCACHE-1)];
283 	dp = c->cache;
284 	for(i=0; i<c->n; i++, dp++)
285 		if(dir->qid.path == dp->qid.path &&
286 		   dir->type == dp->type &&
287 		   dir->dev == dp->dev)
288 			return 1;
289 	if(c->n == c->max){
290 		if (c->max == 0)
291 			c->max = 8;
292 		else
293 			c->max += c->max/2;
294 		c->cache = realloc(c->cache, c->max*sizeof(Dir));
295 		if(c->cache == nil)
296 			err("malloc failure");
297 	}
298 	c->cache[c->n++] = *dir;
299 	return 0;
300 }
301 
302 void
err(char * s)303 err(char *s)
304 {
305 	fprint(2, "du: %s: %r\n", s);
306 	exits(s);
307 }
308 
309 int
warn(char * s)310 warn(char *s)
311 {
312 	if(fflag == 0)
313 		fprint(2, "du: %s: %r\n", s);
314 	return 0;
315 }
316 
317 /* round up n to nearest block */
318 vlong
blkmultiple(vlong n)319 blkmultiple(vlong n)
320 {
321 	if(blocksize == 1)		/* no quantization */
322 		return n;
323 	return ROUNDUP(n, blocksize);
324 }
325