If the number of filesystem inodes or blocks is too big, the columns in the
output of df will become misaligned.
I'm resending the patch that adds variable width columns to df. The display
parts of the code have been rewritten, the information gathering code
remains unchanged.
The code first gathers the relevant information, fills in the 'cols'
array formatting the values in accordance with the settings (including
columns that might not be displayed later (-i)), calculates the widths,
and prints out the resulting 2d array.
* no allocated memory is freed.
* some cleanup in pre-existing code left, like marking functions static
or wrapping long lines.
Does this seem reasonable?
Index: bin/df/df.c
===================================================================
RCS file: /cvs/src/bin/df/df.c,v
retrieving revision 1.55
diff -u -p -r1.55 df.c
--- bin/df/df.c 8 Feb 2016 16:23:54 -0000 1.55
+++ bin/df/df.c 29 Feb 2016 18:35:28 -0000
@@ -42,6 +42,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -49,17 +50,26 @@
extern char *__progname;
+#define COLS 9
+struct col {
+ char *c_label;
+ int c_width;
+ char **c_vals;
+};
+
char *getmntpt(char *);
int selected(const char *);
void maketypelist(char *);
long regetmntinfo(struct statfs **, long);
-void bsdprint(struct statfs *, long, int);
-void prtstat(struct statfs *, int, int, int);
-void posixprint(struct statfs *, long, int);
int bread(int, off_t, void *, int);
void usage(void);
-void prthumanval(long long);
-void prthuman(struct statfs *sfsp, unsigned long long);
+
+static char *xstrdup(const char *);
+static int xasprintf(char **, const char *, ...);
+static char *prtsize(int64_t, int64_t, int);
+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 *);
@@ -77,6 +87,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");
@@ -160,22 +171,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);
}
@@ -276,151 +273,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)
{
@@ -463,4 +315,181 @@ 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)))
+
+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;
+
+ cols[0].c_label = "Filesystem";
+ cols[1].c_label = df_getbsize(&blocksize);
+ 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";
+
+ /* the list of filesystems can be empty when running e.g. df -l /nfs */
+ if (mntsize == 0)
+ goto widths;
+
+ 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;
+ }
+
+widths:
+ /* calculate max widths */
+ 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 result 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 ignored;
+ header = getbsize(&ignored, blocksizep);
+ }
+ }
+ }
+
+ return header;
+}
+
+#define VAL(idx, col) \
+ ((idx) == -1 ? cols[(col)].c_label : cols[(col)].c_vals[(idx)])
+static void
+print_table(struct col *cols, int mntsize)
+{
+ int i;
+
+ for (i = -1; i < mntsize; i++) {
+ printf("%*-s", cols[0].c_width, VAL(i, 0));
+ printf("%*s", cols[1].c_width, VAL(i, 1));
+ printf("%*s", cols[2].c_width, VAL(i, 2));
+ printf("%*s", cols[3].c_width, VAL(i, 3));
+ printf("%*s", cols[4].c_width, VAL(i, 4));
+ if (iflag) {
+ printf("%*s", cols[5].c_width, VAL(i, 5));
+ printf("%*s", cols[6].c_width, VAL(i, 6));
+ printf("%*s", cols[7].c_width, VAL(i, 7));
+ }
+ printf(" %*-s", cols[8].c_width, VAL(i, 8));
+ printf("\n");
+ }
}
--
Michal Mazurek