du.c 5.3 KB

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