fix(vfs): resolve CWD tracking, filename clobbering, touch creation, and nano full-screen editing & saving

This commit is contained in:
subhobhai943 committed 2026-08-17 12:26:07 +05:30
1 parent d680335f05
commit 9f9a19bab0
5 files changed
+282 -123

No files matched your search

+182 -77
View File
@@ -10,9 +10,11 @@
static vfs_node_t* vfs_root = NULL; static vfs_node_t* vfs_root = NULL;
static file_descriptor_t file_descriptors[MAX_OPEN_FILES]; static file_descriptor_t file_descriptors[MAX_OPEN_FILES];
static char vfs_cwd[256] = "/";
void vfs_init(void) { void vfs_init(void) {
memset(file_descriptors, 0, sizeof(file_descriptors)); memset(file_descriptors, 0, sizeof(file_descriptors));
strcpy(vfs_cwd, "/");
// 1. Create root RAM filesystem // 1. Create root RAM filesystem
vfs_root = ramfs_create_root(); vfs_root = ramfs_create_root();
@@ -32,14 +34,104 @@ vfs_node_t* vfs_get_root(void) {
return vfs_root; return vfs_root;
} }
const char* vfs_getcwd(void) {
return vfs_cwd;
}
void vfs_resolve_path(const char* path, char* resolved_out, size_t max_len) {
if (!resolved_out || max_len == 0) return;
char temp[512];
if (!path || path[0] == '\0') {
strncpy(resolved_out, vfs_cwd, max_len - 1);
resolved_out[max_len - 1] = '\0';
return;
}
if (path[0] == '/') {
strncpy(temp, path, sizeof(temp) - 1);
} else {
if (strcmp(vfs_cwd, "/") == 0) {
snprintf(temp, sizeof(temp), "/%s", path);
} else {
snprintf(temp, sizeof(temp), "%s/%s", vfs_cwd, path);
}
}
temp[sizeof(temp) - 1] = '\0';
// Normalize '.' and '..' components
char* stack[32];
int top = 0;
char* token = temp;
while (*token == '/') token++;
while (*token) {
char* next_slash = strchr(token, '/');
if (next_slash) *next_slash = '\0';
if (strcmp(token, ".") == 0 || token[0] == '\0') {
// Do nothing
} else if (strcmp(token, "..") == 0) {
if (top > 0) top--;
} else {
if (top < 32) {
stack[top++] = token;
}
}
if (next_slash) {
token = next_slash + 1;
while (*token == '/') token++;
} else {
break;
}
}
if (top == 0) {
strcpy(resolved_out, "/");
return;
}
resolved_out[0] = '\0';
for (int i = 0; i < top; i++) {
strcat(resolved_out, "/");
strcat(resolved_out, stack[i]);
}
}
int vfs_chdir(const char* path) {
if (!path || path[0] == '\0') return -1;
char abs_path[256];
vfs_resolve_path(path, abs_path, sizeof(abs_path));
vfs_node_t* node = vfs_namei(abs_path);
if (!node) {
return -1; // Directory does not exist
}
if (!(node->flags & FS_DIRECTORY)) {
return -2; // Not a directory
}
strncpy(vfs_cwd, abs_path, sizeof(vfs_cwd) - 1);
vfs_cwd[sizeof(vfs_cwd) - 1] = '\0';
return 0;
}
vfs_node_t* vfs_namei(const char* path) { vfs_node_t* vfs_namei(const char* path) {
if (!path || !vfs_root) return NULL; if (!path || !vfs_root) return NULL;
if (strcmp(path, "/") == 0) return vfs_root;
if (*path == '/') path++; // Skip leading slash char abs_path[256];
vfs_resolve_path(path, abs_path, sizeof(abs_path));
if (strcmp(abs_path, "/") == 0) return vfs_root;
char buffer[256]; char buffer[256];
strncpy(buffer, path, sizeof(buffer) - 1); const char* p = abs_path;
if (*p == '/') p++;
strncpy(buffer, p, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; buffer[sizeof(buffer) - 1] = '\0';
vfs_node_t* current = vfs_root; vfs_node_t* current = vfs_root;
@@ -65,6 +157,7 @@ vfs_node_t* vfs_namei(const char* path) {
if (next_slash) { if (next_slash) {
token = next_slash + 1; token = next_slash + 1;
while (*token == '/') token++;
} else { } else {
break; break;
} }
@@ -74,26 +167,45 @@ vfs_node_t* vfs_namei(const char* path) {
} }
int vfs_open(const char* path, uint32_t flags) { int vfs_open(const char* path, uint32_t flags) {
vfs_node_t* node = vfs_namei(path); if (!path) return -1;
char abs_path[256];
vfs_resolve_path(path, abs_path, sizeof(abs_path));
vfs_node_t* node = vfs_namei(abs_path);
if (!node) { if (!node) {
if (flags & O_CREAT) { if (flags & O_CREAT) {
// Find parent and create // Find parent and create
char parent_path[256]; char parent_path[256];
strncpy(parent_path, path, sizeof(parent_path) - 1); strncpy(parent_path, abs_path, sizeof(parent_path) - 1);
parent_path[sizeof(parent_path) - 1] = '\0';
char* last_slash = strrchr(parent_path, '/'); char* last_slash = strrchr(parent_path, '/');
if (last_slash) { if (last_slash) {
char filename[128];
strncpy(filename, last_slash + 1, sizeof(filename) - 1);
filename[sizeof(filename) - 1] = '\0';
if (last_slash == parent_path) {
parent_path[1] = '\0'; // Root "/"
} else {
*last_slash = '\0'; *last_slash = '\0';
const char* filename = last_slash + 1; }
vfs_node_t* parent = vfs_namei(parent_path[0] ? parent_path : "/");
vfs_node_t* parent = vfs_namei(parent_path);
if (parent && parent->create) { if (parent && parent->create) {
parent->create(parent, filename, 0644); parent->create(parent, filename, 0644);
node = vfs_namei(path); node = vfs_namei(abs_path);
} }
} }
} }
if (!node) return -1; if (!node) return -1;
} }
if (flags & O_TRUNC) {
node->length = 0;
}
if (node->open) { if (node->open) {
if (node->open(node, flags) != 0) return -1; if (node->open(node, flags) != 0) return -1;
} }
@@ -102,7 +214,7 @@ int vfs_open(const char* path, uint32_t flags) {
for (int fd = 3; fd < MAX_OPEN_FILES; fd++) { for (int fd = 3; fd < MAX_OPEN_FILES; fd++) {
if (!file_descriptors[fd].node) { if (!file_descriptors[fd].node) {
file_descriptors[fd].node = node; file_descriptors[fd].node = node;
file_descriptors[fd].offset = 0; file_descriptors[fd].offset = (flags & O_APPEND) ? (off_t)node->length : 0;
file_descriptors[fd].flags = flags; file_descriptors[fd].flags = flags;
return fd; return fd;
} }
@@ -160,100 +272,93 @@ off_t vfs_lseek(int fd, off_t offset, int whence) {
if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1; if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1;
file_descriptor_t* desc = &file_descriptors[fd]; file_descriptor_t* desc = &file_descriptors[fd];
if (whence == SEEK_SET) desc->offset = offset; switch (whence) {
else if (whence == SEEK_CUR) desc->offset += offset; case SEEK_SET:
else if (whence == SEEK_END) desc->offset = desc->node->length + offset; desc->offset = offset;
break;
case SEEK_CUR:
desc->offset += offset;
break;
case SEEK_END:
desc->offset = (off_t)desc->node->length + offset;
break;
default:
return -1;
}
return desc->offset; return desc->offset;
} }
int vfs_close(int fd) { int vfs_close(int fd) {
if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1; if (fd < 0 || fd >= MAX_OPEN_FILES || !file_descriptors[fd].node) return -1;
if (file_descriptors[fd].node->close) { file_descriptor_t* desc = &file_descriptors[fd];
file_descriptors[fd].node->close(file_descriptors[fd].node); if (desc->node->close) {
desc->node->close(desc->node);
} }
file_descriptors[fd].node = NULL;
file_descriptors[fd].offset = 0; desc->node = NULL;
desc->offset = 0;
desc->flags = 0;
return 0; return 0;
} }
int vfs_mkdir(const char* path, mode_t mode) { int vfs_mkdir(const char* path, mode_t mode) {
if (!path || !*path) return -1; if (!path) return -1;
char abs_path[256];
vfs_resolve_path(path, abs_path, sizeof(abs_path));
char parent_path[256]; char parent_path[256];
strncpy(parent_path, path, sizeof(parent_path) - 1); strncpy(parent_path, abs_path, sizeof(parent_path) - 1);
parent_path[sizeof(parent_path) - 1] = '\0'; parent_path[sizeof(parent_path) - 1] = '\0';
char* last_slash = strrchr(parent_path, '/'); char* last_slash = strrchr(parent_path, '/');
const char* dirname = path; if (!last_slash) return -1;
vfs_node_t* parent = vfs_get_root();
char dirname[128];
strncpy(dirname, last_slash + 1, sizeof(dirname) - 1);
dirname[sizeof(dirname) - 1] = '\0';
if (last_slash) {
if (last_slash == parent_path) { if (last_slash == parent_path) {
parent = vfs_namei("/"); parent_path[1] = '\0'; // Root "/"
dirname = last_slash + 1;
} else { } else {
*last_slash = '\0'; *last_slash = '\0';
dirname = last_slash + 1;
parent = vfs_namei(parent_path);
}
} }
if (parent && parent->mkdir) { vfs_node_t* parent = vfs_namei(parent_path);
if (!parent || !parent->mkdir) return -1;
return parent->mkdir(parent, dirname, mode); return parent->mkdir(parent, dirname, mode);
}
return -1;
} }
int vfs_create(const char* path, mode_t mode) { int vfs_create(const char* path, mode_t mode) {
if (!path || !*path) return -1; int fd = vfs_open(path, O_CREAT | O_RDWR);
char parent_path[256]; if (fd >= 0) {
strncpy(parent_path, path, sizeof(parent_path) - 1); vfs_close(fd);
parent_path[sizeof(parent_path) - 1] = '\0';
char* last_slash = strrchr(parent_path, '/');
const char* filename = path;
vfs_node_t* parent = vfs_get_root();
if (last_slash) {
if (last_slash == parent_path) {
parent = vfs_namei("/");
filename = last_slash + 1;
} else {
*last_slash = '\0';
filename = last_slash + 1;
parent = vfs_namei(parent_path);
}
}
if (parent && parent->create) {
return parent->create(parent, filename, mode);
}
return -1;
}
int vfs_mount(const char* path, vfs_node_t* fs_root) {
if (!path || !fs_root) return -1;
vfs_node_t* mount_node = vfs_namei(path);
if (!mount_node) {
// Create directory node for mount point
char parent_path[256];
strncpy(parent_path, path, sizeof(parent_path) - 1);
char* last_slash = strrchr(parent_path, '/');
if (last_slash) {
*last_slash = '\0';
const char* dirname = last_slash + 1;
vfs_node_t* parent = vfs_namei(parent_path[0] ? parent_path : "/");
if (parent && parent->mkdir) {
parent->mkdir(parent, dirname, 0755);
mount_node = vfs_namei(path);
}
}
}
if (mount_node) {
mount_node->flags |= FS_MOUNTPOINT;
mount_node->ptr = fs_root;
return 0; return 0;
} }
return -1; return -1;
} }
int vfs_unlink(const char* path) {
(void)path;
return 0;
}
int vfs_mount(const char* path, vfs_node_t* fs_root) {
if (!path || !fs_root) return -1;
vfs_node_t* mountpoint = vfs_namei(path);
if (!mountpoint) {
// Automatically create directory if missing
if (vfs_mkdir(path, 0755) == 0) {
mountpoint = vfs_namei(path);
}
}
if (!mountpoint) return -1;
mountpoint->flags |= FS_MOUNTPOINT;
mountpoint->ptr = fs_root;
return 0;
}
+4
View File
@@ -82,5 +82,9 @@ int vfs_create(const char* path, mode_t mode);
vfs_node_t* vfs_get_root(void); vfs_node_t* vfs_get_root(void);
int vfs_mount(const char* path, vfs_node_t* fs_root); int vfs_mount(const char* path, vfs_node_t* fs_root);
const char* vfs_getcwd(void);
int vfs_chdir(const char* path);
void vfs_resolve_path(const char* path, char* resolved_out, size_t max_len);
int vfs_unlink(const char* path);
#endif // _FS_VFS_H #endif // _FS_VFS_H
+6 -4
View File
@@ -40,7 +40,7 @@
// ------------------------------------------------------------- // -------------------------------------------------------------
static int applet_ls(int argc, char** argv) { static int applet_ls(int argc, char** argv) {
const char* path = (argc >= 2) ? argv[1] : "/"; const char* path = (argc >= 2) ? argv[1] : vfs_getcwd();
vfs_node_t* dir = vfs_namei(path); vfs_node_t* dir = vfs_namei(path);
if (!dir) { if (!dir) {
@@ -112,6 +112,8 @@ static int applet_touch(int argc, char** argv) {
int fd = vfs_open(argv[i], O_CREAT | O_RDWR); int fd = vfs_open(argv[i], O_CREAT | O_RDWR);
if (fd >= 0) { if (fd >= 0) {
vfs_close(fd); vfs_close(fd);
} else {
printk(KERN_ERR "touch: cannot touch '%s': No such file or directory\n", argv[i]);
} }
} }
return 0; return 0;
@@ -141,14 +143,14 @@ static int applet_echo(int argc, char** argv) {
static int applet_pwd(int argc, char** argv) { static int applet_pwd(int argc, char** argv) {
(void)argc; (void)argv; (void)argc; (void)argv;
printk("/\n"); printk("%s\n", vfs_getcwd());
return 0; return 0;
} }
static int applet_cd(int argc, char** argv) { static int applet_cd(int argc, char** argv) {
const char* target = (argc >= 2) ? argv[1] : "/"; const char* target = (argc >= 2) ? argv[1] : "/";
vfs_node_t* node = vfs_namei(target); int res = vfs_chdir(target);
if (!node || !(node->flags & FS_DIRECTORY)) { if (res != 0) {
printk(KERN_ERR "cd: %s: No such file or directory\n", target); printk(KERN_ERR "cd: %s: No such file or directory\n", target);
return 1; return 1;
} }
+57 -25
View File
@@ -16,39 +16,46 @@ static int nano_cursor_row = 0;
static int nano_cursor_col = 0; static int nano_cursor_col = 0;
static int nano_top_row = 0; static int nano_top_row = 0;
static bool nano_modified = false; static bool nano_modified = false;
static char nano_filename[128]; static char nano_filename[256];
static char nano_status[80]; static char nano_status[128];
static char nano_cut_buffer[NANO_LINE_LEN]; static char nano_cut_buffer[NANO_LINE_LEN];
static bool nano_has_cut = false; static bool nano_has_cut = false;
static void nano_render(void) { static void nano_render(void) {
tty_clear(); // 1. Title bar (Row 0)
tty_set_cursor(0, 0);
// 1. Title bar (Inverted Color) char title[128];
printk(ANSI_INVERT " GNU nano 2.0.0 File: %-32s %s" ANSI_RESET "\n", snprintf(title, sizeof(title), " GNU nano 2.0.0 File: %-32s %s",
nano_filename[0] ? nano_filename : "New Buffer", nano_filename[0] ? nano_filename : "New Buffer",
nano_modified ? "[Modified]" : " "); nano_modified ? "[Modified]" : " ");
printk(ANSI_INVERT "%-80s" ANSI_RESET, title);
// 2. Viewport Lines (Rows 1 to 21) // 2. Viewport Lines (Rows 1 to 21)
for (int r = 0; r < NANO_SCREEN_ROWS; r++) { for (int r = 0; r < NANO_SCREEN_ROWS; r++) {
tty_set_cursor(r + 1, 0);
int line_idx = nano_top_row + r; int line_idx = nano_top_row + r;
if (line_idx < (int)nano_line_count) { if (line_idx < (int)nano_line_count) {
printk("%s\n", nano_lines[line_idx]); printk("\033[K%s", nano_lines[line_idx]);
} else { } else {
printk("~\n"); printk("\033[K" ANSI_BRIGHT_BLACK "~" ANSI_RESET);
} }
} }
// 3. Status Message Bar // 3. Status Message Bar (Row 22)
printk(ANSI_BRIGHT_CYAN "%-78s" ANSI_RESET "\n", nano_status); tty_set_cursor(22, 0);
printk(ANSI_BRIGHT_CYAN "\033[K%-78s" ANSI_RESET, nano_status);
// 4. Shortcut Cheatsheet Footer // 4. Shortcut Cheatsheet Footer (Row 23)
printk(ANSI_INVERT "^G Get Help ^O WriteOut ^W Where Is ^K Cut Line ^U Paste ^X Exit" ANSI_RESET "\n"); tty_set_cursor(23, 0);
printk(ANSI_INVERT "%-80s" ANSI_RESET,
"^G Help ^O WriteOut ^W Where Is ^K Cut Line ^U Paste ^X Exit");
// 5. Position Hardware Cursor // 5. Position Hardware Cursor
int scr_row = (nano_cursor_row - nano_top_row) + 2; int scr_row = (nano_cursor_row - nano_top_row) + 1;
int scr_col = nano_cursor_col + 1; int scr_col = nano_cursor_col;
tty_set_cursor(scr_row - 1, scr_col - 1); if (scr_row >= 1 && scr_row <= NANO_SCREEN_ROWS) {
tty_set_cursor(scr_row, scr_col);
}
} }
static void nano_save(void) { static void nano_save(void) {
@@ -59,7 +66,7 @@ static void nano_save(void) {
int fd = vfs_open(nano_filename, O_CREAT | O_WRONLY | O_TRUNC); int fd = vfs_open(nano_filename, O_CREAT | O_WRONLY | O_TRUNC);
if (fd < 0) { if (fd < 0) {
strcpy(nano_status, "[ Error opening file for writing ]"); strcpy(nano_status, "[ Error: Cannot open file for writing ]");
return; return;
} }
@@ -78,7 +85,7 @@ static void nano_save(void) {
vfs_close(fd); vfs_close(fd);
nano_modified = false; nano_modified = false;
sprintf(nano_status, "[ Wrote %llu bytes to %s ]", (uint64_t)total_bytes, nano_filename); snprintf(nano_status, sizeof(nano_status), "[ Wrote %llu bytes to %s ]", (uint64_t)total_bytes, nano_filename);
} }
static void nano_load(const char* path) { static void nano_load(const char* path) {
@@ -90,13 +97,21 @@ static void nano_load(const char* path) {
nano_modified = false; nano_modified = false;
nano_status[0] = '\0'; nano_status[0] = '\0';
strncpy(nano_filename, path, sizeof(nano_filename) - 1); if (!path || path[0] == '\0') {
nano_filename[0] = '\0';
nano_line_count = 1;
nano_lines[0][0] = '\0';
strcpy(nano_status, "[ New Buffer - Use ^O to save ]");
return;
}
int fd = vfs_open(path, O_RDONLY); vfs_resolve_path(path, nano_filename, sizeof(nano_filename));
int fd = vfs_open(nano_filename, O_RDONLY);
if (fd < 0) { if (fd < 0) {
nano_line_count = 1; nano_line_count = 1;
nano_lines[0][0] = '\0'; nano_lines[0][0] = '\0';
sprintf(nano_status, "[ New File: %s ]", path); snprintf(nano_status, sizeof(nano_status), "[ New File: %s ]", nano_filename);
return; return;
} }
@@ -129,13 +144,14 @@ static void nano_load(const char* path) {
cur_line++; cur_line++;
} }
nano_line_count = cur_line > 0 ? cur_line : 1; nano_line_count = cur_line > 0 ? cur_line : 1;
sprintf(nano_status, "[ Read %llu line(s) from %s ]", (uint64_t)nano_line_count, path); snprintf(nano_status, sizeof(nano_status), "[ Read %llu line(s) from %s ]", (uint64_t)nano_line_count, nano_filename);
} }
int nano_main(int argc, char** argv) { int nano_main(int argc, char** argv) {
tty_clear();
if (argc < 2) { if (argc < 2) {
nano_load(""); nano_load("");
strcpy(nano_status, "[ New Buffer - Use ^O to save ]");
} else { } else {
nano_load(argv[1]); nano_load(argv[1]);
} }
@@ -162,11 +178,16 @@ int nano_main(int argc, char** argv) {
uint16_t key = keyboard_get_key(); uint16_t key = keyboard_get_key();
switch (key) { switch (key) {
case 0x18: // Ctrl+X -> Exit case 0x18: // Ctrl+X
case 0x11: // Ctrl+Q
case 27: // ESC
case KEY_F10:
running = false; running = false;
break; break;
case 0x0F: // Ctrl+O -> Write Out / Save case 0x0F: // Ctrl+O
case 0x13: // Ctrl+S
case KEY_F2:
nano_save(); nano_save();
break; break;
@@ -200,6 +221,7 @@ int nano_main(int argc, char** argv) {
break; break;
case 0x07: // Ctrl+G -> Help case 0x07: // Ctrl+G -> Help
case KEY_F1:
strcpy(nano_status, "[ Help: ^O=Save, ^X=Exit, ^K=Cut, ^U=Paste, Arrows=Move ]"); strcpy(nano_status, "[ Help: ^O=Save, ^X=Exit, ^K=Cut, ^U=Paste, Arrows=Move ]");
break; break;
@@ -250,7 +272,8 @@ int nano_main(int argc, char** argv) {
nano_cursor_row + NANO_SCREEN_ROWS : (int)nano_line_count - 1; nano_cursor_row + NANO_SCREEN_ROWS : (int)nano_line_count - 1;
break; break;
case '\n': // Enter -> Split line case '\n':
case '\r': // Enter -> Split line
if (nano_line_count < NANO_MAX_LINES) { if (nano_line_count < NANO_MAX_LINES) {
for (int i = (int)nano_line_count; i > nano_cursor_row + 1; i--) { for (int i = (int)nano_line_count; i > nano_cursor_row + 1; i--) {
strcpy(nano_lines[i], nano_lines[i - 1]); strcpy(nano_lines[i], nano_lines[i - 1]);
@@ -286,6 +309,15 @@ int nano_main(int argc, char** argv) {
} }
break; break;
case KEY_DELETE:
if (nano_cursor_col < line_len) {
memmove(&nano_lines[nano_cursor_row][nano_cursor_col],
&nano_lines[nano_cursor_row][nano_cursor_col + 1],
strlen(&nano_lines[nano_cursor_row][nano_cursor_col + 1]) + 1);
nano_modified = true;
}
break;
default: default:
if (key >= 32 && key <= 126) { if (key >= 32 && key <= 126) {
char c = (char)key; char c = (char)key;
+33 -17
View File
@@ -1,5 +1,6 @@
#include <userland/shell.h> #include <userland/shell.h>
#include <userland/lazybox.h> #include <userland/lazybox.h>
#include <fs/vfs.h>
#include <drivers/tty.h> #include <drivers/tty.h>
#include <drivers/keyboard.h> #include <drivers/keyboard.h>
#include <drivers/speaker.h> #include <drivers/speaker.h>
@@ -19,11 +20,14 @@ static int history_count = 0;
static int history_head = 0; static int history_head = 0;
static const char* shell_builtins[] = { static const char* shell_builtins[] = {
"lazybox", "ls", "cat", "touch", "mkdir", "wc", "echo", "lazybox", "nano", "ls", "cat", "touch", "mkdir", "pwd", "cd", "wc", "echo",
"md5sum", "sha256sum", "crc32", "ifconfig", "ping", "arp", "head", "tail", "stat", "cp", "grep", "hexdump", "tts", "alsamixer",
"hdparm", "uname", "free", "uptime", "dmesg", "sleep", "clear", "lsmod", "insmod", "rmmod", "md5sum", "sha256sum", "crc32", "rand",
"help", "neofetch", "calc", "hexdump", "matrix", "history", "tty", "certcheck", "capsh", "ipcs", "ifconfig", "ping", "arp", "dhclient",
"beep", "reboot", "shutdown", NULL "nslookup", "hdparm", "lspci", "speaker", "mouse", "virtinfo",
"io_uring_test", "uname", "free", "uptime", "dmesg", "ps", "top",
"sleep", "reboot", "poweroff", "clear", "help", "neofetch", "calc",
"matrix", "history", "tty", "beep", "shutdown", NULL
}; };
static void history_add(const char* cmd) { static void history_add(const char* cmd) {
@@ -51,26 +55,34 @@ static void redraw_line(const char* prompt, const char* buffer, size_t len, size
static void cmd_help(void) { static void cmd_help(void) {
printk(ANSI_BRIGHT_CYAN "================== SUB-OS Production Command Reference ==================\n" ANSI_RESET); printk(ANSI_BRIGHT_CYAN "================== SUB-OS Production Command Reference ==================\n" ANSI_RESET);
printk(ANSI_YELLOW " [File & Storage Tools]\n" ANSI_RESET); printk(ANSI_YELLOW " [File & Storage Tools]\n" ANSI_RESET);
printk(" " ANSI_GREEN "nano [file]" ANSI_RESET " - Full-screen visual interactive text editor\n");
printk(" " ANSI_GREEN "ls [path]" ANSI_RESET " - Directory list with type & inode numbers\n"); printk(" " ANSI_GREEN "ls [path]" ANSI_RESET " - Directory list with type & inode numbers\n");
printk(" " ANSI_GREEN "cat <file>" ANSI_RESET " - Display file contents (supports /proc & /dev)\n"); printk(" " ANSI_GREEN "cat <file>" ANSI_RESET " - Display file contents (supports /proc & /dev)\n");
printk(" " ANSI_GREEN "touch / mkdir" ANSI_RESET " - Create files and directories in VFS\n"); printk(" " ANSI_GREEN "touch <file>" ANSI_RESET " - Create empty files in VFS\n");
printk(" " ANSI_GREEN "mkdir <dir>" ANSI_RESET " - Create directories in VFS\n");
printk(" " ANSI_GREEN "cd [dir] / pwd" ANSI_RESET " - Change / Print current working directory\n");
printk(" " ANSI_GREEN "hdparm" ANSI_RESET " - Inspect ATA hard disk parameters & sectors\n\n"); printk(" " ANSI_GREEN "hdparm" ANSI_RESET " - Inspect ATA hard disk parameters & sectors\n\n");
printk(ANSI_YELLOW " [Sound & Audio]\n" ANSI_RESET);
printk(" " ANSI_GREEN "tts <text>" ANSI_RESET " - Phonetic formant voice synthesizer\n");
printk(" " ANSI_GREEN "alsamixer" ANSI_RESET " - Sound architecture & audio card status\n\n");
printk(ANSI_YELLOW " [Kernel & Modules]\n" ANSI_RESET);
printk(" " ANSI_GREEN "lsmod / insmod / rmmod" ANSI_RESET " - Manage dynamically loadable kernel modules\n\n");
printk(ANSI_YELLOW " [Cryptography & Security]\n" ANSI_RESET); printk(ANSI_YELLOW " [Cryptography & Security]\n" ANSI_RESET);
printk(" " ANSI_GREEN "sha256sum <file>" ANSI_RESET " - FIPS 180-4 SHA-256 secure hash\n"); printk(" " ANSI_GREEN "sha256sum <file>" ANSI_RESET " - FIPS 180-4 SHA-256 secure hash\n");
printk(" " ANSI_GREEN "md5sum <file>" ANSI_RESET " - RFC 1321 MD5 message digest\n"); printk(" " ANSI_GREEN "md5sum <file>" ANSI_RESET " - RFC 1321 MD5 message digest\n");
printk(" " ANSI_GREEN "crc32 <text>" ANSI_RESET " - IEEE 802.3 32-bit CRC calculation\n\n"); printk(" " ANSI_GREEN "crc32 <text>" ANSI_RESET " - IEEE 802.3 32-bit CRC calculation\n");
printk(" " ANSI_GREEN "certcheck / capsh" ANSI_RESET " - X.509 keyring & POSIX capabilities\n\n");
printk(ANSI_YELLOW " [Networking Suite]\n" ANSI_RESET); printk(ANSI_YELLOW " [Networking Suite]\n" ANSI_RESET);
printk(" " ANSI_GREEN "ifconfig" ANSI_RESET " - Ethernet eth0 configuration & RX/TX metrics\n"); printk(" " ANSI_GREEN "ifconfig" ANSI_RESET " - Ethernet eth0 configuration & RX/TX metrics\n");
printk(" " ANSI_GREEN "ping <ip>" ANSI_RESET " - Send ICMP echo requests with latency ms\n"); printk(" " ANSI_GREEN "ping <ip>" ANSI_RESET " - Send ICMP echo requests with latency ms\n");
printk(" " ANSI_GREEN "arp" ANSI_RESET " - Display kernel ARP resolution cache table\n\n"); printk(" " ANSI_GREEN "dhclient" ANSI_RESET " - Request IP lease from DHCP server\n");
printk(" " ANSI_GREEN "nslookup <host>" ANSI_RESET " - Query DNS domain name server\n\n");
printk(ANSI_YELLOW " [System & Diagnostics]\n" ANSI_RESET); printk(ANSI_YELLOW " [System & Diagnostics]\n" ANSI_RESET);
printk(" " ANSI_GREEN "neofetch" ANSI_RESET " - System information & OS ASCII badge\n"); printk(" " ANSI_GREEN "neofetch" ANSI_RESET " - System information & OS ASCII badge\n");
printk(" " ANSI_GREEN "free / uptime" ANSI_RESET " - Memory metrics and CPU uptime\n"); printk(" " ANSI_GREEN "free / uptime" ANSI_RESET " - Memory metrics and CPU uptime\n");
printk(" " ANSI_GREEN "dmesg" ANSI_RESET " - Kernel boot ring buffer log\n"); printk(" " ANSI_GREEN "dmesg / ps / top" ANSI_RESET " - Kernel boot log & process manager\n");
printk(" " ANSI_GREEN "hexdump [addr] [n]" ANSI_RESET " - Memory hex inspector\n");
printk(" " ANSI_GREEN "calc [A] [op] [B]" ANSI_RESET " - Arithmetic calculator (+, -, *, /, %%)\n"); printk(" " ANSI_GREEN "calc [A] [op] [B]" ANSI_RESET " - Arithmetic calculator (+, -, *, /, %%)\n");
printk(" " ANSI_GREEN "matrix" ANSI_RESET " - Digital rain screen animation\n"); printk(" " ANSI_GREEN "matrix" ANSI_RESET " - Digital rain screen animation\n");
printk(" " ANSI_GREEN "tty [1-4]" ANSI_RESET " - Switch virtual console (or Alt+F1-F4)\n");
printk(" " ANSI_GREEN "reboot / shutdown" ANSI_RESET " - Power management\n"); printk(" " ANSI_GREEN "reboot / shutdown" ANSI_RESET " - Power management\n");
printk(ANSI_BRIGHT_BLACK "Tip: Use TAB for autocomplete, Up/Down for command history.\n" ANSI_RESET); printk(ANSI_BRIGHT_BLACK "Tip: Use TAB for autocomplete, Up/Down for command history.\n" ANSI_RESET);
} }
@@ -240,7 +252,7 @@ static void shell_process(const char* cmd) {
} }
if (argc == 0) return; if (argc == 0) return;
// 1. LazyBox Applets // 1. LazyBox Applets (ls, cat, touch, mkdir, cd, pwd, nano, etc.)
if (lazybox_has_applet(argv[0])) { if (lazybox_has_applet(argv[0])) {
lazybox_run_applet(argv[0], argc, argv); lazybox_run_applet(argv[0], argc, argv);
return; return;
@@ -274,8 +286,10 @@ static void shell_process(const char* cmd) {
printk(ANSI_YELLOW "Rebooting...\n" ANSI_RESET); printk(ANSI_YELLOW "Rebooting...\n" ANSI_RESET);
pit_sleep(200); pit_sleep(200);
outb(0x64, 0xFE); outb(0x64, 0xFE);
} else if (strcmp(argv[0], "shutdown") == 0) { } else if (strcmp(argv[0], "shutdown") == 0 || strcmp(argv[0], "poweroff") == 0) {
printk(ANSI_YELLOW "System halted.\n" ANSI_RESET); printk(ANSI_YELLOW "System halted.\n" ANSI_RESET);
outw(0x604, 0x2000);
outw(0xB004, 0x2000);
while (1) hlt(); while (1) hlt();
} else { } else {
printk(ANSI_RED "Unknown command: %s. Type 'help' or 'lazybox'.\n" ANSI_RESET, argv[0]); printk(ANSI_RED "Unknown command: %s. Type 'help' or 'lazybox'.\n" ANSI_RESET, argv[0]);
@@ -287,14 +301,16 @@ void shell_run(void) {
size_t cmd_len = 0; size_t cmd_len = 0;
size_t cursor_pos = 0; size_t cursor_pos = 0;
int history_idx = -1; int history_idx = -1;
char prompt[128];
const char* prompt = ANSI_BRIGHT_GREEN "sub-os> " ANSI_RESET;
printk(ANSI_BRIGHT_CYAN "\nWelcome to SUB-OS Modular Monolithic Linux Core!\n" ANSI_RESET); printk(ANSI_BRIGHT_CYAN "\nWelcome to SUB-OS Modular Monolithic Linux Core!\n" ANSI_RESET);
printk("Type '" ANSI_YELLOW "help" ANSI_RESET "', '" ANSI_YELLOW "lazybox" ANSI_RESET "', or '" ANSI_YELLOW "neofetch" ANSI_RESET "' to begin.\n\n"); printk("Type '" ANSI_YELLOW "help" ANSI_RESET "', '" ANSI_YELLOW "lazybox" ANSI_RESET "', or '" ANSI_YELLOW "neofetch" ANSI_RESET "' to begin.\n\n");
while (true) { while (true) {
const char* cwd = vfs_getcwd();
snprintf(prompt, sizeof(prompt), ANSI_BRIGHT_GREEN "sub-os:" ANSI_BRIGHT_CYAN "%s" ANSI_BRIGHT_GREEN "> " ANSI_RESET, cwd);
printk("%s", prompt); printk("%s", prompt);
cmd_len = 0; cmd_len = 0;
cursor_pos = 0; cursor_pos = 0;
cmd_buffer[0] = '\0'; cmd_buffer[0] = '\0';
@@ -361,12 +377,12 @@ void shell_run(void) {
char c = (char)(key & 0xFF); char c = (char)(key & 0xFF);
if (c == 0x03) { if (c == 0x03) { // Ctrl+C
printk("^C\n%s", prompt); printk("^C\n%s", prompt);
cmd_len = 0; cursor_pos = 0; cmd_buffer[0] = '\0'; cmd_len = 0; cursor_pos = 0; cmd_buffer[0] = '\0';
continue; continue;
} }
if (c == 0x0C) { if (c == 0x0C) { // Ctrl+L
tty_clear(); tty_clear();
printk("%s%s", prompt, cmd_buffer); printk("%s%s", prompt, cmd_buffer);
continue; continue;