Annotation of src/usr.bin/du/du.c, Revision 1.15
1.15 ! tom 1: /* $OpenBSD: du.c,v 1.14 2003/07/02 21:04:09 deraadt Exp $ */
1.3 millert 2: /* $NetBSD: du.c,v 1.11 1996/10/18 07:20:35 thorpej Exp $ */
1.1 deraadt 3:
4: /*
5: * Copyright (c) 1989, 1993, 1994
6: * The Regents of the University of California. All rights reserved.
7: *
8: * This code is derived from software contributed to Berkeley by
9: * Chris Newcomb.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
1.12 millert 19: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #ifndef lint
37: static char copyright[] =
38: "@(#) Copyright (c) 1989, 1993, 1994\n\
39: The Regents of the University of California. All rights reserved.\n";
40: #endif /* not lint */
41:
42: #ifndef lint
43: #if 0
44: static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95";
45: #else
1.15 ! tom 46: static char rcsid[] = "$OpenBSD: du.c,v 1.14 2003/07/02 21:04:09 deraadt Exp $";
1.1 deraadt 47: #endif
48: #endif /* not lint */
49:
50: #include <sys/types.h>
51: #include <sys/stat.h>
52:
53: #include <dirent.h>
54: #include <err.h>
55: #include <errno.h>
56: #include <fts.h>
1.7 pjanzen 57: #include <math.h>
1.1 deraadt 58: #include <stdio.h>
59: #include <stdlib.h>
60: #include <string.h>
61: #include <unistd.h>
62:
1.14 deraadt 63: typedef enum { NONE = 0, KILO, MEGA, GIGA, TERA, PETA /* , EXA */ } unit_t;
64:
1.11 millert 65: int linkchk(FTSENT *);
66: void prtout(quad_t, char *, int);
67: void usage(void);
1.14 deraadt 68: unit_t unit_adjust(double *);
1.1 deraadt 69:
70: int
1.13 deraadt 71: main(int argc, char *argv[])
1.1 deraadt 72: {
73: FTS *fts;
74: FTSENT *p;
1.7 pjanzen 75: long blocksize;
76: quad_t totalblocks;
1.1 deraadt 77: int ftsoptions, listdirs, listfiles;
1.7 pjanzen 78: int Hflag, Lflag, Pflag, aflag, cflag, hflag, kflag, sflag;
79: int ch, notused, rval;
1.1 deraadt 80: char **save;
81:
82: save = argv;
1.7 pjanzen 83: Hflag = Lflag = Pflag = aflag = cflag = hflag = kflag = sflag = 0;
1.3 millert 84: totalblocks = 0;
1.1 deraadt 85: ftsoptions = FTS_PHYSICAL;
1.7 pjanzen 86: while ((ch = getopt(argc, argv, "HLPachksxr")) != -1)
1.1 deraadt 87: switch (ch) {
88: case 'H':
89: Hflag = 1;
90: Lflag = Pflag = 0;
91: break;
92: case 'L':
93: Lflag = 1;
94: Hflag = Pflag = 0;
95: break;
96: case 'P':
97: Pflag = 1;
98: Hflag = Lflag = 0;
99: break;
100: case 'a':
101: aflag = 1;
102: break;
1.3 millert 103: case 'c':
104: cflag = 1;
105: break;
1.7 pjanzen 106: case 'h':
107: hflag = 1;
108: break;
1.1 deraadt 109: case 'k':
110: kflag = 1;
111: break;
112: case 's':
113: sflag = 1;
114: break;
1.5 deraadt 115: case 'r':
116: break;
1.1 deraadt 117: case 'x':
118: ftsoptions |= FTS_XDEV;
119: break;
120: case '?':
121: default:
122: usage();
123: }
124: argc -= optind;
125: argv += optind;
126:
127: /*
128: * XXX
129: * Because of the way that fts(3) works, logical walks will not count
130: * the blocks actually used by symbolic links. We rationalize this by
131: * noting that users computing logical sizes are likely to do logical
132: * copies, so not counting the links is correct. The real reason is
133: * that we'd have to re-implement the kernel's symbolic link traversing
134: * algorithm to get this right. If, for example, you have relative
135: * symbolic links referencing other relative symbolic links, it gets
136: * very nasty, very fast. The bottom line is that it's documented in
137: * the man page, so it's a feature.
138: */
139: if (Hflag)
140: ftsoptions |= FTS_COMFOLLOW;
141: if (Lflag) {
142: ftsoptions &= ~FTS_PHYSICAL;
143: ftsoptions |= FTS_LOGICAL;
144: }
145:
146: if (aflag) {
147: if (sflag)
148: usage();
149: listdirs = listfiles = 1;
150: } else if (sflag)
151: listdirs = listfiles = 0;
152: else {
153: listfiles = 0;
154: listdirs = 1;
155: }
156:
157: if (!*argv) {
158: argv = save;
159: argv[0] = ".";
160: argv[1] = NULL;
161: }
162:
1.8 pjanzen 163: if (hflag)
164: blocksize = 512;
165: else if (kflag)
166: blocksize = 1024;
167: else
1.1 deraadt 168: (void)getbsize(¬used, &blocksize);
169: blocksize /= 512;
170:
171: if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
1.3 millert 172: err(1, "fts_open");
1.1 deraadt 173:
174: for (rval = 0; (p = fts_read(fts)) != NULL;)
175: switch (p->fts_info) {
176: case FTS_D: /* Ignore. */
177: break;
178: case FTS_DP:
179: p->fts_parent->fts_number +=
180: p->fts_number += p->fts_statp->st_blocks;
1.3 millert 181: if (cflag)
182: totalblocks += p->fts_statp->st_blocks;
1.1 deraadt 183: /*
184: * If listing each directory, or not listing files
185: * or directories and this is post-order of the
186: * root of a traversal, display the total.
187: */
1.3 millert 188: if (listdirs || (!listfiles && !p->fts_level))
1.7 pjanzen 189: prtout((quad_t)howmany(p->fts_number, blocksize),
190: p->fts_path, hflag);
1.1 deraadt 191: break;
192: case FTS_DC: /* Ignore. */
193: break;
194: case FTS_DNR: /* Warn, continue. */
195: case FTS_ERR:
196: case FTS_NS:
197: warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
198: rval = 1;
199: break;
200: default:
201: if (p->fts_statp->st_nlink > 1 && linkchk(p))
202: break;
203: /*
204: * If listing each file, or a non-directory file was
205: * the root of a traversal, display the total.
206: */
207: if (listfiles || !p->fts_level)
1.7 pjanzen 208: prtout(howmany(p->fts_statp->st_blocks, blocksize),
209: p->fts_path, hflag);
1.1 deraadt 210: p->fts_parent->fts_number += p->fts_statp->st_blocks;
1.3 millert 211: if (cflag)
212: totalblocks += p->fts_statp->st_blocks;
1.1 deraadt 213: }
214: if (errno)
215: err(1, "fts_read");
1.7 pjanzen 216: if (cflag) {
217: prtout((quad_t)howmany(totalblocks, blocksize), "total", hflag);
218: }
1.6 ericj 219: exit(rval);
1.1 deraadt 220: }
221:
222: typedef struct _ID {
223: dev_t dev;
224: ino_t inode;
225: } ID;
226:
227: int
1.13 deraadt 228: linkchk(FTSENT *p)
1.1 deraadt 229: {
230: static ID *files;
231: static int maxfiles, nfiles;
232: ID *fp, *start;
233: ino_t ino;
234: dev_t dev;
235:
236: ino = p->fts_statp->st_ino;
237: dev = p->fts_statp->st_dev;
238: if ((start = files) != NULL)
239: for (fp = start + nfiles - 1; fp >= start; --fp)
240: if (ino == fp->inode && dev == fp->dev)
241: return (1);
242:
243: if (nfiles == maxfiles && (files = realloc((char *)files,
244: (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
1.15 ! tom 245: err(1, NULL);
1.1 deraadt 246: files[nfiles].inode = ino;
247: files[nfiles].dev = dev;
248: ++nfiles;
249: return (0);
250: }
251:
1.7 pjanzen 252: /*
253: * "human-readable" output: use 3 digits max.--put unit suffixes at
254: * the end. Makes output compact and easy-to-read.
255: */
256:
257: unit_t
1.13 deraadt 258: unit_adjust(double *val)
1.7 pjanzen 259: {
260: double abval;
261: unit_t unit;
262:
263: abval = fabs(*val);
264: if (abval < 1024)
265: unit = NONE;
266: else if (abval < 1048576ULL) {
267: unit = KILO;
268: *val /= 1024;
269: } else if (abval < 1073741824ULL) {
270: unit = MEGA;
271: *val /= 1048576;
272: } else if (abval < 1099511627776ULL) {
273: unit = GIGA;
274: *val /= 1073741824ULL;
275: } else if (abval < 1125899906842624ULL) {
276: unit = TERA;
277: *val /= 1099511627776ULL;
278: } else /* if (abval < 1152921504606846976ULL) */ {
279: unit = PETA;
280: *val /= 1125899906842624ULL;
281: }
282: return (unit);
283: }
284:
285: void
1.13 deraadt 286: prtout(quad_t size, char *path, int hflag)
1.7 pjanzen 287: {
288: unit_t unit;
289: double bytes;
290:
291: if (!hflag)
1.9 deraadt 292: (void)printf("%lld\t%s\n", (long long)size, path);
1.7 pjanzen 293: else {
294: bytes = (double)size * 512.0;
295: unit = unit_adjust(&bytes);
296:
297: if (bytes == 0)
1.10 deraadt 298: (void)printf("0B\t%s\n", path);
1.7 pjanzen 299: else if (bytes > 10)
300: (void)printf("%.0f%c\t%s\n", bytes, "BKMGTPE"[unit], path);
301: else
302: (void)printf("%.1f%c\t%s\n", bytes, "BKMGTPE"[unit], path);
303: }
304: }
305:
1.1 deraadt 306: void
1.13 deraadt 307: usage(void)
1.1 deraadt 308: {
309:
310: (void)fprintf(stderr,
1.7 pjanzen 311: "usage: du [-H | -L | -P] [-a | -s] [-chkrx] [file ...]\n");
1.1 deraadt 312: exit(1);
313: }