From a9eae2a65816e82d0a3b5f029f480d270eefae59 Mon Sep 17 00:00:00 2001 From: Niklas Poslovski Date: Mon, 5 May 2025 07:37:42 +0200 Subject: [PATCH] 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 Reviewed-by: Adrien Destugues --- src/bin/df.cpp | 160 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 134 insertions(+), 26 deletions(-) diff --git a/src/bin/df.cpp b/src/bin/df.cpp index c0fce748b7..b5efb7908b 100644 --- a/src/bin/df.cpp +++ b/src/bin/df.cpp @@ -19,6 +19,15 @@ #include +enum FormatType +{ + k512Block, + k1024Block, + kHumanReadable, + kUnspecified +}; + + void PrintFlag(uint32 deviceFlags, uint32 testFlag, char yes, char no) { @@ -58,7 +67,7 @@ PrintType(const char *fileSystem) char type[16]; 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 -PrintBlocks(int64 blocks, int64 blockSize, bool showBlocks) +PrintBlocks(int64 blocks, int64 blockSize, FormatType format) { char temp[1024]; - if (showBlocks) + if (format == k1024Block) sprintf(temp, "%" B_PRId64, blocks * (blockSize / 1024)); + else if (format == k512Block) + sprintf(temp, "%" B_PRId64, blocks * (blockSize / 512)); else strcpy(temp, ByteString(blocks, blockSize)); @@ -110,6 +155,12 @@ PrintVerbose(dev_t device) 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); PrintMountPoint(info.dev, true); printf(" Volume Name: \"%s\"\n", info.volume_name); @@ -131,16 +182,20 @@ PrintVerbose(dev_t device) ByteString(info.block_size, 1), info.block_size); printf(" Total Blocks: %10s (%" B_PRIdOFF " blocks)\n", 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", 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(" Used Nodes: %" B_PRIdOFF "\n", info.total_nodes - info.free_nodes); printf(" Free Nodes: %" B_PRIdOFF "\n", info.free_nodes); printf(" Root Inode: %" B_PRIdINO "\n", info.root); } void -PrintCompact(dev_t device, bool showBlocks, bool all) +PrintCompact(dev_t device, FormatType format, bool strictPosix, bool all) { fs_info info; 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) return; - PrintType(info.fsh_name); - PrintBlocks(info.total_blocks, info.block_size, showBlocks); - PrintBlocks(info.free_blocks, info.block_size, showBlocks); + 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(" "); - 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'); + if (strictPosix) { + printf("%24s ", info.device_name); + PrintBlocks(info.total_blocks, info.block_size, format); + PrintBlocks(usedBlocks, info.block_size, format); + PrintBlocks(info.free_blocks, info.block_size, format); + printf(" %s ", UsePercentage(info.total_blocks, info.free_blocks, true)); + PrintMountPoint(info.dev, false); + } 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); - PrintMountPoint(info.dev, false); + printf(" "); + 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"); } @@ -171,10 +244,13 @@ PrintCompact(dev_t device, bool showBlocks, bool all) void ShowUsage(const char *programName) { - printf("usage: %s [--help | --blocks, -b | -all, -a] []\n" + printf("usage: %s [--help | --blocks, -b | -all, -a] []\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" - "If is used, detailed info for that device only will be listed.\n" + " -b, --blocks\tshow device size in blocks of 512 bytes\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 is used, detailed info for that volume only will be listed.\n" "Flags:\n" " Q: has query\n" " A: has attribute\n" @@ -194,7 +270,8 @@ main(int argc, char **argv) if (strrchr(programName, '/')) programName = strrchr(programName, '/') + 1; - bool showBlocks = false; + FormatType format = kUnspecified; + bool strictPosix = false; bool all = false; dev_t device = -1; @@ -207,10 +284,17 @@ main(int argc, char **argv) all = true; break; case 'b': - showBlocks = true; + format = k512Block; break; case 'h': // human readable units in Unix df + format = kHumanReadable; + break; + case 'k': + format = k1024Block; + break; + case 'P': + strictPosix = true; break; default: ShowUsage(programName); @@ -221,7 +305,13 @@ main(int argc, char **argv) if (!strcmp(arg, "all")) all = true; 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 ShowUsage(programName); } @@ -229,6 +319,17 @@ main(int argc, char **argv) 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 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 - printf(" Type Total Free Flags Device Mounted on\n" - "--------- --------- --------- ------- ------------------------ -----------------\n"); + if (strictPosix) { + 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; while ((device = next_dev(&cookie)) >= B_OK) { - PrintCompact(device, showBlocks, all); + PrintCompact(device, format, strictPosix, all); } return 0;