diff options
Diffstat (limited to 'file_cmds/du')
-rw-r--r-- | file_cmds/du/du.1 | 172 | ||||
-rw-r--r-- | file_cmds/du/du.c | 730 |
2 files changed, 902 insertions, 0 deletions
diff --git a/file_cmds/du/du.1 b/file_cmds/du/du.1 new file mode 100644 index 0000000..1bc6b2b --- /dev/null +++ b/file_cmds/du/du.1 @@ -0,0 +1,172 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)du.1 8.2 (Berkeley) 4/1/94 +.\" $FreeBSD: src/usr.bin/du/du.1,v 1.30 2005/05/21 09:55:05 ru Exp $ +.\" +.Dd June 2, 2004 +.Dt DU 1 +.Os +.Sh NAME +.Nm du +.Nd display disk usage statistics +.Sh SYNOPSIS +.Nm du +.Op Fl H | L | P +.Op Fl a | s | d Ar depth +.Op Fl c +.Op Fl h | k | m | g +.Op Fl x +.Op Fl I Ar mask +.Op Ar +.Sh DESCRIPTION +The +.Nm du +utility displays the file system block usage for each file argument +and for each directory in the file hierarchy rooted in each directory +argument. +If no file is specified, the block usage of the hierarchy rooted in +the current directory is displayed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a +Display an entry for each file in a file hierarchy. +.It Fl c +Display a grand total. +.It Fl d Ar depth +Display an entry for all files and directories +.Ar depth +directories deep. +.It Fl H +Symbolic links on the command line are followed, symbolic links in file +hierarchies are not followed. +.It Fl h +"Human-readable" output. +Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte. +.It Fl I Ar mask +Ignore files and directories matching the specified +.Ar mask . +.It Fl g +Display block counts in 1073741824-byte (1-Gbyte) blocks. +.It Fl k +Display block counts in 1024-byte (1-Kbyte) blocks. +.It Fl L +Symbolic links on the command line and in file hierarchies are followed. +.It Fl m +Display block counts in 1048576-byte (1-Mbyte) blocks. +.It Fl P +No symbolic links are followed. +This is the default. +.It Fl r +Generate messages about directories that cannot be read, files +that cannot be opened, and so on. +This is the default case. +This option exists solely for conformance with +.St -xpg4 . +.It Fl s +Display an entry for each specified file. +(Equivalent to +.Fl d Li 0 ) +.It Fl x +File system mount points are not traversed. +.El +.Pp +The +.Nm du +utility counts the storage used by symbolic links and not the files they +reference unless the +.Fl H +or +.Fl L +option is specified. +If either the +.Fl H +or +.Fl L +options are specified, storage used by any symbolic links which are +followed is not counted or displayed. +If more than one of the +.Fl H , +.Fl L , +and +.Fl P +options is specified, the last one given is used. +.Pp +Files having multiple hard links are counted (and displayed) a single +time per +.Nm du +execution. +Directories having multiple hard links (typically Time Machine backups) are +counted a single time per +.Nm du +execution. +.Sh ENVIRONMENT +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, and the +.Fl k +option is not specified, the block counts will be displayed in units of that +size block. +If +.Ev BLOCKSIZE +is not set, and the +.Fl k +option is not specified, the block counts will be displayed in 512-byte blocks. +.El +.Sh LEGACY DESCRIPTION +In legacy mode, only one of the +.Fl H , +.Fl L , +or +.Fl P +options may be specified. +.Pp +The command will detect and report a SYMLOOP error +(loop involving symbolic links). +In legacy mode, this is not the case. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr df 1 , +.Xr fts 3 , +.Xr compat 5 , +.Xr symlink 7 , +.Xr quot 8 +.Sh HISTORY +A +.Nm du +command appeared in +.At v1 . diff --git a/file_cmds/du/du.c b/file_cmds/du/du.c new file mode 100644 index 0000000..9aea790 --- /dev/null +++ b/file_cmds/du/du.c @@ -0,0 +1,730 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Newcomb. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $"); + +#include <sys/mount.h> +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <sys/attr.h> + +#include <err.h> +#include <errno.h> +#include <fnmatch.h> +#include <fts.h> +#include <locale.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#ifdef __APPLE__ +#include <get_compat.h> +#include <sys/sysctl.h> +#else +#define COMPAT_MODE(func, mode) (1) +#endif + +#define KILO_SZ(n) (n) +#define MEGA_SZ(n) ((n) * (n)) +#define GIGA_SZ(n) ((n) * (n) * (n)) +#define TERA_SZ(n) ((n) * (n) * (n) * (n)) +#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) + +#define KILO_2_SZ (KILO_SZ(1024ULL)) +#define MEGA_2_SZ (MEGA_SZ(1024ULL)) +#define GIGA_2_SZ (GIGA_SZ(1024ULL)) +#define TERA_2_SZ (TERA_SZ(1024ULL)) +#define PETA_2_SZ (PETA_SZ(1024ULL)) + +#define KILO_SI_SZ (KILO_SZ(1000ULL)) +#define MEGA_SI_SZ (MEGA_SZ(1000ULL)) +#define GIGA_SI_SZ (GIGA_SZ(1000ULL)) +#define TERA_SI_SZ (TERA_SZ(1000ULL)) +#define PETA_SI_SZ (PETA_SZ(1000ULL)) + +unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; +unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; +unsigned long long *valp; + +typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; + +int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; + +SLIST_HEAD(ignhead, ignentry) ignores; +struct ignentry { + char *mask; + SLIST_ENTRY(ignentry) next; +}; + +static int linkchk(FTSENT *); +static int dirlinkchk(FTSENT *); +static void usage(void); +void prthumanval(double); +unit_t unit_adjust(double *); +void ignoreadd(const char *); +void ignoreclean(void); +int ignorep(FTSENT *); + +int +main(int argc, char *argv[]) +{ + FTS *fts; + FTSENT *p; + off_t savednumber = 0; + long blocksize; + int ftsoptions; + int listall; + int depth; + int Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval; + char **save; + static char dot[] = "."; + off_t *ftsnum, *ftsparnum; + + setlocale(LC_ALL, ""); + + Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0; + + save = argv; + ftsoptions = FTS_NOCHDIR; + depth = INT_MAX; + SLIST_INIT(&ignores); + + while ((ch = getopt(argc, argv, "HI:LPasd:cghkmrx")) != -1) + switch (ch) { + case 'H': + Lflag = Pflag = 0; + Hflag = 1; + break; + case 'I': + ignoreadd(optarg); + break; + case 'L': + Hflag = Pflag = 0; + Lflag = 1; + break; + case 'P': + Hflag = Lflag = 0; + Pflag = 1; + break; + case 'a': + aflag = 1; + break; + case 's': + sflag = 1; + break; + case 'd': + dflag = 1; + errno = 0; + depth = atoi(optarg); + if (errno == ERANGE || depth < 0) { + warnx("invalid argument to option d: %s", optarg); + usage(); + } + break; + case 'c': + cflag = 1; + break; + case 'h': + putenv("BLOCKSIZE=512"); + hflag = 1; + valp = vals_base2; + break; + case 'k': + hflag = 0; + putenv("BLOCKSIZE=1024"); + break; + case 'm': + hflag = 0; + putenv("BLOCKSIZE=1048576"); + break; + case 'g': + hflag = 0; + putenv("BLOCKSIZE=1g"); + break; + case 'r': /* Compatibility. */ + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + +// argc -= optind; + argv += optind; + + /* + * XXX + * Because of the way that fts(3) works, logical walks will not count + * the blocks actually used by symbolic links. We rationalize this by + * noting that users computing logical sizes are likely to do logical + * copies, so not counting the links is correct. The real reason is + * that we'd have to re-implement the kernel's symbolic link traversing + * algorithm to get this right. If, for example, you have relative + * symbolic links referencing other relative symbolic links, it gets + * very nasty, very fast. The bottom line is that it's documented in + * the man page, so it's a feature. + */ + + if (Hflag + Lflag + Pflag > 1) + usage(); + + if (Hflag + Lflag + Pflag == 0) + Pflag = 1; /* -P (physical) is default */ + + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + + if (Lflag) + ftsoptions |= FTS_LOGICAL; + + if (Pflag) + ftsoptions |= FTS_PHYSICAL; + + listall = 0; + + if (aflag) { + if (sflag || dflag) + usage(); + listall = 1; + } else if (sflag) { + if (dflag) + usage(); + depth = 0; + } + + if (!*argv) { + argv = save; + argv[0] = dot; + argv[1] = NULL; + } + + (void) getbsize(¬used, &blocksize); + blocksize /= 512; + +#ifdef __APPLE__ + // "du" should not have any side effect on disk usage, + // so prevent materializing dataless directories upon traversal + rval = 1; + (void) sysctlbyname("vfs.nspace.prevent_materialization", NULL, NULL, &rval, sizeof(rval)); +#endif /* __APPLE__ */ + + rval = 0; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) + err(1, "fts_open"); + + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_D: + if (ignorep(p) || dirlinkchk(p)) + fts_set(fts, p, FTS_SKIP); + break; + case FTS_DP: + if (ignorep(p)) + break; + + ftsparnum = (off_t *)&p->fts_parent->fts_number; + ftsnum = (off_t *)&p->fts_number; + ftsparnum[0] += ftsnum[0] += p->fts_statp->st_blocks; + + if (p->fts_level <= depth) { + if (hflag) { + (void) prthumanval(howmany(*ftsnum, blocksize)); + (void) printf("\t%s\n", p->fts_path); + } else { + (void) printf("%jd\t%s\n", + (intmax_t)howmany(*ftsnum, blocksize), + p->fts_path); + } + } + break; + case FTS_DC: /* Ignore. */ + if (COMPAT_MODE("bin/du", "unix2003")) { + errx(1, "Can't follow symlink cycle from %s to %s", p->fts_path, p->fts_cycle->fts_path); + } + break; + case FTS_DNR: /* Warn, continue. */ + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_SLNONE: + if (COMPAT_MODE("bin/du", "unix2003")) { + struct stat sb; + int rc = stat(p->fts_path, &sb); + if (rc < 0 && errno == ELOOP) { + errx(1, "Too many symlinks at %s", p->fts_path); + } + } + default: + if (ignorep(p)) + break; + + if (p->fts_statp->st_nlink > 1 && linkchk(p)) + break; + + if (listall || p->fts_level == 0) { + if (hflag) { + (void) prthumanval(howmany(p->fts_statp->st_blocks, + blocksize)); + (void) printf("\t%s\n", p->fts_path); + } else { + (void) printf("%jd\t%s\n", + (intmax_t)howmany(p->fts_statp->st_blocks, blocksize), + p->fts_path); + } + } + + ftsparnum = (off_t *)&p->fts_parent->fts_number; + ftsparnum[0] += p->fts_statp->st_blocks; + } + savednumber = ((off_t *)&p->fts_parent->fts_number)[0]; + } + + if (errno) + err(1, "fts_read"); + + if (cflag) { + if (hflag) { + (void) prthumanval(howmany(savednumber, blocksize)); + (void) printf("\ttotal\n"); + } else { + (void) printf("%jd\ttotal\n", (intmax_t)howmany(savednumber, blocksize)); + } + } + + ignoreclean(); + exit(rval); +} + +static int +linkchk(FTSENT *p) +{ + struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + }; + static const size_t links_hash_initial_size = 8192; + static struct links_entry **buckets; + static struct links_entry *free_list; + static size_t number_buckets; + static unsigned long number_entries; + static char stop_allocating; + struct links_entry *le, **new_buckets; + struct stat *st; + size_t i, new_size; + int hash; + + st = p->fts_statp; + + /* If necessary, initialize the hash table. */ + if (buckets == NULL) { + number_buckets = links_hash_initial_size; + buckets = malloc(number_buckets * sizeof(buckets[0])); + if (buckets == NULL) + errx(1, "No memory for hardlink detection"); + for (i = 0; i < number_buckets; i++) + buckets[i] = NULL; + } + + /* If the hash table is getting too full, enlarge it. */ + if (number_entries > number_buckets * 10 && !stop_allocating) { + new_size = number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + /* Try releasing the free list to see if that helps. */ + if (new_buckets == NULL && free_list != NULL) { + while (free_list != NULL) { + le = free_list; + free_list = le->next; + free(le); + } + new_buckets = malloc(new_size * sizeof(new_buckets[0])); + } + + if (new_buckets == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + } else { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < number_buckets; i++) { + while (buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = buckets[i]; + buckets[i] = le->next; + + /* Add entry to new bucket. */ + hash = (le->dev ^ le->ino) % new_size; + + if (new_buckets[hash] != NULL) + new_buckets[hash]->previous = + le; + le->next = new_buckets[hash]; + le->previous = NULL; + new_buckets[hash] = le; + } + } + free(buckets); + buckets = new_buckets; + number_buckets = new_size; + } + } + + /* Try to locate this entry in the hash table. */ + hash = ( st->st_dev ^ st->st_ino ) % number_buckets; + for (le = buckets[hash]; le != NULL; le = le->next) { + if (le->dev == st->st_dev && le->ino == st->st_ino) { + /* + * Save memory by releasing an entry when we've seen + * all of it's links. + */ + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (buckets[hash] == le) + buckets[hash] = le->next; + number_entries--; + /* Recycle this node through the free list */ + if (stop_allocating) { + free(le); + } else { + le->next = free_list; + free_list = le; + } + } + return (1); + } + } + + if (stop_allocating) + return (0); + + /* Add this entry to the links cache. */ + if (free_list != NULL) { + /* Pull a node from the free list if we can. */ + le = free_list; + free_list = le->next; + } else + /* Malloc one if we have to. */ + le = malloc(sizeof(struct links_entry)); + if (le == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + return (0); + } + le->dev = st->st_dev; + le->ino = st->st_ino; + le->links = st->st_nlink - 1; + number_entries++; + le->next = buckets[hash]; + le->previous = NULL; + if (buckets[hash] != NULL) + buckets[hash]->previous = le; + buckets[hash] = le; + return (0); +} + +static int +dirlinkchk(FTSENT *p) +{ + struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + }; + static const size_t links_hash_initial_size = 8192; + static struct links_entry **buckets; + static struct links_entry *free_list; + static size_t number_buckets; + static unsigned long number_entries; + static char stop_allocating; + struct links_entry *le, **new_buckets; + struct stat *st; + size_t i, new_size; + int hash; + struct attrbuf { + int size; + int linkcount; + } buf; + struct attrlist attrList; + + memset(&attrList, 0, sizeof(attrList)); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.dirattr = ATTR_DIR_LINKCOUNT; + if (-1 == getattrlist(p->fts_path, &attrList, &buf, sizeof(buf), 0)) + return 0; + if (buf.linkcount == 1) + return 0; + st = p->fts_statp; + + /* If necessary, initialize the hash table. */ + if (buckets == NULL) { + number_buckets = links_hash_initial_size; + buckets = malloc(number_buckets * sizeof(buckets[0])); + if (buckets == NULL) + errx(1, "No memory for directory hardlink detection"); + for (i = 0; i < number_buckets; i++) + buckets[i] = NULL; + } + + /* If the hash table is getting too full, enlarge it. */ + if (number_entries > number_buckets * 10 && !stop_allocating) { + new_size = number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + /* Try releasing the free list to see if that helps. */ + if (new_buckets == NULL && free_list != NULL) { + while (free_list != NULL) { + le = free_list; + free_list = le->next; + free(le); + } + new_buckets = malloc(new_size * sizeof(new_buckets[0])); + } + + if (new_buckets == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking directory hard links"); + } else { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < number_buckets; i++) { + while (buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = buckets[i]; + buckets[i] = le->next; + + /* Add entry to new bucket. */ + hash = (le->dev ^ le->ino) % new_size; + + if (new_buckets[hash] != NULL) + new_buckets[hash]->previous = + le; + le->next = new_buckets[hash]; + le->previous = NULL; + new_buckets[hash] = le; + } + } + free(buckets); + buckets = new_buckets; + number_buckets = new_size; + } + } + + /* Try to locate this entry in the hash table. */ + hash = ( st->st_dev ^ st->st_ino ) % number_buckets; + for (le = buckets[hash]; le != NULL; le = le->next) { + if (le->dev == st->st_dev && le->ino == st->st_ino) { + /* + * Save memory by releasing an entry when we've seen + * all of it's links. + */ + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (buckets[hash] == le) + buckets[hash] = le->next; + number_entries--; + /* Recycle this node through the free list */ + if (stop_allocating) { + free(le); + } else { + le->next = free_list; + free_list = le; + } + } + return (1); + } + } + + if (stop_allocating) + return (0); + /* Add this entry to the links cache. */ + if (free_list != NULL) { + /* Pull a node from the free list if we can. */ + le = free_list; + free_list = le->next; + } else + /* Malloc one if we have to. */ + le = malloc(sizeof(struct links_entry)); + if (le == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + return (0); + } + le->dev = st->st_dev; + le->ino = st->st_ino; + le->links = buf.linkcount - 1; + number_entries++; + le->next = buckets[hash]; + le->previous = NULL; + if (buckets[hash] != NULL) + buckets[hash]->previous = le; + buckets[hash] = le; + return (0); +} + +/* + * Output in "human-readable" format. Uses 3 digits max and puts + * unit suffixes at the end. Makes output compact and easy to read, + * especially on huge disks. + * + */ +unit_t +unit_adjust(double *val) +{ + double abval; + unit_t unit; + unsigned int unit_sz; + + abval = fabs(*val); + + unit_sz = abval ? ilogb(abval) / 10 : 0; + + if (unit_sz >= UNIT_MAX) { + unit = NONE; + } else { + unit = unitp[unit_sz]; + *val /= (double)valp[unit_sz]; + } + + return (unit); +} + +void +prthumanval(double bytes) +{ + unit_t unit; + + bytes *= 512; + unit = unit_adjust(&bytes); + + if (bytes == 0) + (void)printf(" 0B"); + else if (bytes > 10) + (void)printf("%3.0f%c", bytes, "BKMGTPE"[unit]); + else + (void)printf("%3.1f%c", bytes, "BKMGTPE"[unit]); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k | -m | -g] [-x] [-I mask] [file ...]\n"); + exit(EX_USAGE); +} + +void +ignoreadd(const char *mask) +{ + struct ignentry *ign; + + ign = calloc(1, sizeof(*ign)); + if (ign == NULL) + errx(1, "cannot allocate memory"); + ign->mask = strdup(mask); + if (ign->mask == NULL) + errx(1, "cannot allocate memory"); + SLIST_INSERT_HEAD(&ignores, ign, next); +} + +void +ignoreclean(void) +{ + struct ignentry *ign; + + while (!SLIST_EMPTY(&ignores)) { + ign = SLIST_FIRST(&ignores); + SLIST_REMOVE_HEAD(&ignores, next); + free(ign->mask); + free(ign); + } +} + +int +ignorep(FTSENT *ent) +{ + struct ignentry *ign; + +#ifdef __APPLE__ + if (S_ISDIR(ent->fts_statp->st_mode) && !strcmp("fd", ent->fts_name)) { + struct statfs sfsb; + int rc = statfs(ent->fts_accpath, &sfsb); + if (rc >= 0 && !strcmp("devfs", sfsb.f_fstypename)) { + /* Don't cd into /dev/fd/N since one of those is likely to be + the cwd as of the start of du which causes all manner of + unpleasant surprises */ + return 1; + } + } +#endif /* __APPLE__ */ + SLIST_FOREACH(ign, &ignores, next) + if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH) + return 1; + return 0; +} |