summaryrefslogtreecommitdiff
path: root/lume-legacy/src/user/sh.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:31:46 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:31:46 +0800
commite90b72ec3082588b296956730bd2930cf8d5d671 (patch)
tree36ff6916af9b4176ea5a9662219d11e721f0b691 /lume-legacy/src/user/sh.cc
parent1767d390fbf3d0c65389de46caf0b6d43ef3b6ad (diff)
downloadlume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.gz
lume-e90b72ec3082588b296956730bd2930cf8d5d671.tar.xz
refactor: promote new architecture to repository root
Archive the deprecated implementation under lume-legacy, make the new kernel the default root-level build, and update repository entry points without changing kernel behavior.
Diffstat (limited to 'lume-legacy/src/user/sh.cc')
-rw-r--r--lume-legacy/src/user/sh.cc423
1 files changed, 423 insertions, 0 deletions
diff --git a/lume-legacy/src/user/sh.cc b/lume-legacy/src/user/sh.cc
new file mode 100644
index 0000000..47aad9e
--- /dev/null
+++ b/lume-legacy/src/user/sh.cc
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 Richard QIn
+ */
+#include "ulib/stdio.h"
+#include "ulib/stdlib.h"
+#include "ulib/string.h"
+#include "user/user.h"
+#include "common/fcntl.h"
+#include "drivers/uart_color.h"
+
+constexpr int MAX_ARGS = 10;
+const char *WHITESPACE = " \t\r\n\v";
+const char *SYMBOLS = "<|>&;()";
+
+void panic(const char *s)
+{
+ printf("shell panic: %s\n", s);
+ exit(1);
+}
+
+int fork_or_panic()
+{
+ int pid = fork();
+ if (pid == -1)
+ panic("fork");
+ return pid;
+}
+
+struct Command
+{
+ virtual ~Command() = default;
+
+ virtual void run() = 0;
+};
+
+struct ExecCommand : public Command
+{
+ char *argv[MAX_ARGS];
+
+ ExecCommand()
+ {
+ memset(argv, 0, sizeof(argv));
+ }
+
+ void run() override
+ {
+ if (argv[0] == nullptr)
+ exit(1);
+
+ exec(argv[0], argv);
+
+ printf("exec %s failed\n", argv[0]);
+ exit(1);
+ }
+};
+
+struct RedirCommand : public Command
+{
+ Command *sub_cmd;
+ char *file;
+ char *efile; // file name end pointer (used by parser)
+ int mode;
+ int fd;
+
+ RedirCommand(Command *c, char *f, char *ef, int m, int fd_in)
+ : sub_cmd(c), file(f), efile(ef), mode(m), fd(fd_in) {}
+
+ ~RedirCommand() { delete sub_cmd; }
+
+ void run() override
+ {
+ close(fd);
+
+ int newfd = open(file, mode);
+
+ if (newfd < 0)
+ {
+ printf("open %s failed\n", file);
+ exit(1);
+ }
+
+ if (newfd != fd)
+ {
+
+ if (dup(newfd) != fd)
+ {
+ printf("Redirection failed: could not move fd %d to %d\n", newfd, fd);
+ exit(1);
+ }
+ close(newfd);
+ }
+
+ sub_cmd->run();
+ }
+};
+
+struct PipeCommand : public Command
+{
+ Command *left;
+ Command *right;
+
+ PipeCommand(Command *l, Command *r) : left(l), right(r) {}
+ ~PipeCommand()
+ {
+ delete left;
+ delete right;
+ }
+
+ void run() override
+ {
+ int p[2];
+ if (pipe(p) < 0)
+ panic("pipe");
+
+ if (fork_or_panic() == 0)
+ {
+ // Left child: write to pipe
+ close(1); // close stdout
+ dup(p[1]); // dup write-end to stdout
+ close(p[0]);
+ close(p[1]);
+ left->run();
+ }
+
+ if (fork_or_panic() == 0)
+ {
+ // Right child: read from pipe
+ close(0); // close stdin
+ dup(p[0]); // dup read-end to stdin
+ close(p[0]);
+ close(p[1]);
+ right->run();
+ }
+
+ close(p[0]);
+ close(p[1]);
+ wait(0);
+ wait(0);
+ exit(0);
+ }
+};
+
+struct ListCommand : public Command
+{
+ Command *left;
+ Command *right;
+
+ ListCommand(Command *l, Command *r) : left(l), right(r) {}
+ ~ListCommand()
+ {
+ delete left;
+ delete right;
+ }
+
+ void run() override
+ {
+ if (fork_or_panic() == 0)
+ {
+ left->run();
+ }
+ wait(0);
+ right->run();
+ }
+};
+
+struct BackCommand : public Command
+{
+ Command *sub_cmd;
+
+ BackCommand(Command *c) : sub_cmd(c) {}
+ ~BackCommand() { delete sub_cmd; }
+
+ void run() override
+ {
+ if (fork_or_panic() == 0)
+ {
+ sub_cmd->run();
+ }
+ // Parent doesn't wait
+ exit(0);
+ }
+};
+
+class Parser
+{
+public:
+ static Command *parse(char *s)
+ {
+ char *es = s + strlen(s);
+ Command *cmd = parse_line(&s, es);
+ peek(&s, es, "");
+ if (s != es)
+ {
+ printf("leftovers: %s\n", s);
+ // panic("syntax"); // strict syntax check
+ }
+ terminate_strings(cmd);
+ return cmd;
+ }
+
+private:
+ // Helper: NUL-terminate strings found during parsing
+ static void terminate_strings(Command *cmd)
+ {
+ if (!cmd)
+ return;
+ }
+
+ static char get_token(char **ps, char *es, char **q, char **eq)
+ {
+ char *s;
+ int ret;
+
+ s = *ps;
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ if (q)
+ *q = s;
+ ret = *s;
+ switch (*s)
+ {
+ case 0:
+ break;
+ case '|':
+ case '(':
+ case ')':
+ case ';':
+ case '&':
+ case '<':
+ s++;
+ break;
+ case '>':
+ s++;
+ if (*s == '>')
+ {
+ ret = '+';
+ s++;
+ }
+ break;
+ default:
+ ret = 'a';
+ while (s < es && !strchr(WHITESPACE, *s) && !strchr(SYMBOLS, *s))
+ s++;
+ break;
+ }
+ if (eq)
+ *eq = s;
+
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ *ps = s;
+ return ret;
+ }
+
+ static int peek(char **ps, char *es, const char *toks)
+ {
+ char *s = *ps;
+ while (s < es && strchr(WHITESPACE, *s))
+ s++;
+ *ps = s;
+ return *s && strchr(toks, *s);
+ }
+
+ // Recursive Descent Parsers
+
+ static Command *parse_line(char **ps, char *es)
+ {
+ Command *cmd = parse_pipe(ps, es);
+ while (peek(ps, es, "&"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new BackCommand(cmd);
+ }
+ if (peek(ps, es, ";"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new ListCommand(cmd, parse_line(ps, es));
+ }
+ return cmd;
+ }
+
+ static Command *parse_pipe(char **ps, char *es)
+ {
+ Command *cmd = parse_exec(ps, es);
+ if (peek(ps, es, "|"))
+ {
+ get_token(ps, es, 0, 0);
+ cmd = new PipeCommand(cmd, parse_pipe(ps, es));
+ }
+ return cmd;
+ }
+
+ static Command *parse_exec(char **ps, char *es)
+ {
+ char *q, *eq;
+ int tok, argc;
+ ExecCommand *cmd = new ExecCommand();
+ Command *ret = cmd;
+
+ argc = 0;
+ ret = parse_redirs(ret, ps, es);
+ while (!peek(ps, es, "|)&;"))
+ {
+ if ((tok = get_token(ps, es, &q, &eq)) == 0)
+ break;
+ if (tok != 'a')
+ panic("syntax");
+
+ cmd->argv[argc] = q;
+ *eq = 0; // Temp
+
+ argc++;
+ if (argc >= MAX_ARGS)
+ panic("too many args");
+ ret = parse_redirs(ret, ps, es);
+ }
+ cmd->argv[argc] = 0;
+ return ret;
+ }
+
+ static Command *parse_redirs(Command *cmd, char **ps, char *es)
+ {
+ int tok;
+ char *q, *eq;
+
+ while (peek(ps, es, "<>"))
+ {
+ tok = get_token(ps, es, 0, 0);
+ if (get_token(ps, es, &q, &eq) != 'a')
+ panic("missing file for redirection");
+
+ *eq = 0; // Null terminate filename immediately
+
+ switch (tok)
+ {
+ case '<':
+ cmd = new RedirCommand(cmd, q, eq, O_RDONLY, 0);
+ break;
+ case '>':
+ cmd = new RedirCommand(cmd, q, eq, O_WRONLY | O_CREATE | O_TRUNC, 1);
+ break;
+ case '+': // >>
+ cmd = new RedirCommand(cmd, q, eq, O_WRONLY | O_CREATE, 1); // Append not fully supported in simple OS yet?
+ break;
+ }
+ }
+ return cmd;
+ }
+};
+
+int getcmd(char *buf, int nbuf)
+{
+ write(1, "$ ", 2);
+ memset(buf, 0, nbuf);
+
+ int n = read(0, buf, nbuf - 1);
+
+ if (n < 0)
+ return -1;
+
+ buf[n] = 0;
+ return 0;
+}
+
+int main()
+{
+ static char buf[100];
+
+
+ printf(ANSI_GREEN "\n[Lume Shell] Welcome! \n" ANSI_RESET);
+ printf(ANSI_YELLOW "[Lume Shell] Start your journey of exploration!" ANSI_RESET);
+ printf("\n");
+
+ while (getcmd(buf, sizeof(buf)) >= 0)
+ {
+ int len = strlen(buf);
+ if (len > 0 && buf[len - 1] == '\n')
+ {
+ buf[len - 1] = 0;
+ }
+
+ if (buf[0] == 'c' && buf[1] == 'd' && (buf[2] == ' ' || buf[2] == '\n' || buf[2] == 0))
+ {
+ if (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = 0;
+
+ char *path = buf + 3;
+ if (buf[2] == '\n' || buf[2] == 0)
+ {
+ printf("cd: argument missing\n");
+ continue;
+ }
+
+ if (chdir(path) < 0)
+ {
+ printf("cannot cd %s\n", path);
+ }
+ continue;
+ }
+ if (strcmp(buf, "shutdown") == 0 || strcmp(buf, "poweroff") == 0)
+ {
+ printf(ANSI_BLUE "System shutting down..." ANSI_RESET);
+ printf("\n");
+ shutdown();
+
+ continue;
+ }
+
+ if (fork_or_panic() == 0)
+ {
+ Command *cmd = Parser::parse(buf);
+ if (cmd)
+ {
+ cmd->run();
+ delete cmd;
+ }
+ exit(0);
+ }
+ wait(0);
+ }
+ return 0;
+} \ No newline at end of file