master xplshn/aruu / cmd / posix / du.c
  1/* See LICENSE file for copyright and license details. */
  2
  3#include <sys/stat.h>
  4#include <sys/types.h>
  5
  6#include <errno.h>
  7#include <fcntl.h>
  8#include <limits.h>
  9#include <search.h>
 10#include <stdint.h>
 11#include <stdio.h>
 12#include <stdlib.h>
 13#include <unistd.h>
 14
 15#include "fs.h"
 16#include "util.h"
 17
 18static size_t maxdepth = SIZE_MAX;
 19static size_t blksize  = 512;
 20
 21static int aflag = 0;
 22static int sflag = 0;
 23static int hflag = 0;
 24
 25struct file {
 26  dev_t devno;
 27  ino_t inode;
 28};
 29
 30static void
 31printpath(off_t n, const char *path)
 32{
 33  if (hflag)
 34    printf("%s\t%s\n", humansize(n * blksize), path);
 35  else
 36    printf("%jd\t%s\n", (intmax_t)n, path);
 37}
 38
 39static off_t
 40nblks(blkcnt_t blocks)
 41{
 42  return (512 * blocks + blksize - 1) / blksize;
 43}
 44
 45static int
 46cmp(const void *p1, const void *p2)
 47{
 48  const struct file *f1 = p1, *f2 = p2;
 49
 50  if (f1->devno > f2->devno)
 51    return -1;
 52  if (f1->devno < f2->devno)
 53    return 1;
 54
 55  /* f1->devno == f2->devno */
 56  if (f1->inode < f2->inode)
 57    return -1;
 58  if (f1->inode > f2->inode)
 59    return 1;
 60
 61  return 0;
 62}
 63
 64static int
 65duplicated(dev_t dev, ino_t ino)
 66{
 67  static void  *tree;
 68  struct file **fpp, *fp, file = {dev, ino};
 69
 70  if ((fpp = tsearch(&file, &tree, cmp)) == NULL)
 71    eprintf("%s:", argv0);
 72
 73  if (*fpp != &file)
 74    return 1;
 75
 76  /* new file added */
 77  fp   = emalloc(sizeof(*fp));
 78  *fp  = file;
 79  *fpp = fp;
 80
 81  return 0;
 82}
 83
 84static void
 85du(int dirfd, const char *path, struct stat *st, void *data, struct recursor *r)
 86{
 87  off_t *total = data, subtotal;
 88
 89  subtotal = nblks(st->st_blocks);
 90  if (S_ISDIR(st->st_mode)) {
 91    recurse(dirfd, path, &subtotal, r);
 92  } else if (r->follow != 'P' || st->st_nlink > 1) {
 93    if (duplicated(st->st_dev, st->st_ino))
 94      goto print;
 95  }
 96
 97  *total += subtotal;
 98
 99print:
100  if (!r->depth)
101    printpath(*total, r->path);
102  else if (!sflag && (size_t)r->depth <= maxdepth && (S_ISDIR(st->st_mode) || aflag))
103    printpath(subtotal, r->path);
104}
105
106static void
107usage(void)
108{
109  eprintf(
110      "usage: %s [-a | -s] [-d depth] [-h] [-k] [-H | -L | -P] [-x] "
111      "[file ...]\n",
112      argv0
113  );
114}
115
116// ?man du: estimate file space usage
117// ?man arguments: file ...
118// ?man display disk space used by files and directories
119int
120main(int argc, char *argv[])
121{
122  struct recursor r     = {.fn = du, .follow = 'P'};
123  off_t           n     = 0;
124  int             kflag = 0, dflag = 0;
125  char           *bsize;
126
127  ARGBEGIN
128  {
129    // ?man -a: print or show all entries
130    case 'a':
131      aflag = 1;
132      break;
133    // ?man -d:num: specify directory
134    case 'd':
135      dflag    = 1;
136      maxdepth = estrtonum(
137          EARGF(usage()), 0, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX)
138      );
139      break;
140    // ?man -h: suppress headers or print help
141    case 'h':
142      hflag = 1;
143      break;
144    // ?man -k: specify option flag
145    case 'k':
146      kflag = 1;
147      break;
148    // ?man -s: silent mode or print summary
149    case 's':
150      sflag = 1;
151      break;
152    // ?man -x: hex format or match whole lines
153    case 'x':
154      r.flags |= SAMEDEV;
155      break;
156    // ?man -H: specify option flag
157    case 'H':
158    // ?man -L: specify option flag
159    case 'L':
160    // ?man -P: specify option flag
161    case 'P':
162      r.follow = ARGC();
163      break;
164    default:
165      usage();
166  }
167  ARGEND
168
169  if ((aflag && sflag) || (dflag && sflag))
170    usage();
171
172  bsize = getenv("BLOCKSIZE");
173  if (bsize)
174    blksize = estrtonum(bsize, 1, MIN((unsigned long long)LLONG_MAX, (unsigned long long)SIZE_MAX));
175  if (kflag)
176    blksize = 1024;
177
178  if (!argc) {
179    recurse(AT_FDCWD, ".", &n, &r);
180  } else {
181    for (; *argv; argc--, argv++) {
182      n = 0;
183      recurse(AT_FDCWD, *argv, &n, &r);
184    }
185  }
186
187  return fshut(stdout, "<stdout>") || recurse_status;
188}