#include <Volume.h>
#include <Directory.h>
#include <Path.h>
#include <fs_info.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
enum FormatType
{
k512Block,
k1024Block,
kHumanReadable,
kUnspecified
};
void
PrintFlag(uint32 deviceFlags, uint32 testFlag, char yes, char no)
{
printf("%c", deviceFlags & testFlag ? yes : no);
}
void
PrintMountPoint(dev_t device, bool verbose)
{
char mount[B_PATH_NAME_LENGTH];
mount[0] = '\0';
BVolume volume(device);
BDirectory root;
if (volume.GetRootDirectory(&root) == B_OK) {
BPath path(&root, NULL);
if (path.InitCheck() == B_OK)
strlcpy(mount, path.Path(), sizeof(mount));
else
strlcpy(mount, "?", sizeof(mount));
}
if (verbose)
printf(" Mounted at: %s\n", mount);
else {
printf("%-17s ", mount);
if (strlen(mount) > 17)
printf("\n%17s ", "");
}
}
void
PrintType(const char *fileSystem)
{
char type[16];
strlcpy(type, fileSystem, sizeof(type));
printf("%11s ", type);
}
const char *
ByteString(int64 numBlocks, int64 blockSize)
{
double blocks = 1. * numBlocks * blockSize;
static char string[64];
if (blocks < 1024)
sprintf(string, "%" B_PRId64, numBlocks * blockSize);
else {
const char *units[] = {"KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
"ZiB", "YiB", NULL};
int32 i = -1;
do {
blocks /= 1024.0;
i++;
} while (blocks >= 1024 && units[i + 1]);
sprintf(string, "%.1f %s", blocks, units[i]);
}
return string;
}
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, FormatType format)
{
char temp[1024];
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));
printf("%10s", temp);
}
void
PrintVerbose(dev_t device)
{
fs_info info;
if (fs_stat_dev(device, &info) != B_OK) {
fprintf(stderr, "Could not stat fs: %s\n", strerror(errno));
return;
}
int64 usedBlocks = info.total_blocks - info.free_blocks;
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);
printf(" File System: %s\n", info.fsh_name);
printf(" Device: %s\n", info.device_name);
printf(" Flags: ");
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("\n I/O Size: %10s (%" B_PRIdOFF " byte)\n",
ByteString(info.io_size, 1), info.io_size);
printf(" Block Size: %10s (%" B_PRIdOFF " byte)\n",
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, FormatType format, bool strictPosix, bool all)
{
fs_info info;
if (fs_stat_dev(device, &info) != B_OK)
return;
if (!all && (info.flags & B_FS_IS_PERSISTENT) == 0)
return;
int64 usedBlocks = info.total_blocks - info.free_blocks;
if (usedBlocks < 0)
usedBlocks = 0;
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(info.free_blocks, info.block_size, format);
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");
}
void
ShowUsage(const char *programName)
{
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"
" -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 <path-to-volume> is used, detailed info for that volume only will be listed.\n"
"Flags:\n"
" Q: has query\n"
" A: has attribute\n"
" M: has mime\n"
" S: is shared\n"
" P: is persistent (visible in Tracker)\n"
" R: is removable\n"
" W: is writable\n", programName);
exit(0);
}
int
main(int argc, char **argv)
{
char *programName = argv[0];
if (strrchr(programName, '/'))
programName = strrchr(programName, '/') + 1;
FormatType format = kUnspecified;
bool strictPosix = false;
bool all = false;
dev_t device = -1;
while (*++argv) {
char *arg = *argv;
if (*arg == '-') {
while (*++arg && isalpha(*arg)) {
switch (arg[0]) {
case 'a':
all = true;
break;
case 'b':
format = k512Block;
break;
case 'h':
format = kHumanReadable;
break;
case 'k':
format = k1024Block;
break;
case 'P':
strictPosix = true;
break;
default:
ShowUsage(programName);
}
}
if (arg[0] == '-') {
arg++;
if (!strcmp(arg, "all"))
all = true;
else if (!strcmp(arg, "blocks"))
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);
}
} else
break;
}
if (format == kUnspecified) {
if (strictPosix)
format = k512Block;
else
format = kHumanReadable;
}
if (argv[0] != NULL) {
PrintVerbose(dev_for_path(argv[0]));
return 0;
}
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, format, strictPosix, all);
}
return 0;
}