commit cd03ec0cd9a0978b73cb47771eeaef9fd9c6e7ab
parent e98815b06bc941a0b653656b6176c614e78fca0d
Author: erai <erai@omiltem.net>
Date: Thu, 12 Sep 2024 12:48:56 -0400
Less than minimal shell
Diffstat:
M | sh.c | | | 521 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
1 file changed, 314 insertions(+), 207 deletions(-)
diff --git a/sh.c b/sh.c
@@ -10,7 +10,6 @@ struct shell {
buf: *byte;
len: int;
cap: int;
- quoted: int;
status: int;
}
@@ -27,8 +26,6 @@ enum {
T_NOT,
T_LPAR,
T_RPAR,
- T_LBRA,
- T_RBRA,
}
feedc(s: *shell) {
@@ -197,26 +194,13 @@ feed(s: *shell) {
return;
}
- if s.c == '{' {
- feedc(s);
- s.tt = T_LBRA;
- return;
- }
-
if s.c == ')' {
feedc(s);
s.tt = T_RPAR;
return;
}
- if s.c == '}' {
- feedc(s);
- s.tt = T_RBRA;
- return;
- }
-
s.len = 0;
- s.quoted = 0;
s.tt = T_WORD;
loop {
@@ -234,14 +218,12 @@ feed(s: *shell) {
}
if s.c == '\\' {
- s.quoted = 1;
feedc(s);
feedw(s);
continue;
}
if s.c == '"' {
- s.quoted = 1;
feed_quote(s);
continue;
}
@@ -259,16 +241,52 @@ feed(s: *shell) {
}
}
+enum {
+ C_NOP,
+ C_IN,
+ C_OUT,
+ C_CMD,
+ C_ARG,
+ C_FOR,
+ C_SUBSHELL,
+ C_AND,
+ C_OR,
+ C_PIPE,
+}
+
+struct cmd {
+ kind: int;
+ word: *byte;
+ next: *cmd;
+ arg: *cmd;
+ redir: *cmd;
+ cond: *cmd;
+ body: *cmd;
+ other: *cmd;
+}
+
+mkcmd(s: *shell, kind: int): *cmd {
+ var c: *cmd;
+ c = alloc(s.a, sizeof(*c)):*cmd;
+ bzero(c:*byte, sizeof(*c));
+ c.kind = kind;
+ return c;
+}
+
// command = LF
// | and_or LF
-parse_command(s: *shell): int {
+parse_command(s: *shell): *cmd {
+ var c: *cmd;
+
if s.tt == T_LF {
+ c = mkcmd(s, C_NOP);
feed(s);
- return 1;
+ return c;
}
- if !parse_and_or(s) {
- return 0;
+ c = parse_and_or(s);
+ if !c {
+ return 0:*cmd;
}
if s.tt != T_LF {
@@ -276,127 +294,163 @@ parse_command(s: *shell): int {
}
feed(s);
- return 1;
+ return c;
}
// and_or = pipeline
// | pipeline '||' and_or
// | pipeine '&&' and_or
-parse_and_or(s: *shell): int {
- if !parse_pipeline(s) {
- return 0;
+parse_and_or(s: *shell): *cmd {
+ var c: *cmd;
+ var link: **cmd;
+ var p: *cmd;
+ var t: *cmd;
+
+ c = 0:*cmd;
+ link = &c;
+
+ c = parse_pipeline(s);
+ if !c {
+ return 0:*cmd;
}
loop {
- if s.tt != T_AND && s.tt != T_OR {
- return 1;
+ if s.tt == T_AND {
+ feed(s);
+ p = parse_pipeline(s);
+ if ! p {
+ die("expected pipeline");
+ }
+ t = mkcmd(s, C_AND);
+ t.cond = *link;
+ t.other = p;
+ *link = t;
+ link = &t.other;
}
- feed(s);
- if !parse_pipeline(s) {
- die("expected pipeline");
+ if s.tt == T_OR {
+ feed(s);
+ p = parse_pipeline(s);
+ if ! p {
+ die("expected pipeline");
+ }
+ t = mkcmd(s, C_OR);
+ t.cond = *link;
+ t.other = p;
+ *link = t;
+ link = &t.other;
}
+
+ return c;
}
}
// pipeline = '!' pipeline
// | compound
// | compound '|' pipeline
-parse_pipeline(s: *shell): int {
+parse_pipeline(s: *shell): *cmd {
+ var neg: int;
+ var c: *cmd;
+ var link: **cmd;
+ var p: *cmd;
+ var t: *cmd;
+
+ c = 0:*cmd;
+ link = &c;
+
if s.tt == T_NOT {
loop {
if s.tt != T_NOT {
break;
}
feed(s);
+ neg = neg + 1;
}
- if !parse_compound(s) {
+ c = parse_compound(s);
+ if !c {
die("expected compound");
}
} else {
- if !parse_compound(s) {
- return 0;
+ c = parse_compound(s);
+ if !c {
+ return 0:*cmd;
}
}
loop {
if s.tt != T_PIPE {
- return 1;
+ return c;
}
feed(s);
- if !parse_compound(s) {
+ p = parse_compound(s);
+ if !p {
die("expected compound");
}
- }
-}
-// compound = subshell | brace | if | for | while | simple
-parse_compound(s: *shell): int {
- if parse_subshell(s) {
- return 1;
- } else if parse_brace(s) {
- return 1;
- } else if parse_if(s) {
- return 1;
- } else if parse_for(s) {
- return 1;
- } else if parse_while(s) {
- return 1;
- } else if parse_simple(s) {
- return 1;
- } else {
- return 0;
+ t = mkcmd(s, C_PIPE);
+ t.cond = *link;
+ t.body = p;
+ *link = t;
+ link = &t.body;
}
}
-// subshell = '(' command_list ')'
-// | subshell redir_list
-parse_subshell(s: *shell): int {
- if s.tt != T_LPAR {
- return 0;
- }
- feed(s);
+// compound = subshell | if | for | simple
+parse_compound(s: *shell): *cmd {
+ var c: *cmd;
- loop {
- if !parse_command(s) {
- break;
- }
+ c = parse_subshell(s);
+ if c {
+ return c;
}
- if s.tt != T_RPAR {
- die("expected )");
+ c = parse_for(s);
+ if c {
+ return c;
}
- feed(s);
- parse_redir_list(s);
+ c = parse_simple(s);
+ if c {
+ return c;
+ }
- return 1;
+ return 0:*cmd;
}
-// brace = '{' command_list '}'
-// | brace redir_list
-parse_brace(s: *shell): int {
- if s.tt != T_LBRA {
- return 0;
+// subshell = '(' command_list ')'
+// | subshell redir_list
+parse_subshell(s: *shell): *cmd {
+ var body: *cmd;
+ var link: **cmd;
+ var t: *cmd;
+
+ body = 0:*cmd;
+ link = &body;
+
+ if s.tt != T_LPAR {
+ return 0:*cmd;
}
feed(s);
loop {
- if !parse_command(s) {
+ t = parse_command(s);
+ if !t {
break;
}
+ *link = t;
+ link = &t.next;
}
- if s.tt != T_RBRA {
- die("expected }");
+ if s.tt != T_RPAR {
+ die("expected )");
}
feed(s);
- parse_redir_list(s);
-
- return 1;
+ t = mkcmd(s, C_SUBSHELL);
+ t.redir = parse_redir_list(s);
+ return t;
}
parse_keyword(s: *shell, key: *byte): int {
@@ -404,10 +458,6 @@ parse_keyword(s: *shell, key: *byte): int {
return 0;
}
- if s.quoted {
- return 0;
- }
-
if s.len != strlen(key) {
return 0;
}
@@ -421,73 +471,29 @@ parse_keyword(s: *shell, key: *byte): int {
return 1;
}
-// if = 'if' command 'then' command_list else_fi
-// else_fi = 'elif' command 'then' command_list else_fi
-// | 'else' command_list 'fi'
-// | 'fi'
-parse_if(s: *shell): int {
- if !parse_keyword(s, "if") {
- return 0;
- }
-
- if !parse_command(s) {
- die("expected command");
- }
-
- if !parse_keyword(s, "then") {
- die("expected then");
- }
-
- loop {
- if parse_keyword(s, "fi") {
- break;
- }
-
- if parse_keyword(s, "elif") {
- if !parse_command(s) {
- die("expected command");
- }
-
- if !parse_keyword(s, "then") {
- die("expected then");
- }
- continue;
- }
-
- if parse_keyword(s, "else") {
- loop {
- if parse_keyword(s, "fi") {
- break;
- }
-
- if !parse_command(s) {
- die("expected command");
- }
- }
- break;
- }
-
- if !parse_command(s) {
- die("expected command");
- }
- }
-
- parse_redir_list(s);
-
- return 1;
-}
-
// for = 'for' NAME 'in' word_list LF 'do' command_list 'done'
// | for redir_list
-parse_for(s: *shell): int {
+parse_for(s: *shell): *cmd {
+ var arg: *cmd;
+ var arg_link: **cmd;
+ var body: *cmd;
+ var body_link: **cmd;
+ var t: *cmd;
+ var w: *byte;
+
+ arg = 0:*cmd;
+ arg_link = &arg;
+ body = 0:*cmd;
+ body_link = &body;
+
if !parse_keyword(s, "for") {
- return 0;
+ return 0:*cmd;
}
- if s.tt != T_WORD {
+ w = take(s);
+ if !w {
die("expceted name");
}
- feed(s);
if !parse_keyword(s, "in") {
die("expected in");
@@ -499,11 +505,14 @@ parse_for(s: *shell): int {
break;
}
- if s.tt != T_WORD {
+ w = take(s);
+ if !w {
die("expected word");
}
-
- feed(s);
+ t = mkcmd(s, C_ARG);
+ t.word = w;
+ *arg_link = t;
+ arg_link = &t.next;
}
if !parse_keyword(s, "do") {
@@ -515,99 +524,196 @@ parse_for(s: *shell): int {
break;
}
- if !parse_command(s) {
- die("expected command");
- }
- }
-
- parse_redir_list(s);
-
- return 1;
-}
-
-// while = 'while' command 'do' command_list 'done'
-// | while redir_list
-parse_while(s: *shell): int {
- if !parse_keyword(s, "while") {
- return 0;
- }
-
- if !parse_command(s) {
- die("expected command");
- }
-
- if !parse_keyword(s, "do") {
- die("expceted do");
- }
-
- loop {
- if !parse_keyword(s, "done") {
- break;
- }
-
- if !parse_command(s) {
+ t = parse_command(s);
+ if !t {
die("expected command");
}
+ *body_link = t;
+ body_link = &t.next;
}
- parse_redir_list(s);
-
- return 1;
+ t = mkcmd(s, C_FOR);
+ t.arg = arg;
+ t.body = body;
+ t.redir = parse_redir_list(s);
+ return t;
}
// simple = word
// | redir
// | word simple
// | redir simple
-parse_simple(s: *shell): int {
- if s.tt == T_WORD {
- feed(s);
- } else if !parse_redir(s) {
- return 0;
+parse_simple(s: *shell): *cmd {
+ var arg: *cmd;
+ var arg_link: **cmd;
+ var redir: *cmd;
+ var redir_link: **cmd;
+ var c: *cmd;
+ var t: *cmd;
+ var w: *byte;
+
+ arg = 0:*cmd;
+ arg_link = &arg;
+ redir = 0:*cmd;
+ redir_link = &redir;
+
+ w = take(s);
+ if w {
+ t = mkcmd(s, C_ARG);
+ t.word = w;
+ *arg_link = t;
+ arg_link = &t.next;
+ } else {
+ t = parse_redir(s);
+ if t {
+ *redir_link = t;
+ redir_link = &t.next;
+ } else {
+ return 0: *cmd;
+ }
}
loop {
- if s.tt == T_WORD {
- feed(s);
- } else if !parse_redir(s) {
- return 1;
+ w = take(s);
+ if w {
+ t = mkcmd(s, C_ARG);
+ t.word = w;
+ *arg_link = t;
+ arg_link = &t.next;
+ continue;
+ }
+
+ t = parse_redir(s);
+ if t {
+ *redir_link = t;
+ redir_link = &t.next;
+ continue;
}
+
+ t = mkcmd(s, C_CMD);
+ t.arg = arg;
+ t.redir = redir;
+ return t;
}
}
// redir_list =
// | redir redir_list
-parse_redir_list(s: *shell): int {
+parse_redir_list(s: *shell): *cmd {
+ var c: *cmd;
+ var t: *cmd;
+ var link: **cmd;
+
+ c = 0:*cmd;
+ link = &c;
+
loop {
- if !parse_redir(s) {
- return 1;
+ t = parse_redir(s);
+ if !t {
+ return c;
}
+ *link = t;
+ link = &t.next;
}
}
+take(s: *shell): *byte {
+ var w: *byte;
+
+ if s.tt != T_WORD {
+ return 0: *byte;
+ }
+
+ w = alloc(s.a, s.len + 1);
+ memcpy(w, s.buf, s.len);
+ w[s.len] = 0:byte;
+ feed(s);
+
+ return w;
+}
+
// redir = '>' word
// | '<' word
-parse_redir(s: *shell): int {
- if s.tt != T_IN && s.tt != T_OUT {
- return 0;
+parse_redir(s: *shell): *cmd {
+ var c: *cmd;
+ var w: *byte;
+
+ if s.tt == T_IN {
+ feed(s);
+ c = mkcmd(s, C_IN);
+ w = take(s);
+ if !w {
+ die("expected word");
+ }
+ c.word = w;
+ return c;
}
- feed(s);
- if s.tt != T_WORD {
- die("expected word");
+ if s.tt == T_OUT {
+ feed(s);
+ c = mkcmd(s, C_OUT);
+ w = take(s);
+ if !w {
+ die("expected word");
+ }
+ c.word = w;
+ return c;
}
- feed(s);
- return 1;
+ return 0:*cmd;
}
-execute_command(s: *shell) {
- die("noimpl");
+execute_command(s: *shell, c: *cmd) {
+ if c.kind == C_NOP {
+ return;
+ } else if c.kind == C_CMD {
+ } else if c.kind == C_FOR {
+ c = c.body;
+ loop {
+ if !c {
+ break;
+ }
+ execute_command(s, c);
+ c = c.next;
+ }
+ } else if c.kind == C_SUBSHELL {
+ c = c.body;
+ loop {
+ if !c {
+ break;
+ }
+ execute_command(s, c);
+ c = c.next;
+ }
+ } else if c.kind == C_AND {
+ execute_command(s, c.cond);
+ if s.status == 0 {
+ execute_command(s, c.other);
+ }
+ } else if c.kind == C_OR {
+ execute_command(s, c.cond);
+ if s.status != 0 {
+ execute_command(s, c.other);
+ }
+ } else if c.kind == C_PIPE {
+ execute_command(s, c.cond);
+ c = c.body;
+ loop {
+ if !c {
+ break;
+ }
+ execute_command(s, c);
+ c = c.body;
+ }
+ } else {
+ die("invalid");
+ }
}
main(argc: int, argv: **byte, envp: **byte) {
var a: alloc;
var s: shell;
+ var c: *cmd;
var i: int;
var fd: int;
setup_alloc(&a);
@@ -657,10 +763,11 @@ main(argc: int, argv: **byte, envp: **byte) {
exit(s.status);
}
- if !parse_command(&s) {
+ c = parse_command(&s);
+ if !c {
exit(127);
}
- execute_command(&s);
+ execute_command(&s, c);
}
}