ESH

Executive Shell
git clone http://git.omkov.net/ESH
Log | Tree | Refs | README | Download

AuthorJakob Wakeling <[email protected]>
Date2023-12-28 00:35:12
Commit1e336bf413a12ed3c021cd24e982c973ff68a00e
Parentda120bbed651b0731eaba4e5ca3549c977c56067

Add pipes and redirection

Diffstat

M src/exec.c | 47 +++++++++++++++++++++++++++++++++++++++++++----
M src/lex.c | 10 +++++++++-
M src/lex.h | 1 +
M src/parse.c | 36 +++++++++++++++++++++++++++++++++---
M src/parse.h | 9 ++++++---

5 files changed, 92 insertions, 11 deletions

diff --git a/src/exec.c b/src/exec.c
index b29b8fe..a6e144c 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -6,21 +6,25 @@
 #include "util/log.h"
 #include "util/util.h"
 
+#include <fcntl.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 s32 _ret;
 
 static s32 execute_comm(ast *a, int fdi, int fd[2]);
+static s32 execute_pipe(ast *a, int fdi, int fd[2]);
 
 /* Execute a program. */
 s32 execute(ast *a, int fdi, int fd[2]) {
 	switch (a->k) {
 	case AK_COMM: {} return execute_comm(a, fdi, fd);
+	case AK_PIPE: {} return execute_pipe(a, fdi, fd);
 	default: { log_warn("Unhandled AST kind \"%s\"", ast_ks[a->k]); } return -1;
 	}
 }
@@ -32,9 +36,20 @@ static s32 execute_comm(ast *a, int fdi, int fd[2]) {
 
 	/* TODO handle builtins */
 
-	pid_t pid; int status;
+	pid_t pid; int status, rin = -1, rout = -1, rerr = -1;
 
-	/* TODO handle redirects */
+	/* Open redirect files. */
+	if (a->rin.s && (rin = open(a->rin.s, O_RDONLY)) == -1) {
+		log_warn("%s: %s", a->rin.s, strerror(errno)); return -1;
+	}
+	if (a->rout.s && (rout = open(a->rout.s, O_WRONLY | O_CREAT | (a->rout.app ? O_APPEND : O_TRUNC), 0666)) == -1) {
+		if (rin != -1) { close(rin); }
+		log_warn("%s: %s", a->rout.s, strerror(errno)); return -1;
+	}
+	if (a->rerr.s && (rerr = open(a->rerr.s, O_WRONLY | O_CREAT | (a->rerr.app ? O_APPEND : O_TRUNC), 0666)) == -1) {
+		if (rin != -1) { close(rin); } if (rout != -1) { close(rout); }
+		log_warn("%s: %s", a->rerr.s, strerror(errno)); return -1;
+	}
 
 	char **av = a->c.a;
 
@@ -42,7 +57,14 @@ static s32 execute_comm(ast *a, int fdi, int fd[2]) {
 	if ((pid = fork()) == 0) {
 		signal(SIGINT, SIG_DFL);
 
-		dup2(fdi, STDIN_FILENO);
+		if (rin != -1) { dup2(rin, STDIN_FILENO); }
+		else { dup2(fdi, STDIN_FILENO); }
+		
+		if (rout != -1) { dup2(rout, STDOUT_FILENO); }
+		else if (fd) { dup2(fd[1], STDOUT_FILENO); }
+		if (fd && fd[0]) { close(fd[0]); }
+		
+		if (rerr != -1) { dup2(rerr, STDERR_FILENO); }
 
 		if (execvp((char *)av[0], (char **)av) == -1) {
 			if (errno == ENOENT) { log_warn("%s: Command not found", av[0]); }
@@ -50,7 +72,24 @@ static s32 execute_comm(ast *a, int fdi, int fd[2]) {
 		}
 	}
 	else if (pid == -1) { perror((char *)av[0]); }
-	else { waitpid(pid, &status, 0); }
+	else {
+		if (rout == -1 && fd) {
+			if (fdi != STDIN_FILENO) { close(fdi); } close(fd[1]);
+		}
+		else {
+			if (fd) { close(fd[1]); }
+			waitpid(pid, &status, 0);
+		}
+	}
+	
+	if (rin != -1) { close(rin); }
+	if (rout != -1) { close(rout); }
+	if (rerr != -1) { close(rerr); }
 
 	return WEXITSTATUS(status);
 }
+
+static s32 execute_pipe(ast *a, int fdi, int fd[2]) {
+	int pd[2]; if (pipe(pd) == -1) { log_warn("PIPE: %s", strerror(errno)); return -1; }
+	execute(a->lc, fdi, pd); return execute(a->rc, pd[0], fd);
+}
diff --git a/src/lex.c b/src/lex.c
index 98a87b9..44d54f1 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -11,7 +11,8 @@
 #include <string.h>
 
 char *tok_ks[] = {
-	"VOID", "EOF", "WORD", "END",
+	"TK_VOID", "TK_EOF", "TK_WORD", "TK_END",
+	"TK_PIPE", "TK_RIN", "TK_ROUT", "TK_RAPP",
 };
 
 #define is_space(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
@@ -47,6 +48,13 @@ tok lex_next(lex *l) {
 	}
 
 	switch (P[0]) {
+	case '|': { T.k = TK_PIPE; P += 1; } break;
+	case '<': { T.k = TK_RIN;  P += 1; } break;
+	case '>': switch (P[1]) {
+		default:  { T.k = TK_ROUT; P += 1; } break;
+		case '>': { T.k = TK_RAPP; P += 2; } break;
+	} break;
+	
 	/* Handle words, TODO review quotes and substitutions */
 	default: {
 		stack s = stack_init(sizeof (char), NULL);
diff --git a/src/lex.h b/src/lex.h
index 7ea5454..77226ad 100644
--- a/src/lex.h
+++ b/src/lex.h
@@ -9,6 +9,7 @@
 /* Remember to update tok_ks in lex.c */
 typedef enum {
 	TK_VOID, TK_EOF, TK_WORD, TK_END,
+	TK_PIPE, TK_RIN, TK_ROUT, TK_RAPP,
 } tok_k;
 
 /* k : Kind, s : String */
diff --git a/src/parse.c b/src/parse.c
index 05dd994..771cee3 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -10,7 +10,7 @@
 #include <stdlib.h>
 
 char *ast_ks[] = {
-	"VOID", "COMM",
+	"AK_VOID", "AK_COMM", "AK_PIPE",
 };
 
 /* Initialise an AST node. */
@@ -22,12 +22,14 @@ extern ast *ast_init(ast_k kind) {
 /* Uninitialise an AST node and its children. */
 extern void ast_free(ast *a) {
 	if (a == NULL) { return; }
-	free(a);
+	ast_free(a->lc); ast_free(a->rc); stack_free(&a->c);
+	free(a->rin.s); free(a->rout.s); free(a->rerr.s); free(a);
 }
 
 #define T (l->t) /* Current Token */
 
 static ast *parse_comm(lex *l);
+static ast *parse_pipe(lex *l, ast *lhs);
 
 /* Parse a program. */
 extern ast *parse(lex *l) {
@@ -38,7 +40,12 @@ extern ast *parse(lex *l) {
 		log_warn("Unexpected \"%s\", was expecting a word", tok_ks[T.k]); return NULL;
 	}
 
-	return parse_comm(l);
+	ast *lhs = parse_comm(l);
+	
+	for (;;) switch (lex_next(l).k) {
+	case TK_PIPE: { lhs = parse_pipe(l, lhs); } continue;
+	default: {} return lhs;
+	}
 }
 
 /* Parse a command statement. */
@@ -53,10 +60,28 @@ static ast *parse_comm(lex *l) {
 
 	for (;;) switch (lex_peek(l).k) {
 	case TK_WORD: { stack_push(&a->c, lex_next(l).s); } continue;
+	case TK_RIN: {
+		if (lex_next(l), T.k == TK_WORD) { a->rin.s = lex_next(l).s; }
+		else { log_warn("Unexpected token \"%s\"", tok_ks[T.k]); }
+	} continue;
+	case TK_ROUT: {
+		if (lex_next(l), T.k == TK_WORD) { a->rout.s = lex_next(l).s; a->rout.app = false; }
+		else { log_warn("Unexpected token \"%s\"", tok_ks[T.k]); }
+	} continue;
+	case TK_RAPP: {
+		if (lex_next(l), T.k == TK_WORD) { a->rout.s = lex_next(l).s; a->rout.app = true; }
+		else { log_warn("Unexpected token \"%s\"", tok_ks[T.k]); }
+	} continue;
 	default: { stack_push(&a->c, NULL); } return a;
 	}
 }
 
+/* Parse a pipe statement. */
+static ast *parse_pipe(lex *l, ast *lhs) {
+	ast *a = ast_init(AK_PIPE);
+	a->lc = lhs; a->rc = parse_comm(l); return a;
+}
+
 /* Print parser debug output. */
 extern void ast_debug(ast *a, u64 indent) {
 	for (u64 i = 0; i != indent; i += 1) { printf("\t"); }
@@ -67,7 +92,12 @@ extern void ast_debug(ast *a, u64 indent) {
 		for (u64 i = 1; i != a->c.al - 1; i += 1) {
 			printf(" %s", ((char **)a->c.a)[i]);
 		}
+		if (a->rin.s) { printf(" < %s", a->rin.s); }
+		if (a->rout.s) { printf(a->rout.app ? " >> %s" : " > %s", a->rout.s); }
 	}
 
 	printf("\n");
+	
+	if (a->lc) { ast_debug(a->lc, indent + 1); }
+	if (a->rc) { ast_debug(a->rc, indent + 1); }
 }
diff --git a/src/parse.h b/src/parse.h
index 36a744e..c3d7ce9 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -9,12 +9,15 @@
 
 /* Remember to update ast_ks in parse.c */
 typedef enum {
-	AK_VOID, AK_COMM,
+	AK_VOID, AK_COMM, AK_PIPE,
 } ast_k;
 
-/* k : Kind, s : String, c : Children */
+/* k : Kind, s : String, lc : Left Child, rc : Right Child, c : Children */
 typedef struct ast_s {
-	ast_k k; char *s; stack c;
+	ast_k k; char *s; struct ast_s *lc, *rc; stack c;
+	struct { char *s; } rin; /* stdin redirect */
+	struct { char *s; bool app; } rout; /* stdout redirect */
+	struct { char *s; bool app; } rerr; /* stderr redirect */
 } ast;
 
 extern char *ast_ks[];