ESH

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

AuthorJamozed <[email protected]>
Date2021-11-25 13:51:12
Commit4eee86debb5d4b41362589324711a460eaf7ff3c
Parentb17c84602b6573995399964a6deb7cdf9bb58642

Properly implement stdin and stdout redirects

Diffstat

M src/exec.c | 51 ++++++++++++++++++++++++++++++++++++++++-----------
M src/lex.c | 4 ++--
M src/lex.h | 2 +-
M src/parse.c | 25 +++++++++++++++++++++----
M src/parse.h | 7 ++++---

5 files changed, 68 insertions, 21 deletions

diff --git a/src/exec.c b/src/exec.c
index b0209a9..6fd4546 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -30,11 +30,6 @@ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
-/*
-	FIXME pipes and redirects are too order dependent, and so sometimes fail.
-	TODO implement stdin and stderr redirection.
-*/
-
 #include "bltn.h"
 #include "esh.h"
 
@@ -57,12 +52,14 @@ static int execute_pipe(ast a, int fdi);
 /* Execute a program. */
 int execute(ast a) {
 	switch (a->k) {
-	case AK_COMM: {} return execute_comm(a, fileno(stdin), NULL);
-	case AK_PIPE: {} return execute_pipe(a, fileno(stdin));
+	case AK_COMM: {} return execute_comm(a, STDIN_FILENO, NULL);
+	case AK_PIPE: {} return execute_pipe(a, STDIN_FILENO);
 	default: { warn("%s: Unimplemented AST", ast_ks[a->k]); } return -1;
 	}
 }
 
+#define CLOSEIF(fd) if (fd >= 0) { close(fd); }
+
 /* Execute a command statement. */
 static int execute_comm(ast a, int fdi, int fd[2]) {
 	/* If the command is null, then do nothing */
@@ -74,14 +71,41 @@ static int execute_comm(ast a, int fdi, int fd[2]) {
 		}
 	}
 
-	pid_t pid; int status; char **av = a->c.a;
+	pid_t pid; int status, ri = -1, ro = -1, re = -1; char **av = a->c.a;
 
+	/* Attempt to open redirect files */
+	if (a->ri.s) {
+		ri = open(a->ri.s, O_RDONLY);
+		if (ri == -1) {
+			warn("%s: %s", a->ri.s, serr()); errno = 0; return 1;
+		}
+	}
+	if (a->ro.s) {
+		ro = open(a->ro.s, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+		if (ro == -1) {
+			warn("%s: %s", a->ri.s, serr()); errno = 0;
+			CLOSEIF(ri); return 1;
+		}
+	}
+	if (a->re.s) {
+		re = open(a->re.s, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+		if (re == -1) {
+			warn("%s: %s", a->re.s, serr()); errno = 0;
+			CLOSEIF(ri); CLOSEIF(ro); return 1;
+		}
+	}
+	
+	/* Fork and exec the child process */
 	if ((pid = fork()) == 0) {
 		signal(SIGINT, SIG_DFL);
 
-		dup2(fdi, fileno(stdin));
+		if (ri != -1) { dup2(ri, STDIN_FILENO); }
+		else { dup2(fdi, STDIN_FILENO); }
+		
+		if (ro != -1) { dup2(ro, STDOUT_FILENO); }
+		else if (fd) { if (fd[0]) { close(fd[0]); } dup2(fd[1], fileno(stdout)); }
 
-		if (fd) { if (fd[0]) { close(fd[0]); } dup2(fd[1], fileno(stdout)); }
+		if (re != -1) { dup2(re, STDERR_FILENO); }
 
 		if (execvp((char *)av[0], (char **)av) == -1) {
 			if (errno == ENOENT) { warn("%s: Command not found", av[0]); }
@@ -91,10 +115,15 @@ static int execute_comm(ast a, int fdi, int fd[2]) {
 	else if (pid == -1) { perror((char *)av[0]); }
 	else {
 		/* Wait for the child to die unless its output is piped */
-		if (fd) { if (fdi != fileno(stdin)) { close(fdi); } close(fd[1]); }
+		if (ro == -1 && fd) {
+			if (fdi != STDIN_FILENO) { close(fdi); } close(fd[1]);
+		}
+		
 		else { waitpid(pid, &status, 0); }
 	}
 
+	CLOSEIF(ri); CLOSEIF(ro); CLOSEIF(re);
+	
 	return WEXITSTATUS(status);
 }
 
diff --git a/src/lex.c b/src/lex.c
index 409eeb9..e84545d 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -43,7 +43,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 
 char *tok_ks[] = {
 	"NULL", "EOF",  "WORD", "END",
-	"PIPE", "RDIN", "ROUT", "RERR",
+	"PIPE", "RIN",  "ROUT", "RERR",
 };
 
 /* Initialise a lexer. */
@@ -82,7 +82,7 @@ skip:;
 	/* Handle punctuators and operators */
 	switch (C) {
 	case '|': { T.k = TK_PIPE; P += 1; } break;
-	case '<': { T.k = TK_RDIN; P += 1; } break;
+	case '<': { T.k = TK_RIN;  P += 1; } break;
 	case '>': { T.k = TK_ROUT; P += 1; } break;
 
 	/* Handle anything else as a word */
diff --git a/src/lex.h b/src/lex.h
index 3dd67cf..6f158fa 100644
--- a/src/lex.h
+++ b/src/lex.h
@@ -37,7 +37,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 
 typedef enum {
 	TK_NULL, TK_EOF,  TK_WORD, TK_END,
-	TK_PIPE, TK_RDIN, TK_ROUT, TK_RERR,
+	TK_PIPE, TK_RIN,  TK_ROUT, TK_RERR,
 } tok_k;
 
 typedef struct { tok_k k; char *s; } tok;
diff --git a/src/parse.c b/src/parse.c
index fc9c33a..3ab46da 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -31,10 +31,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 */
 
 /*
-	TODO: Continue implenting pipes and redirects.
+	FIXME redirecting the output of a command before a pipe results in the
+	post-pipe command hanging indefinitely and leaking memory.
 */
 
 #include "esh.h"
+#include "lex.h"
 #include "util/stack.h"
 #include "util/util.h"
 
@@ -55,7 +57,10 @@ ast ast_init(void) { return assert_calloc(1, sizeof (struct ast_s)); }
 
 /* Uninitialise an AST node and its children. */
 void ast_free(ast a) {
-	if (a) { ast_free(a->lc); ast_free(a->rc); stack_free(&a->c); free(a); }
+	if (a) {
+		ast_free(a->lc); ast_free(a->rc); stack_free(&a->c);
+		free(a->ri.s); free(a->ro.s); free(a->re.s); free(a);
+	}
 }
 
 #define T (lex_peek(l)) /* Current Token */
@@ -85,9 +90,19 @@ static ast parse_comm(lex *l) {
 
 	/* Push each command argument onto the child stack */
 	stack_push(&a->c, (a->s = lex_next(l).s));
-	for (; lex_peek(l).k == TK_WORD;) { stack_push(&a->c, lex_next(l).s); }
 
-	stack_push(&a->c, NULL); return a;
+	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->ri.s = lex_next(l).s; }
+		else { warn("Unexpected token \"%s\"", tok_ks[T.k]); }
+	} continue;
+	case TK_ROUT: {
+		if (lex_next(l), T.k == TK_WORD) { a->ro.s = lex_next(l).s; }
+		else { warn("Unexpected token \"%s\"", tok_ks[T.k]); }
+	} continue;
+	default: { stack_push(&a->c, NULL); } return a;
+	}
 }
 
 /* Parse a pipe statement. */
@@ -106,6 +121,8 @@ static void ast_debug_indent(ast a, UINT i) {
 		for (UINT j = 1; j != a->c.al - 1; j += 1) {
 			printf(" %s", ((char **)a->c.a)[j]);
 		}
+		if (a->ri.s) { printf(" < %s", a->ri.s); }
+		if (a->ro.s) { printf(" > %s", a->ro.s); }
 	}
 
 	printf("\n");
diff --git a/src/parse.h b/src/parse.h
index 41dbcec..0f9331f 100644
--- a/src/parse.h
+++ b/src/parse.h
@@ -37,14 +37,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 #include "util/stack.h"
 #include "util/util.h"
 
-typedef enum {
-	AK_NULL, AK_COMM, AK_PIPE,
-} ast_k;
+typedef enum { AK_NULL, AK_COMM, AK_PIPE } ast_k;
 
 typedef struct ast_s *ast;
 
 struct ast_s {
 	ast_k k; char *s; ast lc, rc; stack c;
+	struct { char *s; bool a; } ri;
+	struct { char *s; bool a; } ro;
+	struct { char *s; bool a; } re;
 };
 
 extern char *ast_ks[];