feat(programs/shell): Shell improvements (inc. cwd support)

This commit is contained in:
2026-02-18 15:21:06 +01:00
parent 24af60d669
commit 03abe9a910
5 changed files with 163 additions and 26 deletions
Binary file not shown.
+17 -6
View File
@@ -18,17 +18,28 @@
.SS man <topic>
Open a manual page in the fullscreen pager. See man(1).
.SS ls
List all files on the ramdisk (drive 0:/).
.SS ls [dir]
List files in the current directory, or in the specified
directory. When a directory argument is given, the output
shows only the entries inside that directory with the
directory prefix stripped.
Examples: ls, ls man
.SS cd [dir]
Change the working directory. With no argument or with /,
returns to the root (0:/). Use cd .. to go up one level.
The shell prompt reflects the current directory.
Examples: cd man, cd .., cd
.SS cat <file>
Print the contents of a ramdisk file to the terminal.
Print the contents of a file to the terminal. The file
path is resolved relative to the current directory.
Example: cat hello.elf
.SS run <file>
Spawn a new process from an ELF binary on the ramdisk and wait
for it to exit. The shell blocks until the child process
terminates.
Spawn a new process from an ELF binary and wait for it to
exit. The file path is resolved relative to the current
directory. The shell blocks until the child process terminates.
Example: run hello.elf
.SS ping <ip>
Binary file not shown.
+146 -20
View File
@@ -27,6 +27,41 @@ static const char* skip_spaces(const char* s) {
return s;
}
static int slen(const char* s) {
int n = 0;
while (s[n]) n++;
return n;
}
// Current working directory (relative to 0:/).
// "" = root, "man" = 0:/man, "man/sub" = 0:/man/sub
static char cwd[128] = "";
// Build full VFS path: "0:/" + cwd + "/" + name
static void resolve_path(const char* name, char* out, int outMax) {
int i = 0;
out[i++] = '0'; out[i++] = ':'; out[i++] = '/';
if (cwd[0]) {
int j = 0;
while (cwd[j] && i < outMax - 2) out[i++] = cwd[j++];
out[i++] = '/';
}
int j = 0;
while (name[j] && i < outMax - 1) out[i++] = name[j++];
out[i] = '\0';
}
// Build VFS directory path: "0:/" or "0:/<dir>"
static void build_dir_path(const char* dir, char* out, int outMax) {
int i = 0;
out[i++] = '0'; out[i++] = ':'; out[i++] = '/';
if (dir && dir[0]) {
int j = 0;
while (dir[j] && i < outMax - 1) out[i++] = dir[j++];
}
out[i] = '\0';
}
static void print_int(uint64_t n) {
if (n == 0) {
zenith::putchar('0');
@@ -44,7 +79,9 @@ static void print_int(uint64_t n) {
}
static void prompt() {
zenith::print("% ");
zenith::print("0:/");
if (cwd[0]) zenith::print(cwd);
zenith::print("> ");
}
static void cmd_help() {
@@ -52,7 +89,8 @@ static void cmd_help() {
zenith::print(" help Show this help message\n");
zenith::print(" info Show system information\n");
zenith::print(" man <topic> View manual pages\n");
zenith::print(" ls List ramdisk files\n");
zenith::print(" ls [dir] List files in directory\n");
zenith::print(" cd [dir] Change working directory\n");
zenith::print(" cat <file> Display file contents\n");
zenith::print(" run <file> Spawn a new process from an ELF file\n");
zenith::print(" ping <ip> Send ICMP echo requests\n");
@@ -73,16 +111,53 @@ static void cmd_info() {
zenith::putchar('\n');
}
static void cmd_ls() {
static void cmd_ls(const char* arg) {
arg = skip_spaces(arg);
// Build the target directory (relative path from root)
char dir[128];
if (*arg) {
// ls <dir> — combine cwd and arg
if (cwd[0]) {
int i = 0, j = 0;
while (cwd[j] && i < 126) dir[i++] = cwd[j++];
dir[i++] = '/';
j = 0;
while (arg[j] && i < 126) dir[i++] = arg[j++];
dir[i] = '\0';
} else {
int i = 0;
while (arg[i] && i < 126) { dir[i] = arg[i]; i++; }
dir[i] = '\0';
}
} else {
// ls with no arg — use cwd
int i = 0;
while (cwd[i] && i < 126) { dir[i] = cwd[i]; i++; }
dir[i] = '\0';
}
char path[128];
build_dir_path(dir, path, sizeof(path));
const char* entries[64];
int count = zenith::readdir("0:/", entries, 64);
int count = zenith::readdir(path, entries, 64);
if (count <= 0) {
zenith::print("(empty)\n");
return;
}
// Prefix to strip: "dir/" (if dir is non-empty)
int prefixLen = 0;
if (dir[0]) prefixLen = slen(dir) + 1;
for (int i = 0; i < count; i++) {
zenith::print(" ");
zenith::print(entries[i]);
if (prefixLen > 0 && starts_with(entries[i], dir)) {
zenith::print(entries[i] + prefixLen);
} else {
zenith::print(entries[i]);
}
zenith::putchar('\n');
}
}
@@ -94,14 +169,8 @@ static void cmd_cat(const char* arg) {
return;
}
// Build path "0:/<filename>"
char path[128];
const char* prefix = "0:/";
int i = 0;
while (prefix[i]) { path[i] = prefix[i]; i++; }
int j = 0;
while (arg[j] && i < 126) { path[i++] = arg[j++]; }
path[i] = '\0';
resolve_path(arg, path, sizeof(path));
int handle = zenith::open(path);
if (handle < 0) {
@@ -232,14 +301,8 @@ static void cmd_run(const char* arg) {
return;
}
// Build path "0:/<filename>"
char path[128];
const char* prefix = "0:/";
int i = 0;
while (prefix[i]) { path[i] = prefix[i]; i++; }
int j = 0;
while (arg[j] && i < 126) { path[i++] = arg[j++]; }
path[i] = '\0';
resolve_path(arg, path, sizeof(path));
int pid = zenith::spawn(path);
if (pid < 0) {
@@ -251,6 +314,63 @@ static void cmd_run(const char* arg) {
}
}
static void cmd_cd(const char* arg) {
arg = skip_spaces(arg);
// cd or cd / → go to root
if (*arg == '\0' || streq(arg, "/")) {
cwd[0] = '\0';
return;
}
// cd .. → go up one level
if (streq(arg, "..")) {
int len = slen(cwd);
int last = -1;
for (int i = 0; i < len; i++) {
if (cwd[i] == '/') last = i;
}
if (last >= 0) {
cwd[last] = '\0';
} else {
cwd[0] = '\0';
}
return;
}
// Build target directory path
char target[128];
if (cwd[0]) {
int i = 0, j = 0;
while (cwd[j] && i < 126) target[i++] = cwd[j++];
target[i++] = '/';
j = 0;
while (arg[j] && i < 126) target[i++] = arg[j++];
target[i] = '\0';
} else {
int i = 0;
while (arg[i] && i < 126) { target[i] = arg[i]; i++; }
target[i] = '\0';
}
// Validate: try readdir on the target
char path[128];
build_dir_path(target, path, sizeof(path));
const char* entries[1];
int count = zenith::readdir(path, entries, 1);
if (count < 0) {
zenith::print("cd: no such directory: ");
zenith::print(arg);
zenith::putchar('\n');
return;
}
// Set cwd
int i = 0;
while (target[i] && i < 126) { cwd[i] = target[i]; i++; }
cwd[i] = '\0';
}
static void cmd_man(const char* arg) {
arg = skip_spaces(arg);
if (*arg == '\0') {
@@ -282,8 +402,14 @@ static void process_command(const char* line) {
cmd_help();
} else if (streq(line, "info")) {
cmd_info();
} else if (starts_with(line, "ls ")) {
cmd_ls(line + 3);
} else if (streq(line, "ls")) {
cmd_ls();
cmd_ls("");
} else if (starts_with(line, "cd ")) {
cmd_cd(line + 3);
} else if (streq(line, "cd")) {
cmd_cd("");
} else if (starts_with(line, "man ")) {
cmd_man(line + 4);
} else if (streq(line, "man")) {