]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/du.c
etheriwl: don't break controller on command flush timeout
[plan9front.git] / sys / src / cmd / du.c
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
46 usage(void)
47 {
48         fprint(2, "usage: du [-aefhnqstu] [-b size] [-p si-pfx] [file ...]\n");
49         exits("usage");
50 }
51
52 void
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
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
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
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
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
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
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
303 err(char *s)
304 {
305         fprint(2, "du: %s: %r\n", s);
306         exits(s);
307 }
308
309 int
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
319 blkmultiple(vlong n)
320 {
321         if(blocksize == 1)              /* no quantization */
322                 return n;
323         return ROUNDUP(n, blocksize);
324 }