Author | Jakob Wakeling <[email protected]> |
Date | 2023-12-28 00:35:12 |
Commit | 1e336bf413a12ed3c021cd24e982c973ff68a00e |
Parent | da120bbed651b0731eaba4e5ca3549c977c56067 |
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[];