boredos_mirror/src/userland/cli/df.c
2026-05-12 18:06:35 +02:00

317 lines
13 KiB
C

// Copyright (c) 2023-2026 Chris (boreddevnl)
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
// This header needs to maintain in any file it is present in, as per the GPL license terms.
// BOREDOS_APP_DESC: Display free disk space
#include <syscall.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#define MULTIPLIER_DEFAULT 0
#define MULTIPLIER_B 1
#define MULTIPLIER_G 2
#define MULTIPLIER_H_1000 3
#define MULTIPLIER_H_1024 4
#define MULTIPLIER_K 5
#define MULTIPLIER_M 6
#define MULTIPLIER_P 7
static int multiplier_mode = MULTIPLIER_K;
static bool opt_all = false;
static bool opt_total = false;
static bool opt_inodes = false;
static bool opt_cached = false;
static bool opt_export = false;
static bool opt_local = false;
static bool opt_type = false;
static bool opt_libxo = false;
static bool opt_comma = false;
static void print_usage(void) {
printf("Usage: df [OPTIONS]\n");
printf(" -a, --all Include dummy file systems\n");
printf(" -B, --block-size=SIZE Use SIZE-byte blocks\n");
printf(" -b Use 512-byte blocks\n");
printf(" -g Use 1-Gigabyte blocks\n");
printf(" -h, --human-readable Print sizes in powers of 1024 (e.g., 1023M)\n");
printf(" -H, --si Print sizes in powers of 1000 (e.g., 1.1G)\n");
printf(" -i, --inodes List inode information instead of block usage\n");
printf(" -k Like --block-size=1K\n");
printf(" -l, --local Limit listing to local file systems\n");
printf(" -m Like --block-size=1M\n");
printf(" -P, --portability Use the POSIX output format\n");
printf(" -T, --print-type Print file system type\n");
printf(" -c, --total Produce a grand total\n");
printf(" -n Use previously obtained statistics (no-op)\n");
printf(" -Y Export-friendly format\n");
printf(" --libxo Structured output (JSON-like)\n");
printf(" , Use comma separator for numbers\n");
}
static void format_number(uint64_t num, char *out, bool use_comma) {
if (!use_comma) {
sprintf(out, "%llu", (unsigned long long)num);
return;
}
char temp[64];
sprintf(temp, "%llu", (unsigned long long)num);
int len = strlen(temp);
int out_idx = 0;
for (int i = 0; i < len; i++) {
out[out_idx++] = temp[i];
if ((len - i - 1) > 0 && (len - i - 1) % 3 == 0) {
out[out_idx++] = ',';
}
}
out[out_idx] = '\0';
}
static void format_human_readable(uint64_t bytes, char *out, bool pow1000, bool use_comma) {
const char *suffixes1024[] = {"", "K", "M", "G", "T", "P"};
const char *suffixes1000[] = {"", "k", "M", "G", "T", "P"};
uint64_t base = pow1000 ? 1000 : 1024;
int s = 0;
double d = (double)bytes;
while (d >= base && s < 5) {
d /= base;
s++;
}
char temp[64];
if (s == 0) {
format_number(bytes, out, use_comma);
} else {
if (d >= 10.0) {
sprintf(temp, "%.0f%s", d, pow1000 ? suffixes1000[s] : suffixes1024[s]);
} else {
sprintf(temp, "%.1f%s", d, pow1000 ? suffixes1000[s] : suffixes1024[s]);
}
strcpy(out, temp);
}
}
static void format_size(uint64_t bytes, char *out) {
uint64_t blocks = 0;
switch (multiplier_mode) {
case MULTIPLIER_B:
case MULTIPLIER_P:
blocks = (bytes + 511) / 512;
format_number(blocks, out, opt_comma);
break;
case MULTIPLIER_G:
blocks = (bytes + (1024ULL * 1024 * 1024 - 1)) / (1024ULL * 1024 * 1024);
format_number(blocks, out, opt_comma);
break;
case MULTIPLIER_H_1000:
format_human_readable(bytes, out, true, opt_comma);
break;
case MULTIPLIER_H_1024:
format_human_readable(bytes, out, false, opt_comma);
break;
case MULTIPLIER_K:
default:
blocks = (bytes + 1023) / 1024;
format_number(blocks, out, opt_comma);
break;
case MULTIPLIER_M:
blocks = (bytes + (1024 * 1024 - 1)) / (1024 * 1024);
format_number(blocks, out, opt_comma);
break;
}
}
int main(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--all") == 0) opt_all = true;
else if (strcmp(argv[i], "-b") == 0) multiplier_mode = MULTIPLIER_B;
else if (strcmp(argv[i], "-g") == 0) multiplier_mode = MULTIPLIER_G;
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--human-readable") == 0) multiplier_mode = MULTIPLIER_H_1024;
else if (strcmp(argv[i], "-H") == 0 || strcmp(argv[i], "--si") == 0) multiplier_mode = MULTIPLIER_H_1000;
else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--inodes") == 0) opt_inodes = true;
else if (strcmp(argv[i], "-k") == 0) multiplier_mode = MULTIPLIER_K;
else if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--local") == 0) opt_local = true;
else if (strcmp(argv[i], "-m") == 0) multiplier_mode = MULTIPLIER_M;
else if (strcmp(argv[i], "-P") == 0 || strcmp(argv[i], "--portability") == 0) multiplier_mode = MULTIPLIER_P;
else if (strcmp(argv[i], "-T") == 0 || strcmp(argv[i], "--print-type") == 0) opt_type = true;
else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--total") == 0) opt_total = true;
else if (strcmp(argv[i], "-n") == 0) opt_cached = true;
else if (strcmp(argv[i], "-Y") == 0) opt_export = true;
else if (strcmp(argv[i], "--libxo") == 0) opt_libxo = true;
else if (strcmp(argv[i], ",") == 0) opt_comma = true;
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
print_usage();
return 0;
} else if (argv[i][0] == '-') {
// Check clustered flags (e.g., -Th)
for (size_t j = 1; j < strlen(argv[i]); j++) {
char c = argv[i][j];
if (c == 'a') opt_all = true;
else if (c == 'b') multiplier_mode = MULTIPLIER_B;
else if (c == 'g') multiplier_mode = MULTIPLIER_G;
else if (c == 'h') multiplier_mode = MULTIPLIER_H_1024;
else if (c == 'H') multiplier_mode = MULTIPLIER_H_1000;
else if (c == 'i') opt_inodes = true;
else if (c == 'k') multiplier_mode = MULTIPLIER_K;
else if (c == 'l') opt_local = true;
else if (c == 'm') multiplier_mode = MULTIPLIER_M;
else if (c == 'P') multiplier_mode = MULTIPLIER_P;
else if (c == 'T') opt_type = true;
else if (c == 'c') opt_total = true;
else if (c == 'n') opt_cached = true;
else if (c == 'Y') opt_export = true;
else {
printf("df: invalid option -- '%c'\n", c);
print_usage();
return 1;
}
}
}
}
int mount_count = sys_fs_mount_count();
if (mount_count < 0) {
printf("df: cannot get mount count\n");
return 1;
}
if (opt_libxo) {
printf("{\n \"storage-system-information\": {\n \"filesystem\": [\n");
} else {
if (opt_type) {
if (multiplier_mode == MULTIPLIER_H_1024 || multiplier_mode == MULTIPLIER_H_1000) {
printf("%-16s %-8s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Type", "Size", "Used", "Avail", "Use%", "Mounted on");
} else if (opt_inodes) {
printf("%-16s %-8s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Type", "Inodes", "IUsed", "IFree", "IUse%", "Mounted on");
} else {
const char *block_str = "1K-blocks";
if (multiplier_mode == MULTIPLIER_B || multiplier_mode == MULTIPLIER_P) block_str = "512-blocks";
else if (multiplier_mode == MULTIPLIER_G) block_str = "1G-blocks";
else if (multiplier_mode == MULTIPLIER_M) block_str = "1M-blocks";
printf("%-16s %-8s %-10s %-10s %-10s %-5s %s\n", "Filesystem", "Type", block_str, "Used", "Available", "Use%", "Mounted on");
}
} else {
if (multiplier_mode == MULTIPLIER_H_1024 || multiplier_mode == MULTIPLIER_H_1000) {
printf("%-16s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Size", "Used", "Avail", "Use%", "Mounted on");
} else if (opt_inodes) {
printf("%-16s %-9s %-9s %-9s %-5s %s\n", "Filesystem", "Inodes", "IUsed", "IFree", "IUse%", "Mounted on");
} else {
const char *block_str = "1K-blocks";
if (multiplier_mode == MULTIPLIER_B || multiplier_mode == MULTIPLIER_P) block_str = "512-blocks";
else if (multiplier_mode == MULTIPLIER_G) block_str = "1G-blocks";
else if (multiplier_mode == MULTIPLIER_M) block_str = "1M-blocks";
if (multiplier_mode == MULTIPLIER_P) {
printf("%-16s %-10s %-10s %-10s %-5s %s\n", "Filesystem", "512-blocks", "Used", "Available", "Capacity", "Mounted on");
} else {
printf("%-16s %-10s %-10s %-10s %-5s %s\n", "Filesystem", block_str, "Used", "Available", "Use%", "Mounted on");
}
}
}
}
uint64_t grand_total_bytes = 0;
uint64_t grand_used_bytes = 0;
uint64_t grand_avail_bytes = 0;
bool first_libxo = true;
for (int i = 0; i < mount_count; i++) {
mount_info_t m_info;
if (sys_fs_mount_info(i, &m_info) != 0) continue;
bool is_pseudo = (strcmp(m_info.fs_type, "ramfs") == 0 && strcmp(m_info.path, "/") != 0) ||
strcmp(m_info.fs_type, "procfs") == 0 ||
strcmp(m_info.fs_type, "sysfs") == 0;
if (is_pseudo && !opt_all) continue;
vfs_statfs_t stat;
if (sys_fs_statfs(m_info.path, &stat) != 0) continue;
uint64_t total_bytes = stat.total_blocks * stat.block_size;
uint64_t free_bytes = stat.free_blocks * stat.block_size;
uint64_t used_bytes = total_bytes - free_bytes;
if (strcmp(m_info.path, "/") == 0) {
if (total_bytes == 0) {
total_bytes = 32 * 1024 * 1024;
used_bytes = 1024 * 1024;
free_bytes = total_bytes - used_bytes;
}
}
grand_total_bytes += total_bytes;
grand_used_bytes += used_bytes;
grand_avail_bytes += free_bytes;
double use_percent = 0;
if (total_bytes > 0) use_percent = ((double)used_bytes / (double)total_bytes) * 100.0;
char use_str[16];
if (is_pseudo && total_bytes == 0) strcpy(use_str, "-");
else sprintf(use_str, "%.0f%%", use_percent);
if (opt_libxo) {
if (!first_libxo) printf(",\n");
first_libxo = false;
printf(" {\n");
printf(" \"name\": \"%s\",\n", m_info.device);
if (opt_type) printf(" \"type\": \"%s\",\n", m_info.fs_type);
printf(" \"total-blocks\": %llu,\n", (unsigned long long)total_bytes);
printf(" \"used-blocks\": %llu,\n", (unsigned long long)used_bytes);
printf(" \"available-blocks\": %llu,\n", (unsigned long long)free_bytes);
printf(" \"used-percent\": %.0f,\n", use_percent);
printf(" \"mounted-on\": \"%s\"\n", m_info.path);
printf(" }");
} else {
char t_str[32], u_str[32], f_str[32];
if (opt_inodes) {
strcpy(t_str, "0");
strcpy(u_str, "0");
strcpy(f_str, "0");
strcpy(use_str, "-");
} else {
format_size(total_bytes, t_str);
format_size(used_bytes, u_str);
format_size(free_bytes, f_str);
}
char dev_name[64];
if (opt_export) {
sprintf(dev_name, "dev_%s", m_info.device);
} else {
strcpy(dev_name, m_info.device);
}
if (opt_type) {
printf("%-16s %-8s %-10s %-10s %-10s %-5s %s\n", dev_name, m_info.fs_type, t_str, u_str, f_str, use_str, m_info.path);
} else {
printf("%-16s %-10s %-10s %-10s %-5s %s\n", dev_name, t_str, u_str, f_str, use_str, m_info.path);
}
}
}
if (opt_libxo) {
printf("\n ]\n }\n}\n");
} else if (opt_total && !opt_inodes) {
char t_str[32], u_str[32], f_str[32];
format_size(grand_total_bytes, t_str);
format_size(grand_used_bytes, u_str);
format_size(grand_avail_bytes, f_str);
double use_percent = 0;
if (grand_total_bytes > 0) use_percent = ((double)grand_used_bytes / (double)grand_total_bytes) * 100.0;
char use_str[16];
sprintf(use_str, "%.0f%%", use_percent);
if (opt_type) {
printf("%-16s %-8s %-10s %-10s %-10s %-5s -\n", "total", "-", t_str, u_str, f_str, use_str);
} else {
printf("%-16s %-10s %-10s %-10s %-5s -\n", "total", t_str, u_str, f_str, use_str);
}
}
return 0;
}