Currently df(1) has fixed width columns. With bigger filesystems the
output can become unaligned. On a 100GB filesystem that I have the number
of free inodes is over 10,000,000, which breaks alignment. On another
filesystem that is 3TB in size the number of available 512-blocks is over
5,000,000,000 which also breaks alignment. This is shown in the daily
output email.

I rewrote the display parts of df to first populate an array with
values, and then output it in a way that ensures alignment. A diff is
attached. This is a draft, it needs refinement. Is this something that
would be accepted?

An alternative would be to port FreeBSD's df. Some modifications are
needed though, most significantly it uses Juniper's libxo for output.


Index: df.c
===================================================================
RCS file: /cvs/src/bin/df/df.c,v
retrieving revision 1.54
diff -u -p -r1.54 df.c
--- df.c        9 Oct 2015 01:37:06 -0000       1.54
+++ df.c        3 Feb 2016 10:55:20 -0000
@@ -43,6 +43,7 @@
 #include <fcntl.h>
 #include <math.h>
 #include <stdio.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -50,6 +51,14 @@
 
 extern char *__progname;
 
+struct col {
+       char    *c_label;
+       int      c_width;
+       char    **c_vals;
+};
+
+#define COLS 9
+
 char   *getmntpt(char *);
 int     selected(const char *);
 void    maketypelist(char *);
@@ -62,6 +71,10 @@ void  usage(void);
 void    prthumanval(long long);
 void    prthuman(struct statfs *sfsp, unsigned long long);
 
+static void fill_table(struct col *, struct statfs *, int);
+static void print_table(struct col *, int);
+static char *df_getbsize(long *);
+
 int            raw_df(char *, struct statfs *);
 extern int     ffs_df(int, char *, struct statfs *);
 extern int     e2fs_df(int, char *, struct statfs *);
@@ -78,6 +91,7 @@ main(int argc, char *argv[])
        int ch, i;
        int width, maxwidth;
        char *mntpt;
+       struct col cols[COLS];
 
        if (pledge("stdio rpath", NULL) == -1)
                err(1, "pledge");
@@ -161,22 +175,8 @@ main(int argc, char *argv[])
                }
        }
 
-       if (mntsize) {
-               maxwidth = 0;
-               for (i = 0; i < mntsize; i++) {
-                       width = strlen(mntbuf[i].f_mntfromname);
-                       if (width > maxwidth)
-                               maxwidth = width;
-               }
-
-               if (maxwidth < 11)
-                       maxwidth = 11;
-
-               if (Pflag)
-                       posixprint(mntbuf, mntsize, maxwidth);
-               else
-                       bsdprint(mntbuf, mntsize, maxwidth);
-       }
+       fill_table(cols, mntbuf, mntsize);
+       print_table(cols, mntsize);
 
        exit(mntsize ? 0 : 1);
 }
@@ -277,151 +277,6 @@ regetmntinfo(struct statfs **mntbufp, lo
        return (j);
 }
 
-/*
- * "human-readable" output: use 3 digits max.--put unit suffixes at
- * the end.  Makes output compact and easy-to-read esp. on huge disks.
- * Code moved into libutil; this is now just a wrapper.
- */
-void
-prthumanval(long long bytes)
-{
-       char ret[FMT_SCALED_STRSIZE];
-
-       if (fmt_scaled(bytes, ret) == -1) {
-               (void)printf(" %lld", bytes);
-               return;
-       }
-       (void)printf(" %7s", ret);
-}
-
-void
-prthuman(struct statfs *sfsp, unsigned long long used)
-{
-       prthumanval(sfsp->f_blocks * sfsp->f_bsize);
-       prthumanval(used * sfsp->f_bsize);
-       prthumanval(sfsp->f_bavail * sfsp->f_bsize);
-}
-
-/*
- * Convert statfs returned filesystem size into BLOCKSIZE units.
- * Attempts to avoid overflow for large filesystems.
- */
-#define fsbtoblk(num, fsbs, bs) \
-       (((fsbs) != 0 && (fsbs) < (bs)) ? \
-               (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
-
-/*
- * Print out status about a filesystem.
- */
-void
-prtstat(struct statfs *sfsp, int maxwidth, int headerlen, int blocksize)
-{
-       u_int64_t used, inodes;
-       int64_t availblks;
-
-       (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname);
-       used = sfsp->f_blocks - sfsp->f_bfree;
-       availblks = sfsp->f_bavail + used;
-       if (hflag)
-               prthuman(sfsp, used);
-       else
-               (void)printf(" %*llu %9llu %9lld", 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(" %5.0f%%",
-           availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
-       if (iflag) {
-               inodes = sfsp->f_files;
-               used = inodes - sfsp->f_ffree;
-               (void)printf(" %7llu %7llu %5.0f%% ", used, sfsp->f_ffree,
-                  inodes == 0 ? 100.0 : (double)used / (double)inodes * 100.0);
-       } else
-               (void)printf("  ");
-       (void)printf("  %s\n", sfsp->f_mntonname);
-}
-
-/*
- * Print in traditional BSD format.
- */
-void
-bsdprint(struct statfs *mntbuf, long mntsize, int maxwidth)
-{
-       int i;
-       char *header;
-       int headerlen;
-       long blocksize;
-
-       /* Print the header line */
-       if (hflag) {
-               header = "   Size";
-               headerlen = strlen(header);
-               (void)printf("%-*.*s %s    Used   Avail Capacity",
-                            maxwidth, maxwidth, "Filesystem", header);
-       } else {
-               if (kflag) {
-                       blocksize = 1024;
-                       header = "1K-blocks";
-                       headerlen = strlen(header);
-               } else
-                       header = getbsize(&headerlen, &blocksize);
-               (void)printf("%-*.*s %s      Used     Avail Capacity",
-                            maxwidth, maxwidth, "Filesystem", header);
-       }
-       if (iflag)
-               (void)printf(" iused   ifree  %%iused");
-       (void)printf("  Mounted on\n");
-
-
-       for (i = 0; i < mntsize; i++)
-               prtstat(&mntbuf[i], maxwidth, headerlen, blocksize);
-       return;
-}
-
-/*
- * Print in format defined by POSIX 1002.2, invoke with -P option.
- */
-void
-posixprint(struct statfs *mntbuf, long mntsize, int maxwidth)
-{
-       int i;
-       int blocksize;
-       char *blockstr;
-       struct statfs *sfsp;
-       long long used, avail;
-       double percentused;
-
-       if (kflag) {
-               blocksize = 1024;
-               blockstr = "1024-blocks";
-       } else {
-               blocksize = 512;
-               blockstr = " 512-blocks";
-       }
-
-       (void)printf(
-           "%-*.*s %s       Used   Available Capacity Mounted on\n",
-           maxwidth, maxwidth, "Filesystem", blockstr);
-
-       for (i = 0; i < mntsize; i++) {
-               sfsp = &mntbuf[i];
-               used = sfsp->f_blocks - sfsp->f_bfree;
-               avail = sfsp->f_bavail + used;
-               if (avail == 0)
-                       percentused = 100.0;
-               else
-                       percentused = (double)used / (double)avail * 100.0;
-
-               (void) printf ("%-*.*s %*lld %10lld %11lld %5.0f%%   %s\n",
-                       maxwidth, maxwidth, sfsp->f_mntfromname,
-                       (int)strlen(blockstr),
-                       fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
-                       fsbtoblk(used, sfsp->f_bsize, blocksize),
-                       fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize),
-                       percentused, sfsp->f_mntonname);
-       }
-}
-
 int
 raw_df(char *file, struct statfs *sfsp)
 {
@@ -464,4 +319,175 @@ usage(void)
            "usage: %s [-hiklnP] [-t type] [[file | file_system] ...]\n",
            __progname);
        exit(1);
+}
+
+static char *
+xstrdup(const char *s)
+{
+       char *p;
+
+       p = strdup(s);
+       if (p == NULL)
+               err(1, NULL);
+       return p;
+}
+
+static int
+xasprintf(char **str, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, fmt);
+       ret = vasprintf(str, fmt, ap);
+       va_end(ap);
+       if (ret == -1)
+               err(1, NULL);
+       return ret;
+}
+
+/*
+ * Convert statfs returned filesystem size into BLOCKSIZE units.
+ * Attempts to avoid overflow for large filesystems.
+ */
+#define fsbtoblk(num, fsbs, bs) \
+       (((fsbs) != 0 && (fsbs) < (bs)) ? \
+               (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
+
+/*
+ * "human-readable" output: use 3 digits max.--put unit suffixes at
+ * the end.  Makes output compact and easy-to-read esp. on huge disks.
+ * Code moved into libutil; this is now just a wrapper.
+ */
+static char *
+prtsize(int64_t num, int64_t fsbs, int bs)
+{
+       if (hflag) {
+               char ret[FMT_SCALED_STRSIZE];
+               if (fmt_scaled(num * fsbs, ret) == -1) {
+                       char *tmp;
+                       xasprintf(&tmp, "%lld", num * fsbs);
+                       return tmp;
+               }
+               return xstrdup(ret);
+       } else {
+               char *tmp;
+               xasprintf(&tmp, "%lld", fsbtoblk(num, fsbs, bs));
+               return tmp;
+       }
+}
+
+static void
+fill_table(struct col *cols, struct statfs *mntbuf, int mntsize)
+{
+       int      i;
+       long     blocksize;
+       char    *size_header;
+
+       size_header = df_getbsize(&blocksize);
+       cols[0].c_label = "Filesystem";
+       cols[1].c_label = size_header;
+       cols[2].c_label = "Used";
+       cols[3].c_label = Pflag ? "Available" : "Avail";
+       cols[4].c_label = "Capacity";
+       cols[5].c_label = "iused";
+       cols[6].c_label = "ifree";
+       cols[7].c_label = "%used";
+       cols[8].c_label = "Mounted on";
+
+       for (i = 0; i < COLS; i++) {
+               if ((cols[i].c_vals = calloc(mntsize, sizeof *cols[i].c_vals)) 
== NULL)
+                       err(1, NULL);
+       }
+
+       /* fill in values */
+       for (i = 0; i < mntsize; i++) {
+               u_int64_t used, inodes, iused;
+               int64_t availblks;
+
+               used = mntbuf[i].f_blocks - mntbuf[i].f_bfree;
+               availblks = mntbuf[i].f_bavail + used;
+
+               cols[0].c_vals[i] = mntbuf[i].f_mntfromname;
+               xasprintf(&cols[1].c_vals[i], "%s", prtsize(mntbuf[i].f_blocks, 
mntbuf[i].f_bsize, blocksize));
+               xasprintf(&cols[2].c_vals[i], "%s", prtsize(used, 
mntbuf[i].f_bsize, blocksize));
+               xasprintf(&cols[3].c_vals[i], "%s", prtsize(mntbuf[i].f_bavail, 
mntbuf[i].f_bsize, blocksize));
+               xasprintf(&cols[4].c_vals[i], "%.0f%%", availblks == 0 ? 100.0 
: (double)used / (double)availblks * 100.0);
+
+               inodes = mntbuf[i].f_files;
+               iused = inodes - mntbuf[i].f_ffree;
+               xasprintf(&cols[5].c_vals[i], "%llu", inodes - 
mntbuf[i].f_ffree);
+               xasprintf(&cols[6].c_vals[i], "%llu", mntbuf[i].f_ffree);
+               xasprintf(&cols[7].c_vals[i], "%.0f%%", inodes == 0 ? 100.0 : 
(double)iused / (double)inodes * 100.0);
+               cols[8].c_vals[i] = mntbuf[i].f_mntonname;
+       }
+
+       /* calculate max width */
+       for (i = 0; i < COLS; i++) {
+               int j;
+               cols[i].c_width = strlen(cols[i].c_label);
+               for (j = 0; j < mntsize; j++) {
+                       int tmp = strlen(cols[i].c_vals[j]);
+                       if (tmp > cols[i].c_width)
+                               cols[i].c_width = tmp;
+               }
+               cols[i].c_width += 1;
+       }
+
+}
+
+/*
+ * Determine the user's preferred block size based on the -P, -k and -h
+ * flags, and the output of getbsize(3).
+ */
+static char *
+df_getbsize(long *blocksizep)
+{
+       char *header;
+
+       if (Pflag) {
+               if (kflag) {
+                       *blocksizep = 1024;
+                       header = "1024-blocks";
+               } else {
+                       *blocksizep = 512;
+                       header = "512-blocks";
+               }
+       } else {
+               if (hflag) {
+                       header = "Size";
+               } else {
+                       if (kflag) {
+                               *blocksizep = 1024;
+                               header = "1K-blocks";
+                       } else {
+                               int *unused;
+                               header = getbsize(unused, blocksizep);
+                       }
+               }
+       }
+
+       return header;
+}
+
+static void
+print_table(struct col *cols, int mntsize)
+{
+       int i;
+
+       for (i = -1; i < mntsize; i++) {
+               printf("%*-s", cols[0].c_width, i == -1 ? cols[0].c_label : 
cols[0].c_vals[i]);
+               printf("%*s", cols[1].c_width, i == -1 ? cols[1].c_label : 
cols[1].c_vals[i]);
+               printf("%*s", cols[2].c_width, i == -1 ? cols[2].c_label : 
cols[2].c_vals[i]);
+               printf("%*s", cols[3].c_width, i == -1 ? cols[3].c_label : 
cols[3].c_vals[i]);
+               printf("%*s", cols[4].c_width, i == -1 ? cols[4].c_label : 
cols[4].c_vals[i]);
+               if (iflag) {
+                       printf("%*s", cols[5].c_width, i == -1 ? 
cols[5].c_label : cols[5].c_vals[i]);
+                       printf("%*s", cols[6].c_width, i == -1 ? 
cols[6].c_label : cols[6].c_vals[i]);
+                       printf("%*s", cols[7].c_width, i == -1 ? 
cols[7].c_label : cols[7].c_vals[i]);
+               }
+               printf(" %*-s", cols[8].c_width, i == -1 ? cols[8].c_label : 
cols[8].c_vals[i]);
+               printf("\n");
+       }
+
 }

-- 
Michal Mazurek

Reply via email to