Hook up the recently added utilities [1] to the build.
[dragonfly.git] / usr.bin / du / du.c
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Chris Newcomb.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#) Copyright (c) 1989, 1993, 1994 The Regents of the University of California.  All rights reserved.
37  * @(#)du.c     8.5 (Berkeley) 5/4/95
38  * $FreeBSD: src/usr.bin/du/du.c,v 1.17.2.4 2002/12/12 16:29:39 trhodes Exp $
39  * $DragonFly: src/usr.bin/du/du.c,v 1.4 2003/10/04 20:36:43 hmp Exp $
40  */
41
42 #include <sys/param.h>
43 #include <sys/queue.h>
44 #include <sys/stat.h>
45
46 #include <err.h>
47 #include <errno.h>
48 #include <fnmatch.h>
49 #include <fts.h>
50 #include <math.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <sysexits.h>
55 #include <unistd.h>
56
57 #define KILO_SZ(n) (n)
58 #define MEGA_SZ(n) ((n) * (n))
59 #define GIGA_SZ(n) ((n) * (n) * (n))
60 #define TERA_SZ(n) ((n) * (n) * (n) * (n))
61 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n))
62
63 #define KILO_2_SZ (KILO_SZ(1024ULL))
64 #define MEGA_2_SZ (MEGA_SZ(1024ULL))
65 #define GIGA_2_SZ (GIGA_SZ(1024ULL))
66 #define TERA_2_SZ (TERA_SZ(1024ULL))
67 #define PETA_2_SZ (PETA_SZ(1024ULL))
68
69 #define KILO_SI_SZ (KILO_SZ(1000ULL))
70 #define MEGA_SI_SZ (MEGA_SZ(1000ULL))
71 #define GIGA_SI_SZ (GIGA_SZ(1000ULL))
72 #define TERA_SI_SZ (TERA_SZ(1000ULL))
73 #define PETA_SI_SZ (PETA_SZ(1000ULL))
74
75 #define HASHSIZE        256             /* power of 2 only */
76 #define HASHMASK        (HASHSIZE - 1)
77
78 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
79 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
80 unsigned long long *valp;
81
82 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
83
84 int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
85
86 SLIST_HEAD(ignhead, ignentry) ignores;
87 struct ignentry {
88         char                    *mask;
89         SLIST_ENTRY(ignentry)   next;
90 };
91
92 int             linkchk(FTSENT *);
93 static void     usage(void);
94 void            prthumanval(double);
95 unit_t          unit_adjust(double *);
96 void            ignoreadd(const char *);
97 void            ignoreclean(void);
98 int             ignorep(FTSENT *);
99
100 int
101 main(int argc, char **argv)
102 {
103         FTS             *fts;
104         FTSENT          *p;
105         long            blocksize, savednumber = 0;
106         int             ftsoptions;
107         int             listall;
108         int             depth;
109         int             Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval;
110         char            **save;
111
112         Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
113         
114         save = argv;
115         ftsoptions = 0;
116         depth = INT_MAX;
117         SLIST_INIT(&ignores);
118         
119         while ((ch = getopt(argc, argv, "HI:LPasd:chkrx")) != -1)
120                 switch (ch) {
121                         case 'H':
122                                 Hflag = 1;
123                                 break;
124                         case 'I':
125                                 ignoreadd(optarg);
126                                 break;
127                         case 'L':
128                                 if (Pflag)
129                                         usage();
130                                 Lflag = 1;
131                                 break;
132                         case 'P':
133                                 if (Lflag)
134                                         usage();
135                                 Pflag = 1;
136                                 break;
137                         case 'a':
138                                 aflag = 1;
139                                 break;
140                         case 's':
141                                 sflag = 1;
142                                 break;
143                         case 'd':
144                                 dflag = 1;
145                                 errno = 0;
146                                 depth = atoi(optarg);
147                                 if (errno == ERANGE || depth < 0) {
148                                         warnx("invalid argument to option d: %s", optarg);
149                                         usage();
150                                 }
151                                 break;
152                         case 'c':
153                                 cflag = 1;
154                                 break;
155                         case 'h':
156                                 putenv("BLOCKSIZE=512");
157                                 hflag = 1;
158                                 valp = vals_base2;
159                                 break;
160                         case 'k':
161                                 if (!hflag)
162                                         putenv("BLOCKSIZE=1024");
163                                 break;
164                         case 'r':                /* Compatibility. */
165                                 break;
166                         case 'x':
167                                 ftsoptions |= FTS_XDEV;
168                                 break;
169                         case '?':
170                         default:
171                                 usage();
172                 }
173
174         argc -= optind;
175         argv += optind;
176
177         /*
178          * XXX
179          * Because of the way that fts(3) works, logical walks will not count
180          * the blocks actually used by symbolic links.  We rationalize this by
181          * noting that users computing logical sizes are likely to do logical
182          * copies, so not counting the links is correct.  The real reason is
183          * that we'd have to re-implement the kernel's symbolic link traversing
184          * algorithm to get this right.  If, for example, you have relative
185          * symbolic links referencing other relative symbolic links, it gets
186          * very nasty, very fast.  The bottom line is that it's documented in
187          * the man page, so it's a feature.
188          */
189
190         if (Hflag + Lflag + Pflag > 1)
191                 usage();
192
193         if (Hflag + Lflag + Pflag == 0)
194                 Pflag = 1;                      /* -P (physical) is default */
195
196         if (Hflag)
197                 ftsoptions |= FTS_COMFOLLOW;
198
199         if (Lflag)
200                 ftsoptions |= FTS_LOGICAL;
201
202         if (Pflag)
203                 ftsoptions |= FTS_PHYSICAL;
204
205         listall = 0;
206
207         if (aflag) {
208                 if (sflag || dflag)
209                         usage();
210                 listall = 1;
211         } else if (sflag) {
212                 if (dflag)
213                         usage();
214                 depth = 0;
215         }
216
217         if (!*argv) {
218                 argv = save;
219                 argv[0] = ".";
220                 argv[1] = NULL;
221         }
222
223         (void) getbsize(&notused, &blocksize);
224         blocksize /= 512;
225
226         rval = 0;
227         
228         if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
229                 err(1, "fts_open");
230
231         while ((p = fts_read(fts)) != NULL) {
232                 switch (p->fts_info) {
233                         case FTS_D:                     /* Ignore. */
234                                 if (ignorep(p))
235                                         fts_set(fts, p, FTS_SKIP);
236                                 break;
237                         case FTS_DP:
238                                 if (ignorep(p))
239                                         break;
240
241                                 p->fts_parent->fts_number +=
242                                     p->fts_number += p->fts_statp->st_blocks;
243                                 
244                                 if (p->fts_level <= depth) {
245                                         if (hflag) {
246                                                 (void) prthumanval(howmany(p->fts_number, blocksize));
247                                                 (void) printf("\t%s\n", p->fts_path);
248                                         } else {
249                                         (void) printf("%ld\t%s\n",
250                                             howmany(p->fts_number, blocksize),
251                                             p->fts_path);
252                                         }
253                                 }
254                                 break;
255                         case FTS_DC:                    /* Ignore. */
256                                 break;
257                         case FTS_DNR:                   /* Warn, continue. */
258                         case FTS_ERR:
259                         case FTS_NS:
260                                 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
261                                 rval = 1;
262                                 break;
263                         default:
264                                 if (ignorep(p))
265                                         break;
266
267                                 if (p->fts_statp->st_nlink > 1 && linkchk(p))
268                                         break;
269                                 
270                                 if (listall || p->fts_level == 0) {
271                                         if (hflag) {
272                                                 (void) prthumanval(howmany(p->fts_statp->st_blocks,
273                                                         blocksize));
274                                                 (void) printf("\t%s\n", p->fts_path);
275                                         } else {
276                                                 (void) printf("%qd\t%s\n",
277                                                         howmany(p->fts_statp->st_blocks, blocksize),
278                                                         p->fts_path);
279                                         }
280                                 }
281
282                                 p->fts_parent->fts_number += p->fts_statp->st_blocks;
283                 }
284                 savednumber = p->fts_parent->fts_number;
285         }
286
287         if (errno)
288                 err(1, "fts_read");
289
290         if (cflag) {
291                 if (hflag) {
292                         (void) prthumanval(howmany(savednumber, blocksize));
293                         (void) printf("\ttotal\n");
294                 } else {
295                         (void) printf("%ld\ttotal\n", howmany(savednumber, blocksize));
296                 }
297         }
298
299         ignoreclean();
300         exit(rval);
301 }
302
303
304 typedef struct _ID {
305         dev_t   dev;
306         ino_t   inode;
307 } ID;
308
309
310 int
311 linkchk(FTSENT *p)
312 {
313         static ID **files;
314         static int *maxfiles, *nfiles;
315         static ID *filesph[HASHSIZE];
316         static int maxfilesh[HASHSIZE], nfilesh[HASHSIZE];
317         ID *fp, *start;
318         ino_t ino;
319         dev_t dev;
320         int index;
321
322         ino = p->fts_statp->st_ino;
323         index = ino & HASHMASK;
324         files = &filesph[index];
325         maxfiles = &maxfilesh[index];
326         nfiles = &nfilesh[index];
327         
328         dev = p->fts_statp->st_dev;
329         if ((start = *files) != NULL) {
330                 for (fp = start + *nfiles - 1; fp >= start; --fp)
331                         if (ino == fp->inode && dev == fp->dev)
332                                 return (1);
333         }
334
335         if (*nfiles == *maxfiles) {
336                 *maxfiles = (*maxfiles + 128) + (*maxfiles / 2);
337                 *files = realloc((char *)*files, sizeof(ID) * *maxfiles);
338                 if (*files == NULL)
339                         errx(1, "can't allocate memory");
340         }
341         (*files)[*nfiles].inode = ino;
342         (*files)[*nfiles].dev = dev;
343         ++(*nfiles);
344         return (0);
345 }
346
347 /*
348  * Output in "human-readable" format.  Uses 3 digits max and puts
349  * unit suffixes at the end.  Makes output compact and easy to read,
350  * especially on huge disks.
351  *
352  */
353 unit_t
354 unit_adjust(double *val)
355 {
356         double abval;
357         unit_t unit;
358         unsigned int unit_sz;
359
360         abval = fabs(*val);
361
362         unit_sz = abval ? ilogb(abval) / 10 : 0;
363
364         if (unit_sz >= UNIT_MAX) {
365                 unit = NONE;
366         } else {
367                 unit = unitp[unit_sz];
368                 *val /= (double)valp[unit_sz];
369         }
370
371         return (unit);
372 }
373
374 void
375 prthumanval(double bytes)
376 {
377         unit_t unit;
378
379         bytes *= 512;
380         unit = unit_adjust(&bytes);
381
382         if (bytes == 0)
383                 (void)printf("  0B");
384         else if (bytes > 10)
385                 (void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]);
386         else
387                 (void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]);
388 }
389
390 static void
391 usage(void)
392 {
393         (void)fprintf(stderr,
394                 "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k] [-x] [-I mask] [file ...]\n");
395         exit(EX_USAGE);
396 }
397
398 void
399 ignoreadd(const char *mask)
400 {
401         struct ignentry *ign;
402
403         ign = calloc(1, sizeof(*ign));
404         if (ign == NULL)
405                 errx(1, "cannot allocate memory");
406         ign->mask = strdup(mask);
407         if (ign->mask == NULL)
408                 errx(1, "cannot allocate memory");
409         SLIST_INSERT_HEAD(&ignores, ign, next);
410 }
411
412 void
413 ignoreclean(void)
414 {
415         struct ignentry *ign;
416         
417         while (!SLIST_EMPTY(&ignores)) {
418                 ign = SLIST_FIRST(&ignores);
419                 SLIST_REMOVE_HEAD(&ignores, next);
420                 free(ign->mask);
421                 free(ign);
422         }
423 }
424
425 int
426 ignorep(FTSENT *ent)
427 {
428         struct ignentry *ign;
429
430         SLIST_FOREACH(ign, &ignores, next)
431                 if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
432                         return 1;
433         return 0;
434 }