Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[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.2 2003/06/17 04:29:26 dillon 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 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ};
76 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ};
77 unsigned long long *valp;
78
79 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t;
80
81 int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA };
82
83 SLIST_HEAD(ignhead, ignentry) ignores;
84 struct ignentry {
85         char                    *mask;
86         SLIST_ENTRY(ignentry)   next;
87 };
88
89 int             linkchk __P((FTSENT *));
90 static void     usage __P((void));
91 void            prthumanval __P((double));
92 unit_t          unit_adjust __P((double *));
93 void            ignoreadd __P((const char *));
94 void            ignoreclean __P((void));
95 int             ignorep __P((FTSENT *));
96
97 int
98 main(argc, argv)
99         int argc;
100         char *argv[];
101 {
102         FTS             *fts;
103         FTSENT          *p;
104         long            blocksize, savednumber = 0;
105         int             ftsoptions;
106         int             listall;
107         int             depth;
108         int             Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval;
109         char            **save;
110
111         Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0;
112         
113         save = argv;
114         ftsoptions = 0;
115         depth = INT_MAX;
116         SLIST_INIT(&ignores);
117         
118         while ((ch = getopt(argc, argv, "HI:LPasd:chkrx")) != -1)
119                 switch (ch) {
120                         case 'H':
121                                 Hflag = 1;
122                                 break;
123                         case 'I':
124                                 ignoreadd(optarg);
125                                 break;
126                         case 'L':
127                                 if (Pflag)
128                                         usage();
129                                 Lflag = 1;
130                                 break;
131                         case 'P':
132                                 if (Lflag)
133                                         usage();
134                                 Pflag = 1;
135                                 break;
136                         case 'a':
137                                 aflag = 1;
138                                 break;
139                         case 's':
140                                 sflag = 1;
141                                 break;
142                         case 'd':
143                                 dflag = 1;
144                                 errno = 0;
145                                 depth = atoi(optarg);
146                                 if (errno == ERANGE || depth < 0) {
147                                         warnx("invalid argument to option d: %s", optarg);
148                                         usage();
149                                 }
150                                 break;
151                         case 'c':
152                                 cflag = 1;
153                                 break;
154                         case 'h':
155                                 putenv("BLOCKSIZE=512");
156                                 hflag = 1;
157                                 valp = vals_base2;
158                                 break;
159                         case 'k':
160                                 if (!hflag)
161                                         putenv("BLOCKSIZE=1024");
162                                 break;
163                         case 'r':                /* Compatibility. */
164                                 break;
165                         case 'x':
166                                 ftsoptions |= FTS_XDEV;
167                                 break;
168                         case '?':
169                         default:
170                                 usage();
171                 }
172
173         argc -= optind;
174         argv += optind;
175
176         /*
177          * XXX
178          * Because of the way that fts(3) works, logical walks will not count
179          * the blocks actually used by symbolic links.  We rationalize this by
180          * noting that users computing logical sizes are likely to do logical
181          * copies, so not counting the links is correct.  The real reason is
182          * that we'd have to re-implement the kernel's symbolic link traversing
183          * algorithm to get this right.  If, for example, you have relative
184          * symbolic links referencing other relative symbolic links, it gets
185          * very nasty, very fast.  The bottom line is that it's documented in
186          * the man page, so it's a feature.
187          */
188
189         if (Hflag + Lflag + Pflag > 1)
190                 usage();
191
192         if (Hflag + Lflag + Pflag == 0)
193                 Pflag = 1;                      /* -P (physical) is default */
194
195         if (Hflag)
196                 ftsoptions |= FTS_COMFOLLOW;
197
198         if (Lflag)
199                 ftsoptions |= FTS_LOGICAL;
200
201         if (Pflag)
202                 ftsoptions |= FTS_PHYSICAL;
203
204         listall = 0;
205
206         if (aflag) {
207                 if (sflag || dflag)
208                         usage();
209                 listall = 1;
210         } else if (sflag) {
211                 if (dflag)
212                         usage();
213                 depth = 0;
214         }
215
216         if (!*argv) {
217                 argv = save;
218                 argv[0] = ".";
219                 argv[1] = NULL;
220         }
221
222         (void) getbsize(&notused, &blocksize);
223         blocksize /= 512;
224
225         rval = 0;
226         
227         if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
228                 err(1, "fts_open");
229
230         while ((p = fts_read(fts)) != NULL) {
231                 switch (p->fts_info) {
232                         case FTS_D:                     /* Ignore. */
233                                 if (ignorep(p))
234                                         fts_set(fts, p, FTS_SKIP);
235                                 break;
236                         case FTS_DP:
237                                 if (ignorep(p))
238                                         break;
239
240                                 p->fts_parent->fts_number +=
241                                     p->fts_number += p->fts_statp->st_blocks;
242                                 
243                                 if (p->fts_level <= depth) {
244                                         if (hflag) {
245                                                 (void) prthumanval(howmany(p->fts_number, blocksize));
246                                                 (void) printf("\t%s\n", p->fts_path);
247                                         } else {
248                                         (void) printf("%ld\t%s\n",
249                                             howmany(p->fts_number, blocksize),
250                                             p->fts_path);
251                                         }
252                                 }
253                                 break;
254                         case FTS_DC:                    /* Ignore. */
255                                 break;
256                         case FTS_DNR:                   /* Warn, continue. */
257                         case FTS_ERR:
258                         case FTS_NS:
259                                 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
260                                 rval = 1;
261                                 break;
262                         default:
263                                 if (ignorep(p))
264                                         break;
265
266                                 if (p->fts_statp->st_nlink > 1 && linkchk(p))
267                                         break;
268                                 
269                                 if (listall || p->fts_level == 0) {
270                                         if (hflag) {
271                                                 (void) prthumanval(howmany(p->fts_statp->st_blocks,
272                                                         blocksize));
273                                                 (void) printf("\t%s\n", p->fts_path);
274                                         } else {
275                                                 (void) printf("%qd\t%s\n",
276                                                         howmany(p->fts_statp->st_blocks, blocksize),
277                                                         p->fts_path);
278                                         }
279                                 }
280
281                                 p->fts_parent->fts_number += p->fts_statp->st_blocks;
282                 }
283                 savednumber = p->fts_parent->fts_number;
284         }
285
286         if (errno)
287                 err(1, "fts_read");
288
289         if (cflag) {
290                 if (hflag) {
291                         (void) prthumanval(howmany(savednumber, blocksize));
292                         (void) printf("\ttotal\n");
293                 } else {
294                         (void) printf("%ld\ttotal\n", howmany(savednumber, blocksize));
295                 }
296         }
297
298         ignoreclean();
299         exit(rval);
300 }
301
302
303 typedef struct _ID {
304         dev_t   dev;
305         ino_t   inode;
306 } ID;
307
308
309 int
310 linkchk(p)
311         FTSENT *p;
312 {
313         static ID *files;
314         static int maxfiles, nfiles;
315         ID *fp, *start;
316         ino_t ino;
317         dev_t dev;
318
319         ino = p->fts_statp->st_ino;
320         dev = p->fts_statp->st_dev;
321         if ((start = files) != NULL)
322                 for (fp = start + nfiles - 1; fp >= start; --fp)
323                         if (ino == fp->inode && dev == fp->dev)
324                                 return (1);
325
326         if (nfiles == maxfiles && (files = realloc((char *)files,
327             (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL)
328                 errx(1, "can't allocate memory");
329         files[nfiles].inode = ino;
330         files[nfiles].dev = dev;
331         ++nfiles;
332         return (0);
333 }
334
335 /*
336  * Output in "human-readable" format.  Uses 3 digits max and puts
337  * unit suffixes at the end.  Makes output compact and easy to read,
338  * especially on huge disks.
339  *
340  */
341 unit_t
342 unit_adjust(val)
343         double *val;
344 {
345         double abval;
346         unit_t unit;
347         unsigned int unit_sz;
348
349         abval = fabs(*val);
350
351         unit_sz = abval ? ilogb(abval) / 10 : 0;
352
353         if (unit_sz >= UNIT_MAX) {
354                 unit = NONE;
355         } else {
356                 unit = unitp[unit_sz];
357                 *val /= (double)valp[unit_sz];
358         }
359
360         return (unit);
361 }
362
363 void
364 prthumanval(bytes)
365         double bytes;
366 {
367         unit_t unit;
368
369         bytes *= 512;
370         unit = unit_adjust(&bytes);
371
372         if (bytes == 0)
373                 (void)printf("  0B");
374         else if (bytes > 10)
375                 (void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]);
376         else
377                 (void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]);
378 }
379
380 static void
381 usage()
382 {
383         (void)fprintf(stderr,
384                 "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k] [-x] [-I mask] [file ...]\n");
385         exit(EX_USAGE);
386 }
387
388 void
389 ignoreadd(mask)
390         const char *mask;
391 {
392         struct ignentry *ign;
393
394         ign = calloc(1, sizeof(*ign));
395         if (ign == NULL)
396                 errx(1, "cannot allocate memory");
397         ign->mask = strdup(mask);
398         if (ign->mask == NULL)
399                 errx(1, "cannot allocate memory");
400         SLIST_INSERT_HEAD(&ignores, ign, next);
401 }
402
403 void
404 ignoreclean()
405 {
406         struct ignentry *ign;
407         
408         while (!SLIST_EMPTY(&ignores)) {
409                 ign = SLIST_FIRST(&ignores);
410                 SLIST_REMOVE_HEAD(&ignores, next);
411                 free(ign->mask);
412                 free(ign);
413         }
414 }
415
416 int
417 ignorep(ent)
418         FTSENT *ent;
419 {
420         struct ignentry *ign;
421
422         SLIST_FOREACH(ign, &ignores, next)
423                 if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH)
424                         return 1;
425         return 0;
426 }