Subject: Patch #2 for df(1)
To: None <tech-userlevel@netbsd.org>
From: Tomas Svensson <tsn@gbdev.net>
List: tech-userlevel
Date: 11/19/2001 21:33:56
Here is a new patch for df(1) with -H added, manpage updated and it's no
longer using FP math (even if its not really as simple as Brian suggests
if we want to avoid precision loss and round sizes properly).

The P-flag is just ignored if combined with -h or -H (does the same on
GNU, depends on the order on FreeBSD). Changing this behavior will not
make a lot of sense unless all exclusive flags give an error when used
together.

-Tomas


diff -u df1/df.1 df/df.1
--- df1/df.1	Sun Nov 18 23:08:29 2001
+++ df/df.1	Mon Nov 19 18:22:27 2001
@@ -41,7 +41,7 @@
 .Nd display free disk space
 .Sh SYNOPSIS
 .Nm
-.Op Fl aiklmnP
+.Op Fl aghHiklmnP
 .Op Fl t Ar type
 .Op Ar file | Ar file_system ...
 .Sh DESCRIPTION
@@ -51,7 +51,7 @@
 or on the file system of which
 .Ar file
 is a part.
-Values are displayed in 512-byte per block block counts.
+Values are displayed in 512-byte per block counts.
 If neither a file or a
 .Ar file_system
 operand is specified,
@@ -74,22 +74,30 @@
 Show all filesystems. By default only filesystems mounted with the
 .Dv MNT_IGNORE
 flag clear are shown.
+.It Fl g
+Use 1073741824-byte (gigabyte) block counts rather than the default. This
+overrides the BLOCKSIZE specification from the environment.
+.It Fl h
+Display sizes in a more "human-readable" and compact form. Use unit suffixes: 
+Byte, Kilobyte, Megabyte, Gigabyte, Terabyte, Petabyte and Exabyte in order to
+reduce the number of digits to four or fewer using base 2 for sizes.
+.It Fl H
+Same as
+.Fl h
+but using base 10 for sizes.
 .It Fl i
 Include statistics on the number of free inodes.
 .It Fl k
-By default, all sizes are reported in 512-byte block counts.
-The
-.Fl k
-option causes the numbers to be reported in kilobyte (1024 bytes) counts.
+Use 1024-byte (kilobyte) block counts rather than the default. This overrides
+the BLOCKSIZE specification from the environment.
 .It Fl l
 Display statistics only about mounted file systems with the
 .Dv MNT_LOCAL
 flag set.  If a non-local file system is given as an argument, a
 warning is issued and no information is given on that file system.
 .It Fl m
-The
-.Fl m
-option causes the numbers to be reported in megabyte (1024*1024 bytes) counts.
+Use 1048576-byte (megabyte) block counts rather than the default. This
+overrides the BLOCKSIZE specification from the environment.
 .It Fl n
 Print out the previously obtained statistics from the file systems.
 This option should be used if it is possible that one or more
@@ -146,6 +154,9 @@
 If the environment variable
 .Ev BLOCKSIZE
 is set, and the
+.Fl g
+.Fl h
+.Fl H
 .Fl k
 and
 .Fl m
diff -u df1/df.c df/df.c
--- df1/df.c	Sun Nov 18 23:08:29 2001
+++ df/df.c	Mon Nov 19 18:30:22 2001
@@ -72,14 +72,15 @@
 int	 main(int, char *[]);
 int	 bread(off_t, void *, int);
 char	*getmntpt(char *);
-void	 prtstat(struct statfs *, int);
+void	 prthuman(int64_t, int);
+void	 prtstat(struct statfs *, int, int);
 int	 ufs_df(char *, struct statfs *);
 int	 selected(const char *);
 void	 maketypelist(char *);
 long	 regetmntinfo(struct statfs **, long);
 void	 usage(void);
 
-int	aflag, iflag, kflag, lflag, mflag, nflag, Pflag;
+int	aflag, gflag, hflag, iflag, kflag, lflag, mflag, nflag, Pflag;
 char	**typelist = NULL;
 struct	ufs_args mdev;
 
@@ -89,14 +90,25 @@
 	struct stat stbuf;
 	struct statfs *mntbuf;
 	long mntsize;
-	int ch, i, maxwidth, width;
+	int base, ch, i, maxwidth, width;
 	char *mntpt;
 
-	while ((ch = getopt(argc, argv, "aiklmnPt:")) != -1)
+	while ((ch = getopt(argc, argv, "aghHiklmnPt:")) != -1)
 		switch (ch) {
 		case 'a':
 			aflag = 1;
 			break;
+		case 'g':
+			gflag = 1;
+			break;
+		case 'h':
+			hflag = 1;
+			base = 1024;
+			break;
+		case 'H':
+			hflag = 1;
+			base = 1000;
+			break;
 		case 'i':
 			iflag = 1;
 			break;
@@ -203,7 +215,7 @@
 			maxwidth = width;
 	}
 	for (i = 0; i < mntsize; i++)
-		prtstat(&mntbuf[i], maxwidth);
+		prtstat(&mntbuf[i], maxwidth, base);
 	exit(0);
 	/* NOTREACHED */
 }
@@ -326,10 +338,49 @@
 	    (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
 
 /*
+ * Print statistics in a more "human-readable" and compact form.
+ */
+void
+prthuman(int64_t bytes, int base)
+{
+	int i, neg, size1, size2, unit;
+
+	if (bytes == 0)
+		(void)printf("     0B");
+	else {
+		i = 0;
+		if (bytes < 0) {
+			bytes *= -100;
+			neg = -1;
+		} else {
+			bytes *= 100;
+			neg = 1;
+		}
+		for(unit = -1; unit == -1; i++)
+			if ((bytes / 100) < base || i == 6)
+				unit = i;
+			else
+				bytes /= base;
+		if (bytes > 1000)
+			(void)printf(" %5lli%c",(neg * ((bytes + 50) / 100)),
+			    "BKMGTPE"[unit]);
+		else {
+			size1 = bytes / 100;
+			if ((size2 = (((bytes % 100) + 5) / 10)) == 10) {
+				size1++;
+				size2 = 0;
+			}
+			(void)printf(" %3i.%1i%c", neg * size1,	neg * size2,
+			    "BKMGTPE"[unit]);
+		}
+	}
+}
+
+/*
  * Print out status about a filesystem.
  */
 void
-prtstat(struct statfs *sfsp, int maxwidth)
+prtstat(struct statfs *sfsp, int maxwidth, int base)
 {
 	static long blocksize;
 	static int headerlen, timesthrough;
@@ -340,19 +391,30 @@
 	if (maxwidth < 11)
 		maxwidth = 11;
 	if (++timesthrough == 1) {
-		if (kflag) {
-			blocksize = 1024;
-			header = Pflag ? "1024-blocks" : "1K-blocks";
-			headerlen = strlen(header);
-		} else if (mflag) {
-			blocksize = 1024 * 1024;
-			header = Pflag ? "1048576-blocks" : "1M-blocks";
-			headerlen = strlen(header);
-		} else
-			header = getbsize(&headerlen, &blocksize);
-		(void)printf("%-*.*s %s     Used %9s Capacity",
-		    maxwidth, maxwidth, "Filesystem", header,
-		    Pflag ? "Available" : "Avail");
+		if (hflag)
+			(void)printf("%-*.*s   Size   Used  Avail Capacity",
+			    maxwidth, maxwidth, "Filesystem");
+		else {
+			if (kflag) {
+				blocksize = 1024;
+				header = Pflag ? "1024-blocks" : "1K-blocks";
+				headerlen = strlen(header);
+			} else if (mflag) {
+				blocksize = 1024 * 1024;
+				header = Pflag ? "1048576-blocks" :
+				    "1M-blocks";
+				headerlen = strlen(header);
+			} else if (gflag) {
+				blocksize = 1024 * 1024 * 1024;
+				header = Pflag ? "1073741824-blocks" :
+				    "1G-blocks";
+				headerlen = strlen(header);
+			} else
+				header = getbsize(&headerlen, &blocksize);
+			(void)printf("%-*.*s %s     Used %9s Capacity",
+			    maxwidth, maxwidth, "Filesystem", header,
+			    Pflag ? "Available" : "Avail");
+		}
 		if (iflag)
 			(void)printf(" iused   ifree  %%iused");
 		(void)printf("  Mounted on\n");
@@ -360,10 +422,15 @@
 	(void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname);
 	used = sfsp->f_blocks - sfsp->f_bfree;
 	availblks = sfsp->f_bavail + used;
-	(void)printf(" %*ld %8ld %9ld", headerlen,
-	    fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
-	    fsbtoblk(used, sfsp->f_bsize, blocksize),
-	    fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize));
+	if (hflag) {
+		prthuman((int64_t)sfsp->f_blocks * (int64_t)sfsp->f_bsize, base);
+		prthuman((int64_t)used * (int64_t)sfsp->f_bsize, base);
+		prthuman((int64_t)sfsp->f_bavail * (int64_t)sfsp->f_bsize, base);
+	} else
+		(void)printf(" %*ld %8ld %9ld", headerlen,
+		    fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
+		    fsbtoblk(used, sfsp->f_bsize, blocksize),
+		    fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize));
 	(void)printf("%7s",
 	    availblks == 0 ? full : strpct((u_long)used, (u_long)availblks, 0));
 	if (iflag) {
@@ -460,7 +527,7 @@
 {
 
 	(void)fprintf(stderr,
-	    "Usage: %s [-aiklmnP] [-t type] [file | file_system ...]\n",
+	    "Usage: %s [-aghHiklmnP] [-t type] [file | file_system ...]\n",
 	    getprogname());
 	exit(1);
 	/* NOTREACHED */