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