10f66f451Sopenharmony_ci/* du.c - disk usage program.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2012 Ashwini Kumar <ak.ashwini@gmail.com>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * See http://opengroup.org/onlinepubs/9699919799/utilities/du.html
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * TODO: cleanup
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciUSE_DU(NEWTOY(du, "d#<0=-1hmlcaHkKLsx[-HL][-kKmh]", TOYFLAG_USR|TOYFLAG_BIN))
100f66f451Sopenharmony_ci
110f66f451Sopenharmony_ciconfig DU
120f66f451Sopenharmony_ci  bool "du"
130f66f451Sopenharmony_ci  default y
140f66f451Sopenharmony_ci  help
150f66f451Sopenharmony_ci    usage: du [-kKmh] [file...]
160f66f451Sopenharmony_ci
170f66f451Sopenharmony_ci    Show disk usage, space consumed by files.
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci    Size in:
200f66f451Sopenharmony_ci    -k	1024 byte blocks (default)
210f66f451Sopenharmony_ci    -K	512 byte blocks (posix)
220f66f451Sopenharmony_ci    -m	Megabytes
230f66f451Sopenharmony_ci    -h	Human readable (e.g., 1K 243M 2G)
240f66f451Sopenharmony_ci*/
250f66f451Sopenharmony_ci
260f66f451Sopenharmony_ci#define FOR_du
270f66f451Sopenharmony_ci#include "toys.h"
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_ciGLOBALS(
300f66f451Sopenharmony_ci  long d;
310f66f451Sopenharmony_ci
320f66f451Sopenharmony_ci  unsigned long depth, total;
330f66f451Sopenharmony_ci  dev_t st_dev;
340f66f451Sopenharmony_ci  void *inodes;
350f66f451Sopenharmony_ci)
360f66f451Sopenharmony_ci
370f66f451Sopenharmony_citypedef struct node_size {
380f66f451Sopenharmony_ci  struct dirtree *node;
390f66f451Sopenharmony_ci  long size;
400f66f451Sopenharmony_ci} node_size;
410f66f451Sopenharmony_ci
420f66f451Sopenharmony_ci// Print the size and name, given size in bytes
430f66f451Sopenharmony_cistatic void print(long long size, struct dirtree *node)
440f66f451Sopenharmony_ci{
450f66f451Sopenharmony_ci  char *name = "total";
460f66f451Sopenharmony_ci
470f66f451Sopenharmony_ci  if (TT.depth > TT.d) return;
480f66f451Sopenharmony_ci
490f66f451Sopenharmony_ci  if (toys.optflags & FLAG_h) {
500f66f451Sopenharmony_ci    human_readable(toybuf, size, 0);
510f66f451Sopenharmony_ci    printf("%s", toybuf);
520f66f451Sopenharmony_ci  } else {
530f66f451Sopenharmony_ci    int bits = 10;
540f66f451Sopenharmony_ci
550f66f451Sopenharmony_ci    if (toys.optflags & FLAG_K) bits = 9;
560f66f451Sopenharmony_ci    else if (toys.optflags & FLAG_m) bits = 20;
570f66f451Sopenharmony_ci
580f66f451Sopenharmony_ci    printf("%llu", (size>>bits)+!!(size&((1<<bits)-1)));
590f66f451Sopenharmony_ci  }
600f66f451Sopenharmony_ci  if (node) name = dirtree_path(node, NULL);
610f66f451Sopenharmony_ci  xprintf("\t%s\n", name);
620f66f451Sopenharmony_ci  if (node) free(name);
630f66f451Sopenharmony_ci}
640f66f451Sopenharmony_ci
650f66f451Sopenharmony_ci// Return whether or not we've seen this inode+dev, adding it to the list if
660f66f451Sopenharmony_ci// we haven't.
670f66f451Sopenharmony_cistatic int seen_inode(void **list, struct stat *st)
680f66f451Sopenharmony_ci{
690f66f451Sopenharmony_ci  if (!st) llist_traverse(st, free);
700f66f451Sopenharmony_ci
710f66f451Sopenharmony_ci  // Skipping dir nodes isn't _quite_ right. They're not hardlinked, but could
720f66f451Sopenharmony_ci  // be bind mounted. Still, it's more efficient and the archivers can't use
730f66f451Sopenharmony_ci  // hardlinked directory info anyway. (Note that we don't catch bind mounted
740f66f451Sopenharmony_ci  // _files_ because it doesn't change st_nlink.)
750f66f451Sopenharmony_ci  else if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
760f66f451Sopenharmony_ci    struct inode_list {
770f66f451Sopenharmony_ci      struct inode_list *next;
780f66f451Sopenharmony_ci      ino_t ino;
790f66f451Sopenharmony_ci      dev_t dev;
800f66f451Sopenharmony_ci    } *new;
810f66f451Sopenharmony_ci
820f66f451Sopenharmony_ci    for (new = *list; new; new = new->next)
830f66f451Sopenharmony_ci      if(new->ino == st->st_ino && new->dev == st->st_dev)
840f66f451Sopenharmony_ci        return 1;
850f66f451Sopenharmony_ci
860f66f451Sopenharmony_ci    new = xzalloc(sizeof(*new));
870f66f451Sopenharmony_ci    new->ino = st->st_ino;
880f66f451Sopenharmony_ci    new->dev = st->st_dev;
890f66f451Sopenharmony_ci    new->next = *list;
900f66f451Sopenharmony_ci    *list = new;
910f66f451Sopenharmony_ci  }
920f66f451Sopenharmony_ci
930f66f451Sopenharmony_ci  return 0;
940f66f451Sopenharmony_ci}
950f66f451Sopenharmony_ci
960f66f451Sopenharmony_ci// dirtree callback, compute/display size of node
970f66f451Sopenharmony_cistatic int do_du(struct dirtree *node)
980f66f451Sopenharmony_ci{
990f66f451Sopenharmony_ci  if (!node->parent) TT.st_dev = node->st.st_dev;
1000f66f451Sopenharmony_ci  else if (!dirtree_notdotdot(node)) return 0;
1010f66f451Sopenharmony_ci
1020f66f451Sopenharmony_ci  // detect swiching filesystems
1030f66f451Sopenharmony_ci  if ((toys.optflags & FLAG_x) && (TT.st_dev != node->st.st_dev))
1040f66f451Sopenharmony_ci    return 0;
1050f66f451Sopenharmony_ci
1060f66f451Sopenharmony_ci  // Don't loop endlessly on recursive directory symlink
1070f66f451Sopenharmony_ci  if (toys.optflags & FLAG_L) {
1080f66f451Sopenharmony_ci    struct dirtree *try = node;
1090f66f451Sopenharmony_ci
1100f66f451Sopenharmony_ci    while ((try = try->parent))
1110f66f451Sopenharmony_ci      if (node->st.st_dev==try->st.st_dev && node->st.st_ino==try->st.st_ino)
1120f66f451Sopenharmony_ci        return 0;
1130f66f451Sopenharmony_ci  }
1140f66f451Sopenharmony_ci
1150f66f451Sopenharmony_ci  // Don't count hard links twice
1160f66f451Sopenharmony_ci  if (!(toys.optflags & FLAG_l) && !node->again)
1170f66f451Sopenharmony_ci    if (seen_inode(&TT.inodes, &node->st)) return 0;
1180f66f451Sopenharmony_ci
1190f66f451Sopenharmony_ci  // Collect child info before printing directory size
1200f66f451Sopenharmony_ci  if (S_ISDIR(node->st.st_mode)) {
1210f66f451Sopenharmony_ci    if (!node->again) {
1220f66f451Sopenharmony_ci      TT.depth++;
1230f66f451Sopenharmony_ci      return DIRTREE_COMEAGAIN|(DIRTREE_SYMFOLLOW*!!(toys.optflags&FLAG_L));
1240f66f451Sopenharmony_ci    } else TT.depth--;
1250f66f451Sopenharmony_ci  }
1260f66f451Sopenharmony_ci
1270f66f451Sopenharmony_ci  // Modern compilers' optimizers are insane and think signed overflow
1280f66f451Sopenharmony_ci  // behaves differently than unsigned overflow. Sigh. Big hammer.
1290f66f451Sopenharmony_ci
1300f66f451Sopenharmony_ci  if ((toys.optflags & FLAG_a) || !node->parent
1310f66f451Sopenharmony_ci      || (S_ISDIR(node->st.st_mode) && !(toys.optflags & FLAG_s))) {
1320f66f451Sopenharmony_ci    print(node->st.st_size, node);
1330f66f451Sopenharmony_ci  }
1340f66f451Sopenharmony_ci  return 0;
1350f66f451Sopenharmony_ci}
1360f66f451Sopenharmony_ci
1370f66f451Sopenharmony_civoid du_main(void)
1380f66f451Sopenharmony_ci{
1390f66f451Sopenharmony_ci  char *noargs[] = {".", 0}, **args;
1400f66f451Sopenharmony_ci  if (toys.optc < 1) help_exit(0);
1410f66f451Sopenharmony_ci
1420f66f451Sopenharmony_ci  if (!strcmp(*toys.optargs, ".") || !strncmp("./" ,*toys.optargs, 2))
1430f66f451Sopenharmony_ci    help_exit("Directory size statistics are not supported");
1440f66f451Sopenharmony_ci
1450f66f451Sopenharmony_ci
1460f66f451Sopenharmony_ci  // Loop over command line arguments, recursing through children
1470f66f451Sopenharmony_ci  for (args = toys.optc ? toys.optargs : noargs; *args; args++)
1480f66f451Sopenharmony_ci    dirtree_flagread(*args, DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)),
1490f66f451Sopenharmony_ci      do_du);
1500f66f451Sopenharmony_ci  if (toys.optflags & FLAG_c) print(TT.total*512, 0);
1510f66f451Sopenharmony_ci
1520f66f451Sopenharmony_ci  if (CFG_TOYBOX_FREE) seen_inode(TT.inodes, 0);
1530f66f451Sopenharmony_ci}
154