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