From 4aaa5377b32f40e614c177a3d061ada5b35cee82 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Sun, 4 Mar 2012 00:53:03 +0100 Subject: [PATCH] Import NetBSD du(1) --- commands/Makefile | 2 +- commands/du/Makefile | 5 - commands/du/du.c | 259 ----------------------------- man/man1/Makefile | 2 +- man/man1/du.1 | 38 ----- tools/nbsd_ports | 1 + tools/release.functions | 4 +- tools/release.sh | 2 +- usr.bin/Makefile | 2 +- usr.bin/du/Makefile | 6 + usr.bin/du/du.1 | 163 +++++++++++++++++++ usr.bin/du/du.c | 350 ++++++++++++++++++++++++++++++++++++++++ 12 files changed, 526 insertions(+), 308 deletions(-) delete mode 100644 commands/du/Makefile delete mode 100644 commands/du/du.c delete mode 100644 man/man1/du.1 create mode 100644 usr.bin/du/Makefile create mode 100644 usr.bin/du/du.1 create mode 100644 usr.bin/du/du.c diff --git a/commands/Makefile b/commands/Makefile index 3d1d1d548..69d5b5444 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -9,7 +9,7 @@ SUBDIR= add_route arp ash at awk \ chmod chown chroot ci cksum cleantmp clear cmp co \ comm compress cp crc cron crontab cut \ dd decomp16 DESCRIBE dev2name devsize df dhcpd \ - dhrystone diff dirname diskctl du dumpcore \ + dhrystone diff dirname diskctl dumpcore \ ed eject elvis env expand factor fbdctl file \ find finger fingerd fix fold format fortune fsck.mfs \ ftp101 gcore gcov-pull getty grep head hexdump host \ diff --git a/commands/du/Makefile b/commands/du/Makefile deleted file mode 100644 index 2f509c421..000000000 --- a/commands/du/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -PROG= du -CPPFLAGS+= -I${MINIXSRCDIR}/servers -MAN= - -.include diff --git a/commands/du/du.c b/commands/du/du.c deleted file mode 100644 index 64c9a8410..000000000 --- a/commands/du/du.c +++ /dev/null @@ -1,259 +0,0 @@ -/* du - report on disk usage Author: Alistair G. Crooks */ - -/* - * du.c 1.1 27/5/87 agc Joypace Ltd. - * 1.2 24 Mar 89 nick@nswitgould.oz - * 1.3 31 Mar 89 nick@nswitgould.oz - * 1.4 22 Feb 90 meulenbr@cst.prl.philips.nl - * 1.5 09 Jul 91 hp@vmars.tuwien.ac.at - * 1.6 01 Oct 92 kjb@cs.vu.nl - * 1.7 04 Jan 93 bde - * 1.8 19 Sep 94 kjb - * 1.9 28 Oct 99 kjb - * - * Copyright 1987, Joypace Ltd., London UK. All rights reserved. - * This code may be freely distributed, provided that this notice - * remains attached. - * - * du - a public domain interpretation of du(1). - * - * 1.2: Fixed bug involving 14 character long filenames - * 1.3: Add [-l levels] option to restrict printing. - * 1.4: Added processing of multiple arguments - * 1.5: Fixed processing of multiple arguments. General cleanup. - * 1.6: Use readdir - * 1.7: Merged 1.5 and 1.6. - * Print totals even for non-dirs at top level. - * Count blocks for each dir before printing total for the dir. - * Count blocks for all non-special files. - * Don't clutter link buffer with directories. - * 1.8: Remember all links. - * 1.9: Added -x flag to not cross device boundaries. Type fixes. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mfs/const.h" - -extern char *optarg; -extern int optind; - -#define LINELEN 256 -#define NR_ALREADY 512 - -#ifdef S_IFLNK -#define LSTAT lstat -#else -#define LSTAT stat -#endif - -typedef struct already { - struct already *al_next; - int al_dev; - ino_t al_inum; - nlink_t al_nlink; -} ALREADY; - -_PROTOTYPE(int main, (int argc, char **argv)); -PRIVATE _PROTOTYPE(int makedname, (char *d, char *f, char *out, int outlen)); -_PROTOTYPE(int done, (dev_t dev, ino_t inum, nlink_t nlink)); -_PROTOTYPE(long dodir, (char *d, int thislev, dev_t dev)); - -char *prog; /* program name */ -char *optstr = "aFsxdl:"; /* options */ -int silent = 0; /* silent mode */ -int all = 0; /* all directory entries mode */ -int crosschk = 0; /* do not cross device boundaries mode */ -int fsoverhead = 0; /* include FS overhead */ -char *startdir = "."; /* starting from here */ -int levels = 20000; /* # of directory levels to print */ -ALREADY *already[NR_ALREADY]; -int alc; - - -/* - * makedname - make the pathname from the directory name, and the - * directory entry, placing it in out. If this would overflow, - * return 0, otherwise 1. - */ -PRIVATE int makedname(char *d, char *f, char *out, int outlen) -{ - char *cp; - int length = strlen(f); - - if (strlen(d) + length + 2 > outlen) return(0); - for (cp = out; *d; *cp++ = *d++); - if (*(cp - 1) != '/') *cp++ = '/'; - while (length--) *cp++ = *f++; - *cp = '\0'; - return(1); -} - -/* - * done - have we encountered (dev, inum) before? Returns 1 for yes, - * 0 for no, and remembers (dev, inum, nlink). - */ -int done(dev_t dev, ino_t inum, nlink_t nlink) -{ - register ALREADY **pap, *ap; - - if (fsoverhead) return 0; - - pap = &already[(unsigned) inum % NR_ALREADY]; - while ((ap = *pap) != NULL) { - if (ap->al_inum == inum && ap->al_dev == dev) { - if (--ap->al_nlink == 0) { - *pap = ap->al_next; - free(ap); - } - return(1); - } - pap = &ap->al_next; - } - if ((ap = malloc(sizeof(*ap))) == NULL) { - fprintf(stderr, "du: Out of memory\n"); - exit(1); - } - ap->al_next = NULL; - ap->al_inum = inum; - ap->al_dev = dev; - ap->al_nlink = nlink - 1; - *pap = ap; - return(0); -} - -int get_block_size(const char *dir, const struct stat *st) -{ - struct statfs stfs; - static int fs_block_size = -1, fs_dev = -1; - int d; - - if(st->st_dev == fs_dev) - return fs_block_size; - - if((d = open(dir, O_RDONLY)) < 0) { - perror(dir); - return 0; - } - - if(fstatfs(d, &stfs) < 0) { - perror(dir); - return 0; - } - - fs_block_size = stfs.f_bsize; - fs_dev = st->st_dev; - - return fs_block_size; -} - - -/* - * dodir - process the directory d. Return the long size (in blocks) - * of d and its descendants. - */ -long dodir(char *d, int thislev, dev_t dev) -{ - int maybe_print; - struct stat s; - long indir_blocks, indir2_blocks, indir_per_block; - long total_blocks, total_kb; - char dent[LINELEN]; - DIR *dp; - struct dirent *entry; - int block_size; - - if (LSTAT(d, &s) < 0) { - fprintf(stderr, - "%s: %s: %s\n", prog, d, strerror(errno)); - return 0L; - } - if (s.st_dev != dev && dev != 0 && crosschk) return 0; - block_size = get_block_size(d, &s); - if(block_size < 1) { - fprintf(stderr, - "%s: %s: funny block size found (%d)\n", - prog, d, block_size); - return 0L; - } - total_blocks = (s.st_size + (block_size - 1)) / block_size; - if (fsoverhead) { - /* file system overhead: indirect blocks */ - indir_per_block = block_size / sizeof(zone_t); - indir_blocks = (total_blocks - V2_NR_DZONES) / indir_per_block; - total_blocks += indir_blocks; - indir2_blocks = (indir_blocks - 1) / (indir_per_block * indir_per_block); - total_blocks += indir2_blocks; - } - total_kb = total_blocks * block_size / 1024; - switch (s.st_mode & S_IFMT) { - case S_IFDIR: - /* Directories should not be linked except to "." and "..", so this - * directory should not already have been done. - */ - maybe_print = !silent; - if ((dp = opendir(d)) == NULL) break; - while ((entry = readdir(dp)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || - strcmp(entry->d_name, "..") == 0) - continue; - if (!makedname(d, entry->d_name, dent, sizeof(dent))) continue; - total_kb += dodir(dent, thislev - 1, s.st_dev); - } - closedir(dp); - break; - case S_IFBLK: - case S_IFCHR: - /* st_size for special files is not related to blocks used. */ - total_kb = 0; - /* Fall through. */ - default: - if (s.st_nlink > 1 && done(s.st_dev, s.st_ino, s.st_nlink)) return 0L; - maybe_print = all; - break; - } - if (thislev >= levels || (maybe_print && thislev >= 0)) { - printf("%ld\t%s\n", total_kb, d); - } - return(total_kb); -} - -int main(argc, argv) -int argc; -char **argv; -{ - int c; - - prog = argv[0]; - while ((c = getopt(argc, argv, optstr)) != EOF) switch (c) { - case 'a': all = 1; break; - case 's': silent = 1; break; - case 'x': - case 'd': crosschk = 1; break; - case 'F': fsoverhead = 1; break; - case 'l': levels = atoi(optarg); break; - default: - fprintf(stderr, - "Usage: %s [-asxF] [-l levels] [startdir]\n", prog); - exit(1); - } - do { - if (optind < argc) startdir = argv[optind++]; - alc = 0; - (void) dodir(startdir, levels, 0); - } while (optind < argc); - return(0); -} diff --git a/man/man1/Makefile b/man/man1/Makefile index 8f99fa973..8c03176b1 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -3,7 +3,7 @@ MAN= ash.1 at.1 banner.1 basename.1 \ calendar.1 cat.1 cawf.1 chgrp.1 \ chmod.1 cksum.1 clear.1 cmp.1 comm.1 compress.1 \ cp.1 crc.1 crontab.1 ctags.1 dd.1 dev2name.1 \ - df.1 dhrystone.1 dosdir.1 dosread.1 doswrite.1 du.1 \ + df.1 dhrystone.1 dosdir.1 dosread.1 doswrite.1 \ dumpcore.1 echo.1 ed.1 eject.1 elvis.1 elvrec.1 \ env.1 expand.1 expr.1 factor.1 file.1 \ finger.1 flexdoc.1 fmt.1 fold.1 format.1 fortune.1 \ diff --git a/man/man1/du.1 b/man/man1/du.1 deleted file mode 100644 index 9bd10399c..000000000 --- a/man/man1/du.1 +++ /dev/null @@ -1,38 +0,0 @@ -.TH DU 1 -.SH NAME -du \- print disk usage -.SH SYNOPSIS -\fBdu\fR [\fB\-as\fR]\fR [\fB\-l \fIn\fR] \fIdir\fR ...\fR -.br -.de FL -.TP -\\fB\\$1\\fR -\\$2 -.. -.de EX -.TP 20 -\\fB\\$1\\fR -# \\$2 -.. -.SH OPTIONS -.FL "\-a" "Give usage for all files" -.FL "\-F" "Include filesystem overhead for indirect blocks" -.FL "\-l" "List up to \fIn\fR levels of subdirectories" -.FL "\-d" "Do not cross file system boundaries" -.FL "\-s" "Summary only" -.SH EXAMPLES -.EX "du dir" "List disk space used by files in dir" -.EX "du \-s dir1 dir2" "Give summaries only" -.EX "du \-d /" "Show only the root device" -.SH DESCRIPTION -.PP -\fIDu\fR examines one or more directories and prints the amount of space -occupied by the files in those directories and their subdirectories in -kilobytes. -.SH BUGS -\fIDu\fR calculates disk usage based on file size. It could be there -are holes in the file, not occupying any actual disk blocks, causing -\fIdu\fR to overestimate disk usage. (\fIDu\fR does recognize and correctly -calculate disk usage when hard links are encountered.) -.SH "SEE ALSO" -.BR df (1). diff --git a/tools/nbsd_ports b/tools/nbsd_ports index becb4653f..fcbdf626b 100644 --- a/tools/nbsd_ports +++ b/tools/nbsd_ports @@ -33,6 +33,7 @@ usr.bin/mkdep src/usr.bin/mkdep usr.bin/newgrp src/usr.bin/newgrp usr.bin/uniq src/usr.bin/uniq usr.bin/seq src/usr.bin/seq +usr.bin/du src/usr.bin/du usr.bin/man src/usr.bin/man usr.bin/apropos src/usr.bin/apropos usr.bin/mdocml src/external/bsd/mdocml diff --git a/tools/release.functions b/tools/release.functions index 537993ddb..2719f0af3 100644 --- a/tools/release.functions +++ b/tools/release.functions @@ -194,9 +194,9 @@ fitfs() inodes="`expr $inodes + $extra_inodes`" # Determine number of data zones - zonekbs=`du -Fs $path | cut -d' ' -f1` + zonekbs=`du -ks $path | cut -d' ' -f1` zonekbsignore=0 - [ ! -d $path/usr ] || zonekbsignore=`du -Fs $path/usr | cut -d" " -f1` + [ ! -d $path/usr ] || zonekbsignore=`du -ks $path/usr | cut -d" " -f1` zones="`expr \( $zonekbs - $zonekbsignore \) / \( $BS / 1024 \) + $extra_zones`" # Determine file system size diff --git a/tools/release.sh b/tools/release.sh index fb7582e91..13f8b1ed6 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -304,7 +304,7 @@ then echo "Created new minix install in $RELEASEDIR." fi echo " * Counting files" -extrakb=`du -s $RELEASEDIR/usr/install | awk '{ print $1 }'` +extrakb=`du -ks $RELEASEDIR/usr/install | awk '{ print $1 }'` find $RELEASEDIR/usr | fgrep -v /install/ | wc -l >$RELEASEDIR/.usrfiles find $RELEASEDIR -print -path $RELEASEDIR/usr -prune | wc -l >$RELEASEDIR/.rootfiles diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 6716ac68a..9674cd9ac 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -3,7 +3,7 @@ .include # NetBSD imports -SUBDIR= indent m4 stat tic sed mkdep uniq seq man mdocml \ +SUBDIR= indent m4 stat tic sed mkdep uniq seq du man mdocml \ apropos chpass newgrp passwd bzip2 bzip2recover gzip # Non-NetBSD imports diff --git a/usr.bin/du/Makefile b/usr.bin/du/Makefile new file mode 100644 index 000000000..20c5426ff --- /dev/null +++ b/usr.bin/du/Makefile @@ -0,0 +1,6 @@ +# $NetBSD: Makefile,v 1.9 2009/04/14 22:15:19 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= du + +.include diff --git a/usr.bin/du/du.1 b/usr.bin/du/du.1 new file mode 100644 index 000000000..d01c7126e --- /dev/null +++ b/usr.bin/du/du.1 @@ -0,0 +1,163 @@ +.\" $NetBSD: du.1,v 1.21 2006/09/24 07:19:16 wiz Exp $ +.\" +.\" 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. 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 +.\" +.Dd September 24, 2006 +.Dt DU 1 +.Os +.Sh NAME +.Nm du +.Nd display disk usage statistics +.Sh SYNOPSIS +.Nm +.Op Fl H | Fl L | Fl P +.Op Fl a | Fl d Ar depth | Fl s +.Op Fl cghkmnrx +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm +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 Ds +.It Fl H +Symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +All symbolic links are followed. +.It Fl P +No symbolic links are followed. +.It Fl a +Display an entry for each file in the file hierarchy. +.It Fl c +Display the grand total after all the arguments have been processed. +.It Fl d +Display an entry files and directories +.Ar depth +directories deep. +.It Fl g +If the +.Fl g +flag is specified, the number displayed is the number of gigabyte +(1024*1024*1024 bytes) blocks. +.It Fl h +If the +.Fl h +flag is specified, the numbers will be displayed in "human-readable" +format. +Use unit suffixes: B (Byte), K (Kilobyte), M (Megabyte), G (Gigabyte), +T (Terabyte) and P (Petabyte). +.It Fl k +By default, +.Nm +displays the number of blocks as returned by the +.Xr stat 2 +system call, i.e. 512-byte blocks. +If the +.Fl k +flag is specified, the number displayed is the number of kilobyte +(1024 bytes) blocks. +Partial numbers of blocks are rounded up. +.It Fl m +If the +.Fl m +flag is specified, the number displayed is the number of megabyte +(1024*1024 bytes) blocks. +.It Fl n +Ignore files and directories with user +.Qq nodump +flag +.Pq Dv UF_NODUMP +set. +.It Fl r +Generate warning messages about directories that cannot be read. +This is the default behaviour. +.It Fl s +Display only the grand total for the specified files. +.It Fl x +Filesystem mount points are not traversed. +.El +.Pp +.Nm +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. +The +.Fl H , +.Fl L +and +.Fl P +options override each other and the command's actions are determined +by the last one specified. +.Pp +Files having multiple hard links are counted (and displayed) a single +time per +.Nm +execution. +.Sh ENVIRONMENT +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, and the +.Fl g , +.Fl h , +.Fl k , +and +.Fl m +options are not specified, the block counts will be displayed in units of that +size block. +.El +.Sh SEE ALSO +.Xr df 1 , +.Xr chflags 2 , +.Xr fts 3 , +.Xr getbsize 3 , +.Xr symlink 7 , +.Xr quot 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c new file mode 100644 index 000000000..1b69f480d --- /dev/null +++ b/usr.bin/du/du.c @@ -0,0 +1,350 @@ +/* $NetBSD: du.c,v 1.35 2011/09/01 13:37:33 joerg Exp $ */ + +/* + * 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. 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 +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: du.c,v 1.35 2011/09/01 13:37:33 joerg Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int linkchk(dev_t, ino_t); +static void prstat(const char *, int64_t); +__dead static void usage(void); + +static int hflag; +static long blocksize; + +int +main(int argc, char *argv[]) +{ + FTS *fts; + FTSENT *p; + int64_t totalblocks; + int ftsoptions, listfiles; + int depth; + int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag; + const char *noargv[2]; + + Hflag = Lflag = aflag = cflag = dflag = gkmflag = nflag = sflag = 0; + totalblocks = 0; + ftsoptions = FTS_PHYSICAL; + depth = INT_MAX; + while ((ch = getopt(argc, argv, "HLPacd:ghkmnrsx")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + depth = atoi(optarg); + if (depth < 0 || depth > SHRT_MAX) { + warnx("invalid argument to option d: %s", + optarg); + usage(); + } + break; + case 'g': + blocksize = 1024 * 1024 * 1024; + gkmflag = 1; + break; + case 'h': + hflag = 1; + break; + case 'k': + blocksize = 1024; + gkmflag = 1; + break; + case 'm': + blocksize = 1024 * 1024; + gkmflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'r': + break; + case 's': + sflag = 1; + 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) + ftsoptions |= FTS_COMFOLLOW; + if (Lflag) { + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + } + + listfiles = 0; + if (aflag) { + if (sflag || dflag) + usage(); + listfiles = 1; + } else if (sflag) { + if (dflag) + usage(); + depth = 0; + } + + if (!*argv) { + noargv[0] = "."; + noargv[1] = NULL; + argv = __UNCONST(noargv); + } + + if (!gkmflag) + (void)getbsize(NULL, &blocksize); + blocksize /= 512; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) + err(1, "fts_open `%s'", *argv); + + for (rval = 0; (p = fts_read(fts)) != NULL;) { + if (nflag) { + switch (p->fts_info) { + case FTS_NS: + case FTS_SLNONE: + /* nothing */ + break; + default: + if (p->fts_statp->st_flags & UF_NODUMP) { + fts_set(fts, p, FTS_SKIP); + continue; + } + } + } + switch (p->fts_info) { + case FTS_D: /* Ignore. */ + break; + case FTS_DP: + p->fts_parent->fts_number += + p->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + /* + * If listing each directory, or not listing files + * or directories and this is post-order of the + * root of a traversal, display the total. + */ + if (p->fts_level <= depth + || (!listfiles && !p->fts_level)) + prstat(p->fts_path, p->fts_number); + break; + case FTS_DC: /* Ignore. */ + 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; + default: + if (p->fts_statp->st_nlink > 1 && + linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino)) + break; + /* + * If listing each file, or a non-directory file was + * the root of a traversal, display the total. + */ + if (listfiles || !p->fts_level) + prstat(p->fts_path, p->fts_statp->st_blocks); + p->fts_parent->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + } + } + if (errno) + err(1, "fts_read"); + if (cflag) + prstat("total", totalblocks); + exit(rval); +} + +static void +prstat(const char *fname, int64_t blocks) +{ + if (hflag) { + char buf[5]; + int64_t sz = blocks * 512; + + humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE, + HN_B | HN_NOSPACE | HN_DECIMAL); + + (void)printf("%s\t%s\n", buf, fname); + } else + (void)printf("%lld\t%s\n", + (long long)howmany(blocks, (int64_t)blocksize), + fname); +} + +static int +linkchk(dev_t dev, ino_t ino) +{ + static struct entry { + dev_t dev; + ino_t ino; + } *htable; + static int htshift; /* log(allocated size) */ + static int htmask; /* allocated size - 1 */ + static int htused; /* 2*number of insertions */ + static int sawzero; /* Whether zero is in table or not */ + int h, h2; + uint64_t tmp; + /* this constant is (1<<64)/((1+sqrt(5))/2) + * aka (word size)/(golden ratio) + */ + const uint64_t HTCONST = 11400714819323198485ULL; + const int HTBITS = CHAR_BIT * sizeof(tmp); + + /* Never store zero in hashtable */ + if (dev == 0 && ino == 0) { + h = sawzero; + sawzero = 1; + return h; + } + + /* Extend hash table if necessary, keep load under 0.5 */ + if (htused<<1 >= htmask) { + struct entry *ohtable; + + if (!htable) + htshift = 10; /* starting hashtable size */ + else + htshift++; /* exponential hashtable growth */ + + htmask = (1 << htshift) - 1; + htused = 0; + + ohtable = htable; + htable = calloc(htmask+1, sizeof(*htable)); + if (!htable) + err(1, "calloc"); + + /* populate newly allocated hashtable */ + if (ohtable) { + int i; + for (i = 0; i <= htmask>>1; i++) + if (ohtable[i].ino || ohtable[i].dev) + linkchk(ohtable[i].dev, ohtable[i].ino); + free(ohtable); + } + } + + /* multiplicative hashing */ + tmp = dev; + tmp <<= HTBITS>>1; + tmp |= ino; + tmp *= HTCONST; + h = tmp >> (HTBITS - htshift); + h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ + + /* open address hashtable search with double hash probing */ + while (htable[h].ino || htable[h].dev) { + if ((htable[h].ino == ino) && (htable[h].dev == dev)) + return 1; + h = (h + h2) & htmask; + } + + /* Insert the current entry into hashtable */ + htable[h].dev = dev; + htable[h].ino = ino; + htused++; + return 0; +} + +static void +usage(void) +{ + + (void)fprintf(stderr, + "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cghkmnrx] [file ...]\n"); + exit(1); +}