df: Rework formatting, implement POSIX required flags

POSIX flags:
- -P for a fixed format usable in scripts with only some fields
- -k for kilobytes (the default for -P is 512 byte blocks). Rename -b to -k accordingly
  and implement a new -b to force 512 byte blocks

Default format rework:
- Add usage percentage
- To keep things under 80 columns, use two lines per device: the first
  shows the mountpoint and the second shows the details.
- Mount point is shown in bold and other fields are indented

Change-Id: I33843caf67ff01509ce38bf0b5fdd18517ea1aa9
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9276
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
Niklas Poslovski
2026-01-09 20:40:50 +00:00
committed by waddlesplash
parent b34fdb5655
commit a9eae2a658
+134 -26
View File
@@ -19,6 +19,15 @@
#include <errno.h> #include <errno.h>
enum FormatType
{
k512Block,
k1024Block,
kHumanReadable,
kUnspecified
};
void void
PrintFlag(uint32 deviceFlags, uint32 testFlag, char yes, char no) PrintFlag(uint32 deviceFlags, uint32 testFlag, char yes, char no)
{ {
@@ -58,7 +67,7 @@ PrintType(const char *fileSystem)
char type[16]; char type[16];
strlcpy(type, fileSystem, sizeof(type)); strlcpy(type, fileSystem, sizeof(type));
printf("%-9s", type); printf("%11s ", type);
} }
@@ -87,13 +96,49 @@ ByteString(int64 numBlocks, int64 blockSize)
} }
const char *
UsePercentage(int64 totalBlocks, int64 freeBlocks, bool compact = false)
{
float usage;
static char string[8];
if (totalBlocks == 0 && freeBlocks == 0)
usage = 100;
else if (totalBlocks == 0)
usage = 0;
else
usage = 100 - 100 * (1. * freeBlocks / totalBlocks);
if (compact) {
if (usage < 10)
sprintf(string, " %.0f%%", round(usage));
else if (usage < 100)
sprintf(string, " %.0f%%", round(usage));
else
sprintf(string, "%.0f%%", round(usage));
}
else {
if (usage < 10)
sprintf(string, " %.1f %%", usage);
else if (usage < 100)
sprintf(string, " %.1f %%", usage);
else
sprintf(string, "%.1f %%", usage);
}
return string;
}
void void
PrintBlocks(int64 blocks, int64 blockSize, bool showBlocks) PrintBlocks(int64 blocks, int64 blockSize, FormatType format)
{ {
char temp[1024]; char temp[1024];
if (showBlocks) if (format == k1024Block)
sprintf(temp, "%" B_PRId64, blocks * (blockSize / 1024)); sprintf(temp, "%" B_PRId64, blocks * (blockSize / 1024));
else if (format == k512Block)
sprintf(temp, "%" B_PRId64, blocks * (blockSize / 512));
else else
strcpy(temp, ByteString(blocks, blockSize)); strcpy(temp, ByteString(blocks, blockSize));
@@ -110,6 +155,12 @@ PrintVerbose(dev_t device)
return; return;
} }
int64 usedBlocks = info.total_blocks - info.free_blocks;
// Used blocks can't be negative, but ramfs reports available RAM as free_blocks,
// and always 0 for total_blocks, so we need to check for that.
if (usedBlocks < 0)
usedBlocks = 0;
printf(" Device No.: %" B_PRIdDEV "\n", info.dev); printf(" Device No.: %" B_PRIdDEV "\n", info.dev);
PrintMountPoint(info.dev, true); PrintMountPoint(info.dev, true);
printf(" Volume Name: \"%s\"\n", info.volume_name); printf(" Volume Name: \"%s\"\n", info.volume_name);
@@ -131,16 +182,20 @@ PrintVerbose(dev_t device)
ByteString(info.block_size, 1), info.block_size); ByteString(info.block_size, 1), info.block_size);
printf(" Total Blocks: %10s (%" B_PRIdOFF " blocks)\n", printf(" Total Blocks: %10s (%" B_PRIdOFF " blocks)\n",
ByteString(info.total_blocks, info.block_size), info.total_blocks); ByteString(info.total_blocks, info.block_size), info.total_blocks);
printf(" Used Blocks: %10s (%" B_PRIdOFF " blocks)\n",
ByteString(usedBlocks, info.block_size), usedBlocks);
printf(" Free Blocks: %10s (%" B_PRIdOFF " blocks)\n", printf(" Free Blocks: %10s (%" B_PRIdOFF " blocks)\n",
ByteString(info.free_blocks, info.block_size), info.free_blocks); ByteString(info.free_blocks, info.block_size), info.free_blocks);
printf(" Usage %%: %s\n", UsePercentage(info.total_blocks, info.free_blocks));
printf(" Total Nodes: %" B_PRIdOFF "\n", info.total_nodes); printf(" Total Nodes: %" B_PRIdOFF "\n", info.total_nodes);
printf(" Used Nodes: %" B_PRIdOFF "\n", info.total_nodes - info.free_nodes);
printf(" Free Nodes: %" B_PRIdOFF "\n", info.free_nodes); printf(" Free Nodes: %" B_PRIdOFF "\n", info.free_nodes);
printf(" Root Inode: %" B_PRIdINO "\n", info.root); printf(" Root Inode: %" B_PRIdINO "\n", info.root);
} }
void void
PrintCompact(dev_t device, bool showBlocks, bool all) PrintCompact(dev_t device, FormatType format, bool strictPosix, bool all)
{ {
fs_info info; fs_info info;
if (fs_stat_dev(device, &info) != B_OK) if (fs_stat_dev(device, &info) != B_OK)
@@ -149,21 +204,39 @@ PrintCompact(dev_t device, bool showBlocks, bool all)
if (!all && (info.flags & B_FS_IS_PERSISTENT) == 0) if (!all && (info.flags & B_FS_IS_PERSISTENT) == 0)
return; return;
PrintType(info.fsh_name); int64 usedBlocks = info.total_blocks - info.free_blocks;
PrintBlocks(info.total_blocks, info.block_size, showBlocks); // Used blocks can't be negative, but ramfs reports available RAM as free_blocks,
PrintBlocks(info.free_blocks, info.block_size, showBlocks); // and always 0 for total_blocks, so we need to check for that.
if (usedBlocks < 0)
usedBlocks = 0;
printf(" "); if (strictPosix) {
PrintFlag(info.flags, B_FS_HAS_QUERY, 'Q', '-'); printf("%24s ", info.device_name);
PrintFlag(info.flags, B_FS_HAS_ATTR, 'A', '-'); PrintBlocks(info.total_blocks, info.block_size, format);
PrintFlag(info.flags, B_FS_HAS_MIME, 'M', '-'); PrintBlocks(usedBlocks, info.block_size, format);
PrintFlag(info.flags, B_FS_IS_SHARED, 'S', '-'); PrintBlocks(info.free_blocks, info.block_size, format);
PrintFlag(info.flags, B_FS_IS_PERSISTENT, 'P', '-'); printf(" %s ", UsePercentage(info.total_blocks, info.free_blocks, true));
PrintFlag(info.flags, B_FS_IS_REMOVABLE, 'R', '-'); PrintMountPoint(info.dev, false);
PrintFlag(info.flags, B_FS_IS_READONLY, '-', 'W'); } else {
printf("\x1B[1m");
PrintMountPoint(info.dev, false);
printf("\x1B[0m\n");
PrintType(info.fsh_name);
PrintBlocks(info.total_blocks, info.block_size, format);
PrintBlocks(usedBlocks, info.block_size, format);
printf(" %24s ", info.device_name); printf(" ");
PrintMountPoint(info.dev, false); PrintFlag(info.flags, B_FS_HAS_QUERY, 'Q', '-');
PrintFlag(info.flags, B_FS_HAS_ATTR, 'A', '-');
PrintFlag(info.flags, B_FS_HAS_MIME, 'M', '-');
PrintFlag(info.flags, B_FS_IS_SHARED, 'S', '-');
PrintFlag(info.flags, B_FS_IS_PERSISTENT, 'P', '-');
PrintFlag(info.flags, B_FS_IS_REMOVABLE, 'R', '-');
PrintFlag(info.flags, B_FS_IS_READONLY, '-', 'W');
printf(" %6s ", UsePercentage(info.total_blocks, info.free_blocks, true));
printf("%-24s", info.device_name);
}
printf("\n"); printf("\n");
} }
@@ -171,10 +244,13 @@ PrintCompact(dev_t device, bool showBlocks, bool all)
void void
ShowUsage(const char *programName) ShowUsage(const char *programName)
{ {
printf("usage: %s [--help | --blocks, -b | -all, -a] [<path-to-device>]\n" printf("usage: %s [--help | --blocks, -b | -all, -a] [<path-to-volume>]\n"
" -a, --all\tinclude all file systems, also those not visible from Tracker\n" " -a, --all\tinclude all file systems, also those not visible from Tracker\n"
" -b, --blocks\tshow device size in blocks of 1024 bytes\n" " -b, --blocks\tshow device size in blocks of 512 bytes\n"
"If <path-to-device> is used, detailed info for that device only will be listed.\n" " -h, --human\tshow device size in blocks of 1024 bytes\n"
" -k, --kilobytes\tshow device size in blocks of 1024 bytes\n"
" -P, --posix\tproduce output in the format specified by POSIX\n"
"If <path-to-volume> is used, detailed info for that volume only will be listed.\n"
"Flags:\n" "Flags:\n"
" Q: has query\n" " Q: has query\n"
" A: has attribute\n" " A: has attribute\n"
@@ -194,7 +270,8 @@ main(int argc, char **argv)
if (strrchr(programName, '/')) if (strrchr(programName, '/'))
programName = strrchr(programName, '/') + 1; programName = strrchr(programName, '/') + 1;
bool showBlocks = false; FormatType format = kUnspecified;
bool strictPosix = false;
bool all = false; bool all = false;
dev_t device = -1; dev_t device = -1;
@@ -207,10 +284,17 @@ main(int argc, char **argv)
all = true; all = true;
break; break;
case 'b': case 'b':
showBlocks = true; format = k512Block;
break; break;
case 'h': case 'h':
// human readable units in Unix df // human readable units in Unix df
format = kHumanReadable;
break;
case 'k':
format = k1024Block;
break;
case 'P':
strictPosix = true;
break; break;
default: default:
ShowUsage(programName); ShowUsage(programName);
@@ -221,7 +305,13 @@ main(int argc, char **argv)
if (!strcmp(arg, "all")) if (!strcmp(arg, "all"))
all = true; all = true;
else if (!strcmp(arg, "blocks")) else if (!strcmp(arg, "blocks"))
showBlocks = true; format = k512Block;
else if (!strcmp(arg, "human"))
format = kHumanReadable;
else if (!strcmp(arg, "kilobytes"))
format = k1024Block;
else if (!strcmp(arg, "posix"))
strictPosix = true;
else else
ShowUsage(programName); ShowUsage(programName);
} }
@@ -229,6 +319,17 @@ main(int argc, char **argv)
break; break;
} }
// Determine default format if nothing was specified
// This is done after option parsing so that -k -P gives kilobytes as expected
// Otherwise -P could force 512-block format, but it would overwrite the effect of other
// options.
if (format == kUnspecified) {
if (strictPosix)
format = k512Block;
else
format = kHumanReadable;
}
// Do we already have a device? Then let's print out detailed info about that // Do we already have a device? Then let's print out detailed info about that
if (argv[0] != NULL) { if (argv[0] != NULL) {
@@ -238,12 +339,19 @@ main(int argc, char **argv)
// If not, then just iterate over all devices and give a compact summary // If not, then just iterate over all devices and give a compact summary
printf(" Type Total Free Flags Device Mounted on\n" if (strictPosix) {
"--------- --------- --------- ------- ------------------------ -----------------\n"); printf(" Filesystem %11s Used Available Capacity Mounted on\n",
format == kHumanReadable ? "Capacity"
: (format == k512Block ? "512-blocks" : "1024-blocks"));
} else {
printf("\x1B[1mVolume\x1B[0m\n"
" Type Total Free Flags Used%% Device\n"
"----------- ---------- --------- ------- ------ -------------------\n");
}
int32 cookie = 0; int32 cookie = 0;
while ((device = next_dev(&cookie)) >= B_OK) { while ((device = next_dev(&cookie)) >= B_OK) {
PrintCompact(device, showBlocks, all); PrintCompact(device, format, strictPosix, all);
} }
return 0; return 0;