commit: 163d7e89ac8742b919b859d7c7a6f3fd58ebfd60
parent 388be371d91938897924756e705fbaaf959b6361
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Mon,  1 Apr 2024 21:14:33 +0200
cmd/df: Only pad with spaces when stdout is a TTY
This way cut(1) becomes usable for extracting df(1) output.
Diffstat:
3 files changed, 38 insertions(+), 33 deletions(-)
diff --git a/cmd/df.1 b/cmd/df.1
@@ -26,7 +26,9 @@ filters to the first filesystem containing each
 .Pp
 Unlike some implementations the table formatting isn't perfect, you might want to use
 .Xr column 1
-to get more readable outputs.
+to get more readable output.
+Also padding is disabled when output isn't a TTY so you can more easily use tools like
+.Xr cut 1 .
 .Sh OPTIONS
 .Bl -tag -width Ds
 .It Fl a
diff --git a/cmd/df.c b/cmd/df.c
@@ -103,6 +103,11 @@ main(int argc, char *argv[])
 
 	int args_left = argc != 0 ? argc : 1;
 
+	bool tty_out  = isatty(1);
+	int col_width = tty_out ? 10 : 0;
+
+	if(!tty_out) fs_col_width = 0;
+
 	dev_t arg_devs[argc];
 	for(int i = 0; i < argc; i++)
 	{
@@ -120,14 +125,14 @@ main(int argc, char *argv[])
 
 	printf("%-*s ", fs_col_width, "Filesystem");
 
-	if(opt_T) printf("%10s ", "Type");
+	if(opt_T) printf("%*s ", col_width, "Type");
 
 	if(forced_bsize != 0)
-		printf("%3zd-blocks ", forced_bsize);
+		printf("%zd-blocks ", forced_bsize);
 	else
-		printf("%10s ", "Total");
+		printf("%*s ", col_width, "Total");
 
-	printf("%10s %10s ", "Used", "Available");
+	printf("%*s %*s ", col_width, "Used", col_width, "Available");
 
 	if(opt_P)
 		printf("Capacity Mounted on\n");
@@ -251,9 +256,13 @@ main(int argc, char *argv[])
 			{
 				printf("%-*s ", fs_col_width, mntent->mnt_fsname);
 
-				if(opt_T) printf("%10s ", mntent->mnt_type);
+				if(opt_T) printf("%*s ", col_width, mntent->mnt_type);
 
-				printf("%10s %10s %10s %4s %s\n", "-", "-", "-", "-", mntent->mnt_dir);
+				printf("%*s ", col_width, "-");       // total
+				printf("%*s ", col_width, "-");       // used
+				printf("%*s ", col_width, "-");       // free
+				printf("%*s ", tty_out ? 3 : 0, "-"); // percent
+				printf("%s\n", mntent->mnt_dir);
 			}
 
 			continue;
@@ -286,7 +295,7 @@ main(int argc, char *argv[])
 
 		printf("%-*s ", fs_col_width, mntent->mnt_fsname);
 
-		if(opt_T) printf("%10s ", mntent->mnt_type);
+		if(opt_T) printf("%*s ", col_width, mntent->mnt_type);
 
 		if(opt_h && !opt_P)
 		{
@@ -294,28 +303,22 @@ main(int argc, char *argv[])
 			struct si_scale used_scl  = dtosi(used, true);
 			struct si_scale free_scl  = dtosi(free, true);
 
-			// clang-format off
-			printf("%7.2f%-3s %7.2f%-3s %7.2f%-3s %3zd%% %s\n",
-			       total_scl.number, total_scl.prefix,
-			       used_scl.number, used_scl.prefix,
-			       free_scl.number, free_scl.prefix,
-			       percent,
-			       mntent->mnt_dir
-			);
-			// clang-format on
+			int width_num = tty_out ? col_width - 3 : 0;
+			int width_pre = tty_out ? 3 : 0;
+
+			printf("%*.2f%-*s ", width_num, total_scl.number, width_pre, total_scl.prefix);
+			printf("%*.2f%-*s ", width_num, used_scl.number, width_pre, used_scl.prefix);
+			printf("%*.2f%-*s ", width_num, free_scl.number, width_pre, free_scl.prefix);
 		}
 		else
 		{
-			// clang-format off
-			printf("%10zd %10zd %10zd %3zd%% %s\n",
-			       total,
-			       used,
-			       free,
-			       percent,
-			       mntent->mnt_dir
-			);
-			// clang-format on
+			printf("%*zd ", col_width, total);
+			printf("%*zd ", col_width, used);
+			printf("%*zd ", col_width, free);
 		}
+
+		printf("%*zd%% ", tty_out ? 3 : 0, percent);
+		printf("%s\n", mntent->mnt_dir);
 	}
 
 	endmntent(mounted);
diff --git a/test-cmd/df b/test-cmd/df
@@ -7,19 +7,19 @@ posix_body() {
 	# stderr ignored because not all filesystems allow statvfs from a normal user.
 	# For example tracefs aka "/sys/kernel/debug/tracing" on Linux.
 
-	atf_check -e ignore -o 'match:Filesystem *512-blocks *Used *Available *Capacity *Mounted on' sh -c '../cmd/df -P | head -1'
-	atf_check -e ignore -o 'match:Filesystem *1024-blocks *Used *Available *Capacity *Mounted on' sh -c '../cmd/df -Pk | head -1'
+	atf_check -e ignore -o 'match:Filesystem 512-blocks Used Available Capacity Mounted on' sh -c '../cmd/df -P | head -1'
+	atf_check -e ignore -o 'match:Filesystem 1024-blocks Used Available Capacity Mounted on' sh -c '../cmd/df -Pk | head -1'
 
-	atf_check -e ignore -o 'match:^[^ ]* *[0-9]* *[0-9]* *[0-9]* *[0-9]*% [^ ]$' sh -c '../cmd/df -P | sed -n 2,\$p'
-	atf_check -e ignore -o 'match:^[^ ]* *[0-9]* *[0-9]* *[0-9]* *[0-9]*% [^ ]$' sh -c '../cmd/df -Pk | sed -n 2,\$p'
+	atf_check -e ignore -o 'match:^[^ ]* [0-9]* [0-9]* [0-9]* [0-9]*% [^ ]$' sh -c '../cmd/df -P | sed -n 2,\$p'
+	atf_check -e ignore -o 'match:^[^ ]* [0-9]* [0-9]* [0-9]* [0-9]*% [^ ]$' sh -c '../cmd/df -Pk | sed -n 2,\$p'
 
-	atf_check -e ignore -o 'match:^[^ ]* *[0-9]* *[0-9]* *[0-9]* *[0-9]*% [^ ]$' sh -c '../cmd/df -Pa | sed -n 2,\$p'
-	atf_check -e ignore -o 'match:^[^ ]* *[0-9]* *[0-9]* *[0-9]* *[0-9]*% [^ ]$' sh -c '../cmd/df -Pak | sed -n 2,\$p'
+	atf_check -e ignore -o 'match:^[^ ]* [0-9]* [0-9]* [0-9]* [0-9]*% [^ ]$' sh -c '../cmd/df -Pa | sed -n 2,\$p'
+	atf_check -e ignore -o 'match:^[^ ]* [0-9]* [0-9]* [0-9]* [0-9]*% [^ ]$' sh -c '../cmd/df -Pak | sed -n 2,\$p'
 }
 
 atf_test_case args
 args_body() {
-	atf_check -e ignore -o 'match:^[^ ]* *[0-9]* *[0-9]* *[0-9]* *[0-9]*% /$' sh -c '../cmd/df -P / | sed -n 2,\$p'
+	atf_check -e ignore -o 'match:^[^ ]* [0-9]* [0-9]* [0-9]* [0-9]*% /$' sh -c '../cmd/df -P / | sed -n 2,\$p'
 }
 
 atf_init_test_cases() {