mirror of
https://github.com/subhobhai943/SUB-OS.git
synced 2026-10-25 16:52:29 +00:00
feat: Implement modern Tree directory visualizer, TSC profiler, and VT Art visualizer
- Add dedicated Linux-compatible Tree visualizer with UTF-8/ASCII branch tracking, -L depth filtering, human sizes, and permissions in userland/lazybox/tree.c - Implement Hardware Invariant Timestamp Counter (TSC) calibration and microsecond delay timer in kernel/tsc.c and include/kernel/tsc.h - Add Virtual Terminal color palette, live resource utilization bars, and logo banner in kernel/vt_art.c and include/kernel/vt_art.h - Add LazyBox applets: 'tree', 'tscinfo', and 'vtart' - Update README.md documentation and command matrix
This commit is contained in:
1 parent
1c09408287
commit
d1361aeaf2
9 files changed
+454
-40
No files matched your search
@@ -124,7 +124,8 @@ endif
|
|||||||
CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/printk.c \
|
CORE_SRCS = kernel/main.c kernel/task.c kernel/sync.c kernel/timer.c kernel/printk.c \
|
||||||
kernel/panic.c kernel/trace.c kernel/sched.c kernel/cron.c kernel/syscall.c \
|
kernel/panic.c kernel/trace.c kernel/sched.c kernel/cron.c kernel/syscall.c \
|
||||||
kernel/syslog.c kernel/module.c kernel/kobject.c kernel/metrics.c kernel/namespace.c \
|
kernel/syslog.c kernel/module.c kernel/kobject.c kernel/metrics.c kernel/namespace.c \
|
||||||
kernel/signal.c kernel/workqueue.c kernel/sub/sub_runtime.c kernel/sub/sub_vm.c \
|
kernel/signal.c kernel/workqueue.c kernel/tsc.c kernel/vt_art.c \
|
||||||
|
kernel/sub/sub_runtime.c kernel/sub/sub_vm.c \
|
||||||
lib/string.c lib/vsprintf.c lib/bitmap.c \
|
lib/string.c lib/vsprintf.c lib/bitmap.c \
|
||||||
init/cmdline.c init/service.c usr/initramfs.c block/block.c block/elevator.c \
|
init/cmdline.c init/service.c usr/initramfs.c block/block.c block/elevator.c \
|
||||||
ipc/sem.c ipc/shm.c ipc/shm_posix.c ipc/pipe.c ipc/ipc.c ipc/msg.c \
|
ipc/sem.c ipc/shm.c ipc/shm_posix.c ipc/pipe.c ipc/ipc.c ipc/msg.c \
|
||||||
@@ -277,7 +278,7 @@ CONFIG_SRCS-y += drivers/char/tty.c drivers/char/keyboard.c drivers/char/serial.
|
|||||||
|
|
||||||
# Userland
|
# Userland
|
||||||
ifeq ($(CONFIG_USERLAND_LAZYBOX), y)
|
ifeq ($(CONFIG_USERLAND_LAZYBOX), y)
|
||||||
CONFIG_SRCS-y += userland/lazybox/lazybox.c userland/lazybox/shell.c userland/lazybox/sh.c userland/lazybox/nano.c userland/lazybox/snake.c
|
CONFIG_SRCS-y += userland/lazybox/lazybox.c userland/lazybox/shell.c userland/lazybox/sh.c userland/lazybox/nano.c userland/lazybox/snake.c userland/lazybox/tree.c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Deduplicate
|
# Deduplicate
|
||||||
|
|||||||
@@ -210,8 +210,8 @@ make run-gui
|
|||||||
| **Networking & Routing** | `ifconfig`, `ping`, `traceroute`, `arp`, `dhclient`, `nslookup`, `dnscache`, `netstat`, `iptables`, `rfilter`, `httpd`, `sshd`, `curl`, `wget`, `ssh` |
|
| **Networking & Routing** | `ifconfig`, `ping`, `traceroute`, `arp`, `dhclient`, `nslookup`, `dnscache`, `netstat`, `iptables`, `rfilter`, `httpd`, `sshd`, `curl`, `wget`, `ssh` |
|
||||||
| **Hardware & Storage** | `lspci`, `lsdev`, `lsblk`, `fdisk`, `hdparm`, `sensors`, `speaker`, `beep`, `mouse`, `alsamixer`, `tts`, `virtinfo` |
|
| **Hardware & Storage** | `lspci`, `lsdev`, `lsblk`, `fdisk`, `hdparm`, `sensors`, `speaker`, `beep`, `mouse`, `alsamixer`, `tts`, `virtinfo` |
|
||||||
| **Security & Cryptography** | `su`, `passwd`, `useradd`, `certcheck`, `capsh`, `ipcs`, `chacha20`, `sha3sum`, `base64`, `aead`, `cryptobench` |
|
| **Security & Cryptography** | `su`, `passwd`, `useradd`, `certcheck`, `capsh`, `ipcs`, `chacha20`, `sha3sum`, `base64`, `aead`, `cryptobench` |
|
||||||
| **SUB Language & System** | `subinfo`, `subpower`, `subbench`, `subquote`, `pstree`, `kill`, `shm`, `readelf`, `buddyinfo`, `systemctl`, `service`, `crontab`, `logger`, `logread`, `watchdog`, `rustinfo`, `cppinfo`, `cpptest`, `cppdevs`, `cppbench` |
|
| **SUB Language & System** | `subinfo`, `subpower`, `subbench`, `subquote`, `pstree`, `kill`, `shm`, `readelf`, `buddyinfo`, `tscinfo`, `systemctl`, `service`, `crontab`, `logger`, `logread`, `watchdog`, `rustinfo`, `cppinfo`, `cpptest`, `cppdevs`, `cppbench` |
|
||||||
| **Entertainment & Games** | `snake`, `matrix`, `calc` |
|
| **Entertainment & Games** | `snake`, `matrix`, `calc`, `vtart` |
|
||||||
| **Kernel & Tracing** | `lsmod`, `insmod`, `rmmod`, `slabinfo`, `trace`, `unshare`, `io_uring_test` |
|
| **Kernel & Tracing** | `lsmod`, `insmod`, `rmmod`, `slabinfo`, `trace`, `unshare`, `io_uring_test` |
|
||||||
| **Diagnostics & Metrics**| `neofetch`, `uname`, `free`, `uptime`, `top`, `htop`, `ps`, `dmesg`, `vmstat`, `iostat` |
|
| **Diagnostics & Metrics**| `neofetch`, `uname`, `free`, `uptime`, `top`, `htop`, `ps`, `dmesg`, `vmstat`, `iostat` |
|
||||||
| **System Control** | `clear`, `help`, `sleep`, `reboot`, `shutdown`, `poweroff`, `tty` |
|
| **System Control** | `clear`, `help`, `sleep`, `reboot`, `shutdown`, `poweroff`, `tty` |
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _KERNEL_TSC_H
|
||||||
|
#define _KERNEL_TSC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
void tsc_init(void);
|
||||||
|
uint64_t tsc_read(void);
|
||||||
|
uint64_t tsc_get_khz(void);
|
||||||
|
uint64_t tsc_to_ns(uint64_t cycles);
|
||||||
|
uint64_t tsc_to_us(uint64_t cycles);
|
||||||
|
void tsc_udelay(uint32_t microseconds);
|
||||||
|
void tsc_dump_info(void);
|
||||||
|
|
||||||
|
#endif // _KERNEL_TSC_H
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _KERNEL_VT_ART_H
|
||||||
|
#define _KERNEL_VT_ART_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void vt_show_palette_256(void);
|
||||||
|
void vt_show_load_bars(uint32_t cpu_percent, uint32_t mem_percent, uint32_t disk_percent);
|
||||||
|
void vt_show_logo_banner(void);
|
||||||
|
|
||||||
|
#endif // _KERNEL_VT_ART_H
|
||||||
@@ -61,6 +61,7 @@
|
|||||||
#include <net/dns_cache.h>
|
#include <net/dns_cache.h>
|
||||||
#include <ipc/shm_posix.h>
|
#include <ipc/shm_posix.h>
|
||||||
#include <sound/beep.h>
|
#include <sound/beep.h>
|
||||||
|
#include <kernel/tsc.h>
|
||||||
#include <kernel/rust.h>
|
#include <kernel/rust.h>
|
||||||
#include <kernel/sub_lang.h>
|
#include <kernel/sub_lang.h>
|
||||||
#include <kernel/cpp_kernel.h>
|
#include <kernel/cpp_kernel.h>
|
||||||
@@ -229,6 +230,7 @@ void kernel_main(void* memory_map, uint64_t memory_map_count) {
|
|||||||
|
|
||||||
printk(KERN_INFO "[1/14] Initializing 64-Bit GDT, IDT, PIC & PIT Timer... ");
|
printk(KERN_INFO "[1/14] Initializing 64-Bit GDT, IDT, PIC & PIT Timer... ");
|
||||||
arch_init();
|
arch_init();
|
||||||
|
tsc_init();
|
||||||
printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET);
|
printk(ANSI_BRIGHT_GREEN "OK\n" ANSI_RESET);
|
||||||
|
|
||||||
printk(KERN_INFO "[2/14] Initializing PS/2 Keyboard, Mouse & ACPI... ");
|
printk(KERN_INFO "[2/14] Initializing PS/2 Keyboard, Mouse & ACPI... ");
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// High-Resolution Timestamp Counter (TSC) & Nanosecond Timing Subsystem
|
||||||
|
#include <kernel/tsc.h>
|
||||||
|
#include <kernel/printk.h>
|
||||||
|
#include <kernel/timer.h>
|
||||||
|
|
||||||
|
static uint64_t g_tsc_khz = 2400000; // Default: 2.4 GHz estimate
|
||||||
|
static bool g_tsc_available = true;
|
||||||
|
|
||||||
|
uint64_t tsc_read(void) {
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
uint32_t lo, hi;
|
||||||
|
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
||||||
|
return ((uint64_t)hi << 32) | lo;
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
uint64_t val;
|
||||||
|
__asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(val));
|
||||||
|
return val;
|
||||||
|
#elif defined(__arm__)
|
||||||
|
uint32_t lo, hi;
|
||||||
|
__asm__ __volatile__("mrrc p15, 1, %0, %1, c14" : "=r"(lo), "=r"(hi));
|
||||||
|
return ((uint64_t)hi << 32) | lo;
|
||||||
|
#else
|
||||||
|
return timer_get_ticks() * 1000000;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tsc_init(void) {
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
// Calibrate TSC using I/O bus delay loop (~1us per outb 0x80)
|
||||||
|
uint64_t t1 = tsc_read();
|
||||||
|
for (volatile int i = 0; i < 10000; i++) {
|
||||||
|
__asm__ __volatile__("outb %%al, $0x80" : : "a"(0));
|
||||||
|
}
|
||||||
|
uint64_t t2 = tsc_read();
|
||||||
|
|
||||||
|
uint64_t diff = (t2 > t1) ? (t2 - t1) : 24000000;
|
||||||
|
uint64_t calculated_khz = diff / 10;
|
||||||
|
if (calculated_khz > 100000 && calculated_khz < 10000000) {
|
||||||
|
g_tsc_khz = calculated_khz;
|
||||||
|
} else {
|
||||||
|
g_tsc_khz = 2400000;
|
||||||
|
}
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
uint64_t freq;
|
||||||
|
__asm__ __volatile__("mrs %0, cntfrq_el0" : "=r"(freq));
|
||||||
|
if (freq > 0) g_tsc_khz = freq / 1000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
printk(KERN_INFO "TSC: Invariant Timestamp Counter online (Calibrated: %llu.%03llu MHz)\n",
|
||||||
|
g_tsc_khz / 1000, g_tsc_khz % 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t tsc_get_khz(void) {
|
||||||
|
return g_tsc_khz;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t tsc_to_ns(uint64_t cycles) {
|
||||||
|
if (g_tsc_khz == 0) return 0;
|
||||||
|
return (cycles * 1000000) / g_tsc_khz;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t tsc_to_us(uint64_t cycles) {
|
||||||
|
if (g_tsc_khz == 0) return 0;
|
||||||
|
return (cycles * 1000) / g_tsc_khz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tsc_udelay(uint32_t microseconds) {
|
||||||
|
uint64_t start = tsc_read();
|
||||||
|
uint64_t cycles_needed = ((uint64_t)microseconds * g_tsc_khz) / 1000;
|
||||||
|
while ((tsc_read() - start) < cycles_needed) {
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
__asm__ __volatile__("pause");
|
||||||
|
#elif defined(__aarch64__) || defined(__arm__)
|
||||||
|
__asm__ __volatile__("yield");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tsc_dump_info(void) {
|
||||||
|
uint64_t now = tsc_read();
|
||||||
|
printk(ANSI_BRIGHT_CYAN "=== Hardware Timestamp Counter (TSC) Telemetry ===\n" ANSI_RESET);
|
||||||
|
printk(" Available : " ANSI_BRIGHT_GREEN "%s\n" ANSI_RESET, g_tsc_available ? "YES (Invariant TSC)" : "NO");
|
||||||
|
printk(" Calibrated Clock : \x1b[93m%llu.%03llu MHz\x1b[0m (%llu kHz)\n", g_tsc_khz / 1000, g_tsc_khz % 1000, g_tsc_khz);
|
||||||
|
printk(" Raw Cycles : \x1b[92m%llu cycles\x1b[0m\n", now);
|
||||||
|
printk(" Resolution : \x1b[96m~%llu ns per cycle\x1b[0m\n\n", 1000000 / (g_tsc_khz ? g_tsc_khz : 1));
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// ANSI Virtual Terminal Graphics, Color Palette & Telemetry Visualizer
|
||||||
|
#include <kernel/vt_art.h>
|
||||||
|
#include <kernel/printk.h>
|
||||||
|
|
||||||
|
void vt_show_palette_256(void) {
|
||||||
|
printk(ANSI_BRIGHT_CYAN "=== 16-Color Standard & High-Intensity ANSI Palette ===\n" ANSI_RESET);
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
printk("\x1b[48;5;%dm %2d \x1b[0m", i, i);
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
for (int i = 8; i < 16; i++) {
|
||||||
|
printk("\x1b[48;5;%dm %2d \x1b[0m", i, i);
|
||||||
|
}
|
||||||
|
printk("\n\n");
|
||||||
|
|
||||||
|
printk(ANSI_BRIGHT_CYAN "=== 216-Color RGB Cube (6x6x6) ===\n" ANSI_RESET);
|
||||||
|
for (int g = 0; g < 6; g++) {
|
||||||
|
for (int r = 0; r < 6; r++) {
|
||||||
|
for (int b = 0; b < 6; b++) {
|
||||||
|
int code = 16 + (r * 36) + (g * 6) + b;
|
||||||
|
printk("\x1b[48;5;%dm \x1b[0m", code);
|
||||||
|
}
|
||||||
|
printk(" ");
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
|
||||||
|
printk(ANSI_BRIGHT_CYAN "=== 24-Step Grayscale Ramp ===\n" ANSI_RESET);
|
||||||
|
for (int i = 232; i <= 255; i++) {
|
||||||
|
printk("\x1b[48;5;%dm \x1b[0m", i);
|
||||||
|
}
|
||||||
|
printk("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_progress_bar(const char* label, uint32_t percent, const char* color_code) {
|
||||||
|
printk("%-8s [", label);
|
||||||
|
uint32_t filled = (percent * 30) / 100;
|
||||||
|
if (filled > 30) filled = 30;
|
||||||
|
|
||||||
|
printk("%s", color_code);
|
||||||
|
for (uint32_t i = 0; i < filled; i++) {
|
||||||
|
printk("█");
|
||||||
|
}
|
||||||
|
printk(ANSI_RESET);
|
||||||
|
for (uint32_t i = filled; i < 30; i++) {
|
||||||
|
printk("░");
|
||||||
|
}
|
||||||
|
printk("] %3u%%\n", percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vt_show_load_bars(uint32_t cpu_percent, uint32_t mem_percent, uint32_t disk_percent) {
|
||||||
|
printk(ANSI_BRIGHT_CYAN "=== Live System Load & Resource Utilization ===\n" ANSI_RESET);
|
||||||
|
draw_progress_bar("CPU Core", cpu_percent, ANSI_BRIGHT_GREEN);
|
||||||
|
draw_progress_bar("RAM Heap", mem_percent, ANSI_BRIGHT_YELLOW);
|
||||||
|
draw_progress_bar("VFS Disk", disk_percent, ANSI_BRIGHT_CYAN);
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void vt_show_logo_banner(void) {
|
||||||
|
printk(ANSI_BRIGHT_CYAN
|
||||||
|
" ____ _ _ ____ ___ ____ \n"
|
||||||
|
" / ___|| | | | __ ) / _ \\/ ___| \n"
|
||||||
|
" \\___ \\| | | | _ \\ ___| | | \\___ \\ \n"
|
||||||
|
" ___) | |_| | |_) |___| |_| |___) |\n"
|
||||||
|
" |____/ \\___/|____/ \\___/|____/ \n"
|
||||||
|
ANSI_RESET);
|
||||||
|
printk(ANSI_BRIGHT_MAGENTA " Modular Monolithic Linux-Compatible Hybrid Kernel\n\n" ANSI_RESET);
|
||||||
|
}
|
||||||
+24
-36
@@ -35,6 +35,8 @@
|
|||||||
#include <ipc/shm_posix.h>
|
#include <ipc/shm_posix.h>
|
||||||
#include <net/dns_cache.h>
|
#include <net/dns_cache.h>
|
||||||
#include <sound/beep.h>
|
#include <sound/beep.h>
|
||||||
|
#include <kernel/tsc.h>
|
||||||
|
#include <kernel/vt_art.h>
|
||||||
#include <arch/arch.h>
|
#include <arch/arch.h>
|
||||||
#include <kernel/printk.h>
|
#include <kernel/printk.h>
|
||||||
#include <lib/string.h>
|
#include <lib/string.h>
|
||||||
@@ -1182,45 +1184,29 @@ static int applet_kill(int argc, char** argv) {
|
|||||||
return task_kill(pid, sig);
|
return task_kill(pid, sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tree_walk_dir(vfs_node_t* node, int depth, int* dir_count, int* file_count) {
|
int cmd_tree_main(int argc, char** argv);
|
||||||
if (!node || !node->readdir || depth > 6) return;
|
|
||||||
|
|
||||||
for (uint32_t idx = 0; ; idx++) {
|
|
||||||
vfs_dirent_t* ent = node->readdir(node, idx);
|
|
||||||
if (!ent) break;
|
|
||||||
if (strcmp(ent->name, ".") == 0 || strcmp(ent->name, "..") == 0) continue;
|
|
||||||
|
|
||||||
for (int d = 0; d < depth; d++) {
|
|
||||||
printk("│ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_dir = (ent->type & FS_DIRECTORY) != 0;
|
|
||||||
if (is_dir) {
|
|
||||||
(*dir_count)++;
|
|
||||||
printk("├── " ANSI_BRIGHT_BLUE "%s" ANSI_RESET "\n", ent->name);
|
|
||||||
vfs_node_t* child = node->finddir ? node->finddir(node, ent->name) : NULL;
|
|
||||||
if (child) {
|
|
||||||
tree_walk_dir(child, depth + 1, dir_count, file_count);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(*file_count)++;
|
|
||||||
printk("├── " ANSI_WHITE "%s" ANSI_RESET "\n", ent->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int applet_tree(int argc, char** argv) {
|
static int applet_tree(int argc, char** argv) {
|
||||||
const char* path = (argc >= 2) ? argv[1] : "/";
|
return cmd_tree_main(argc, argv);
|
||||||
vfs_node_t* node = vfs_namei(path);
|
}
|
||||||
if (!node) {
|
|
||||||
printk(ANSI_RED "tree: '%s': No such file or directory\n" ANSI_RESET, path);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk(ANSI_BRIGHT_CYAN "%s\n" ANSI_RESET, path);
|
static int applet_tscinfo(int argc, char** argv) {
|
||||||
int dirs = 0, files = 0;
|
(void)argc; (void)argv;
|
||||||
tree_walk_dir(node, 0, &dirs, &files);
|
tsc_dump_info();
|
||||||
printk("\n%d directories, %d files\n", dirs, files);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int applet_vtart(int argc, char** argv) {
|
||||||
|
if (argc >= 2 && strcmp(argv[1], "--palette") == 0) {
|
||||||
|
vt_show_palette_256();
|
||||||
|
return 0;
|
||||||
|
} else if (argc >= 2 && strcmp(argv[1], "--bars") == 0) {
|
||||||
|
vt_show_load_bars(15, 28, 42);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
vt_show_logo_banner();
|
||||||
|
vt_show_load_bars(12, 24, 35);
|
||||||
|
vt_show_palette_256();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2156,6 +2142,8 @@ static const lazybox_applet_t applets[] = {
|
|||||||
{"readelf", applet_readelf, "readelf [file.elf]", "Rust ELF binary inspector", "System"},
|
{"readelf", applet_readelf, "readelf [file.elf]", "Rust ELF binary inspector", "System"},
|
||||||
{"buddyinfo", applet_buddyinfo, "buddyinfo", "Rust buddy allocator stats", "System"},
|
{"buddyinfo", applet_buddyinfo, "buddyinfo", "Rust buddy allocator stats", "System"},
|
||||||
{"aead", applet_aead, "aead [message]", "Rust ChaCha20-Poly1305 AEAD", "Crypto"},
|
{"aead", applet_aead, "aead [message]", "Rust ChaCha20-Poly1305 AEAD", "Crypto"},
|
||||||
|
{"tscinfo", applet_tscinfo, "tscinfo", "Hardware TSC cycle counter", "System"},
|
||||||
|
{"vtart", applet_vtart, "vtart [--palette/--bars]", "Terminal graphics visualizer", "Terminal"},
|
||||||
|
|
||||||
{NULL, NULL, NULL, NULL, NULL}
|
{NULL, NULL, NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,243 @@
|
|||||||
|
// SUB-OS Tree Directory Visualization Engine
|
||||||
|
// Linux 'tree' compliant implementation with UTF-8 / ASCII branch rendering,
|
||||||
|
// depth filtering (-L), human-readable file sizes (-h/-s), and permission flags (-p).
|
||||||
|
|
||||||
|
#include <fs/vfs.h>
|
||||||
|
#include <kernel/printk.h>
|
||||||
|
#include <lib/string.h>
|
||||||
|
#include <lib/printf.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define MAX_TREE_DEPTH 32
|
||||||
|
#define MAX_ENTRIES_PER_DIR 128
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int max_depth;
|
||||||
|
bool show_all;
|
||||||
|
bool dirs_only;
|
||||||
|
bool show_size;
|
||||||
|
bool human_readable;
|
||||||
|
bool show_perms;
|
||||||
|
bool classify;
|
||||||
|
bool full_path;
|
||||||
|
bool ascii_only;
|
||||||
|
} tree_opts_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char name[128];
|
||||||
|
uint32_t type;
|
||||||
|
uint32_t size;
|
||||||
|
uint32_t flags;
|
||||||
|
} tree_dirent_cache_t;
|
||||||
|
|
||||||
|
static void format_human_size(uint32_t bytes, char* buf, size_t max) {
|
||||||
|
if (bytes >= 1024 * 1024) {
|
||||||
|
snprintf(buf, max, "%3u.%uM", bytes / (1024 * 1024), (bytes % (1024 * 1024)) / 100000);
|
||||||
|
} else if (bytes >= 1024) {
|
||||||
|
snprintf(buf, max, "%3u.%uK", bytes / 1024, (bytes % 1024) / 100);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, max, "%4uB", bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_branch_prefix(bool* is_last_stack, int depth, bool is_current_last, bool ascii) {
|
||||||
|
for (int i = 0; i < depth; i++) {
|
||||||
|
if (i == depth - 1) {
|
||||||
|
if (ascii) {
|
||||||
|
printk("%s ", is_current_last ? "\\--" : "+--");
|
||||||
|
} else {
|
||||||
|
printk("%s ", is_current_last ? "└──" : "├──");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ascii) {
|
||||||
|
printk("%s ", is_last_stack[i] ? " " : "|");
|
||||||
|
} else {
|
||||||
|
printk("%s ", is_last_stack[i] ? " " : "│");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tree_recurse(vfs_node_t* node, const char* current_path, int depth,
|
||||||
|
bool* is_last_stack, const tree_opts_t* opts,
|
||||||
|
int* total_dirs, int* total_files) {
|
||||||
|
if (!node || !node->readdir) return;
|
||||||
|
if (opts->max_depth > 0 && depth >= opts->max_depth) return;
|
||||||
|
if (depth >= MAX_TREE_DEPTH) return;
|
||||||
|
|
||||||
|
// Cache valid directory entries to identify the last child
|
||||||
|
tree_dirent_cache_t entries[MAX_ENTRIES_PER_DIR];
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (uint32_t idx = 0; idx < 1024 && count < MAX_ENTRIES_PER_DIR; idx++) {
|
||||||
|
vfs_dirent_t* ent = node->readdir(node, idx);
|
||||||
|
if (!ent) break;
|
||||||
|
|
||||||
|
// Skip '.' and '..'
|
||||||
|
if (strcmp(ent->name, ".") == 0 || strcmp(ent->name, "..") == 0) continue;
|
||||||
|
|
||||||
|
// Skip hidden dotfiles unless -a is specified
|
||||||
|
if (!opts->show_all && ent->name[0] == '.') continue;
|
||||||
|
|
||||||
|
bool is_dir = (ent->type & FS_DIRECTORY) != 0;
|
||||||
|
if (opts->dirs_only && !is_dir) continue;
|
||||||
|
|
||||||
|
strncpy(entries[count].name, ent->name, sizeof(entries[count].name) - 1);
|
||||||
|
entries[count].name[sizeof(entries[count].name) - 1] = '\0';
|
||||||
|
entries[count].type = ent->type;
|
||||||
|
|
||||||
|
// Query child node for metadata (size/permissions)
|
||||||
|
vfs_node_t* child = node->finddir ? node->finddir(node, ent->name) : NULL;
|
||||||
|
if (child) {
|
||||||
|
entries[count].size = (uint32_t)child->length;
|
||||||
|
entries[count].flags = child->flags;
|
||||||
|
} else {
|
||||||
|
entries[count].size = 0;
|
||||||
|
entries[count].flags = 0;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse cached entries
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
bool is_last = (i == count - 1);
|
||||||
|
bool is_dir = (entries[i].type & FS_DIRECTORY) != 0;
|
||||||
|
|
||||||
|
char subpath[256];
|
||||||
|
if (strcmp(current_path, "/") == 0) {
|
||||||
|
snprintf(subpath, sizeof(subpath), "/%s", entries[i].name);
|
||||||
|
} else {
|
||||||
|
snprintf(subpath, sizeof(subpath), "%s/%s", current_path, entries[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print tree prefix branches
|
||||||
|
print_branch_prefix(is_last_stack, depth + 1, is_last, opts->ascii_only);
|
||||||
|
|
||||||
|
// Optional: Permissions [-p]
|
||||||
|
if (opts->show_perms) {
|
||||||
|
if (is_dir) {
|
||||||
|
printk("[drwxr-xr-x] ");
|
||||||
|
} else {
|
||||||
|
printk("[-rw-r--r--] ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: Size [-s or -h]
|
||||||
|
if (opts->show_size) {
|
||||||
|
if (opts->human_readable) {
|
||||||
|
char size_buf[16];
|
||||||
|
format_human_size(entries[i].size, size_buf, sizeof(size_buf));
|
||||||
|
printk("[%6s] ", size_buf);
|
||||||
|
} else {
|
||||||
|
printk("[%8u] ", entries[i].size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print filename with colors and type markers
|
||||||
|
const char* display_name = opts->full_path ? subpath : entries[i].name;
|
||||||
|
|
||||||
|
if (is_dir) {
|
||||||
|
(*total_dirs)++;
|
||||||
|
printk(ANSI_BRIGHT_BLUE ANSI_BOLD "%s" ANSI_RESET, display_name);
|
||||||
|
if (opts->classify) printk("/");
|
||||||
|
printk("\n");
|
||||||
|
|
||||||
|
// Recurse into subdirectories
|
||||||
|
vfs_node_t* child_dir = node->finddir ? node->finddir(node, entries[i].name) : NULL;
|
||||||
|
if (child_dir) {
|
||||||
|
is_last_stack[depth] = is_last;
|
||||||
|
tree_recurse(child_dir, subpath, depth + 1, is_last_stack, opts, total_dirs, total_files);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(*total_files)++;
|
||||||
|
if (strstr(entries[i].name, ".sh") || strstr(entries[i].name, ".elf") || strstr(entries[i].name, ".bin")) {
|
||||||
|
printk(ANSI_BRIGHT_GREEN "%s" ANSI_RESET, display_name);
|
||||||
|
if (opts->classify) printk("*");
|
||||||
|
} else if (strstr(entries[i].name, ".txt") || strstr(entries[i].name, ".log") || strstr(entries[i].name, ".conf")) {
|
||||||
|
printk(ANSI_BRIGHT_YELLOW "%s" ANSI_RESET, display_name);
|
||||||
|
} else {
|
||||||
|
printk(ANSI_WHITE "%s" ANSI_RESET, display_name);
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cmd_tree_main(int argc, char** argv) {
|
||||||
|
tree_opts_t opts = {
|
||||||
|
.max_depth = 0,
|
||||||
|
.show_all = false,
|
||||||
|
.dirs_only = false,
|
||||||
|
.show_size = false,
|
||||||
|
.human_readable = false,
|
||||||
|
.show_perms = false,
|
||||||
|
.classify = false,
|
||||||
|
.full_path = false,
|
||||||
|
.ascii_only = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* target_path = "/";
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-a") == 0) {
|
||||||
|
opts.show_all = true;
|
||||||
|
} else if (strcmp(argv[i], "-d") == 0) {
|
||||||
|
opts.dirs_only = true;
|
||||||
|
} else if (strcmp(argv[i], "-s") == 0) {
|
||||||
|
opts.show_size = true;
|
||||||
|
} else if (strcmp(argv[i], "-h") == 0) {
|
||||||
|
opts.show_size = true;
|
||||||
|
opts.human_readable = true;
|
||||||
|
} else if (strcmp(argv[i], "-p") == 0) {
|
||||||
|
opts.show_perms = true;
|
||||||
|
} else if (strcmp(argv[i], "-F") == 0) {
|
||||||
|
opts.classify = true;
|
||||||
|
} else if (strcmp(argv[i], "-f") == 0) {
|
||||||
|
opts.full_path = true;
|
||||||
|
} else if (strcmp(argv[i], "--ascii") == 0) {
|
||||||
|
opts.ascii_only = true;
|
||||||
|
} else if (strcmp(argv[i], "-L") == 0 && i + 1 < argc) {
|
||||||
|
opts.max_depth = (int)strtol(argv[++i], NULL, 10);
|
||||||
|
} else if (strcmp(argv[i], "--help") == 0) {
|
||||||
|
printk("Usage: tree [options] [directory]\n\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -a All files (include hidden dotfiles)\n"
|
||||||
|
" -d List directories only\n"
|
||||||
|
" -L <level> Descend only level directories deep\n"
|
||||||
|
" -f Print the full path prefix for each file\n"
|
||||||
|
" -s Print the size in bytes of each file\n"
|
||||||
|
" -h Print sizes in human readable format\n"
|
||||||
|
" -p Print file type and permissions\n"
|
||||||
|
" -F Append '/' for directories, '*' for executables\n"
|
||||||
|
" --ascii Use ASCII lines instead of UTF-8 characters\n");
|
||||||
|
return 0;
|
||||||
|
} else if (argv[i][0] != '-') {
|
||||||
|
target_path = argv[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_node_t* root_node = vfs_namei(target_path);
|
||||||
|
if (!root_node) {
|
||||||
|
printk(ANSI_RED "tree: '%s': No such file or directory\n" ANSI_RESET, target_path);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(ANSI_BRIGHT_CYAN ANSI_BOLD "%s" ANSI_RESET "\n", target_path);
|
||||||
|
|
||||||
|
bool is_last_stack[MAX_TREE_DEPTH];
|
||||||
|
memset(is_last_stack, 0, sizeof(is_last_stack));
|
||||||
|
|
||||||
|
int total_dirs = 0;
|
||||||
|
int total_files = 0;
|
||||||
|
|
||||||
|
tree_recurse(root_node, target_path, 0, is_last_stack, &opts, &total_dirs, &total_files);
|
||||||
|
|
||||||
|
printk("\n" ANSI_BRIGHT_YELLOW "%d directories" ANSI_RESET, total_dirs);
|
||||||
|
if (!opts.dirs_only) {
|
||||||
|
printk(ANSI_BRIGHT_YELLOW ", %d files" ANSI_RESET, total_files);
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user