Author | Jamozed <[email protected]> |
Date | 2021-11-25 13:51:12 |
Commit | 4eee86debb5d4b41362589324711a460eaf7ff3c |
Parent | b17c84602b6573995399964a6deb7cdf9bb58642 |
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[];