/** * @file sh.c * @brief ClaudeOS shell. * * A simple interactive shell that reads commands from stdin, * supports built-in commands (cd, exit, help, env), and * executes external programs via fork+exec. */ #include "syscalls.h" /** Maximum command line length. */ #define CMD_MAX 256 /** Read a line from stdin with echo and basic line editing. * Returns length of the line (excluding newline). */ static int readline(char *buf, int maxlen) { int pos = 0; while (pos < maxlen - 1) { char c; int n = read(0, &c, 1); if (n <= 0) { /* No data yet: yield CPU and retry */ yield(); continue; } if (c == '\n' || c == '\r') { putchar('\n'); break; } else if (c == '\b' || c == 127) { /* Backspace */ if (pos > 0) { pos--; puts("\b \b"); /* Move back, overwrite with space, move back */ } } else if (c >= 32) { /* Printable character */ buf[pos++] = c; putchar(c); } } buf[pos] = '\0'; return pos; } /** Skip leading whitespace, return pointer to first non-space. */ static char *skip_spaces(char *s) { while (*s == ' ' || *s == '\t') s++; return s; } /** Find the next space or end of string. */ static char *find_space(char *s) { while (*s && *s != ' ' && *s != '\t') s++; return s; } /** Built-in: cd */ static void builtin_cd(char *arg) { if (!arg || !*arg) { arg = "/"; } /* Update CWD environment variable */ setenv("CWD", arg); /* Verify it was set */ char cwd[128]; if (getenv("CWD", cwd, sizeof(cwd)) >= 0) { puts("cd: "); puts(cwd); putchar('\n'); } } /** Built-in: env - print all known env vars. */ static void builtin_env(void) { char buf[128]; if (getenv("CWD", buf, sizeof(buf)) >= 0) { puts("CWD="); puts(buf); putchar('\n'); } if (getenv("PATH", buf, sizeof(buf)) >= 0) { puts("PATH="); puts(buf); putchar('\n'); } if (getenv("TEST", buf, sizeof(buf)) >= 0) { puts("TEST="); puts(buf); putchar('\n'); } } /** Built-in: help */ static void builtin_help(void) { puts("ClaudeOS Shell\n"); puts("Built-in commands:\n"); puts(" cd - change working directory\n"); puts(" env - show environment variables\n"); puts(" help - show this message\n"); puts(" exit - exit the shell\n"); puts("External commands are loaded from initrd.\n"); } /** Execute an external command via fork+exec. */ static void run_command(const char *cmd) { int32_t pid = fork(); if (pid < 0) { puts("sh: fork failed\n"); return; } if (pid == 0) { /* Child: exec the command */ int32_t ret = exec(cmd); if (ret < 0) { puts("sh: "); puts(cmd); puts(": not found\n"); exit(127); } /* exec doesn't return on success */ exit(1); } /* Parent: wait for child */ waitpid(pid); } int main(void) { puts("ClaudeOS Shell v0.1\n"); puts("Type 'help' for available commands.\n\n"); char cmd[CMD_MAX]; for (;;) { /* Print prompt with CWD */ char cwd[128]; if (getenv("CWD", cwd, sizeof(cwd)) < 0) { strcpy(cwd, "/"); } puts(cwd); puts("$ "); /* Read command */ int len = readline(cmd, CMD_MAX); if (len == 0) continue; /* Parse command and arguments */ char *line = skip_spaces(cmd); if (*line == '\0') continue; char *arg_start = find_space(line); char *arg = (char *)0; if (*arg_start) { *arg_start = '\0'; arg = skip_spaces(arg_start + 1); if (*arg == '\0') arg = (char *)0; } /* Check built-in commands */ if (strcmp(line, "exit") == 0) { puts("Goodbye!\n"); exit(0); } else if (strcmp(line, "cd") == 0) { builtin_cd(arg); } else if (strcmp(line, "env") == 0) { builtin_env(); } else if (strcmp(line, "help") == 0) { builtin_help(); } else { /* External command */ run_command(line); } } return 0; }