Author | Jakob Wakeling <[email protected]> |
Date | 2023-12-28 01:53:40 |
Commit | 4a1246f2dd8f46c225d1bc59a98fa1a7d38497cb |
Parent | 59a2eecffbf9c4899c9539de4938ffe8f3932b49 |
Add eval builtin
Diffstat
M | CMakeLists.txt | | | 3 | ++- |
M | src/bltn.c | | | 2 | ++ |
M | src/builtin/cd.c | | | 1 | + |
A | src/builtin/eval.c | | | 52 | ++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/eval.c | | | 4 | ++-- |
M | src/exec.c | | | 23 | ++++++++++++----------- |
M | src/lex.c | | | 20 | ++++++++++---------- |
R | src/lineread.c -> src/lineread/lineread.c | | | 62 | ++++++++++++++++++++++++++++---------------------------------- |
R | src/lineread.h -> src/lineread/lineread.h | | | 0 | |
M | src/main.c | | | 28 | ++++++++++++++-------------- |
M | src/parse.c | | | 20 | ++++++++++---------- |
M | src/util/optget.c | | | 20 | ++++++++++---------- |
M | src/util/stack.c | | | 2 | +- |
13 files changed, 144 insertions, 93 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e06757..c9e0e5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,9 +3,10 @@ project(ESH VERSION 0.0.0 LANGUAGES C) set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD_REQUIRED TRUE) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin) +add_compile_definitions(PROJECT_VERSION="${PROJECT_VERSION}") file(GLOB_RECURSE SRC CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/src/*.c) -add_compile_definitions(PROJECT_VERSION="${PROJECT_VERSION}") add_executable(esh ${SRC}) diff --git a/src/bltn.c b/src/bltn.c index e8def8d..0858e7a 100644 --- a/src/bltn.c +++ b/src/bltn.c @@ -16,11 +16,13 @@ static int bltn_help(int, char *[]) { fputs(version, stdout); return 0; } static int bltn_true(int, char *[]) { return 0; } extern int bltn_cd(int, char *[]); +extern int bltn_eval(int, char *[]); static int getret(int, char *[]) { printf("%d\n", _ret); return 0; } bltn bltns[] = { { "cd", &bltn_cd }, + { "eval", &bltn_eval }, { "exit", &bltn_exit }, { "false", &bltn_false }, { "help", &bltn_help }, diff --git a/src/builtin/cd.c b/src/builtin/cd.c index 32fda42..87c9fe6 100644 --- a/src/builtin/cd.c +++ b/src/builtin/cd.c @@ -52,4 +52,5 @@ static const char *const help = " -L Handle the operand dot-dot logically\n" " -P Handle the operand dot-dot physically\n" " --help Display help information\n" + " --version Display version information\n" ; diff --git a/src/builtin/eval.c b/src/builtin/eval.c new file mode 100644 index 0000000..1ee3473 --- /dev/null +++ b/src/builtin/eval.c @@ -0,0 +1,52 @@ +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#include "../eval.h" +#include "../util/optget.h" +#include "../util/util.h" + +#include <stdio.h> +#include <string.h> + +static const char *const help; +extern const char *const version; /* main.c */ + +int bltn_eval(int ac, char *av[]) { + struct opt opt = OPTGET_INIT; opt.str = ""; opt.lops = (struct lop[]){ + { "help", ARG_NUL, 256 }, + { "version", ARG_NUL, 257 }, + { NULL, 0, 0 }, + }; + + for (int c; (c = optget(&opt, av, 0)) != -1;) { + switch (c) { + case 256: { fputs(help, stdout); } return 0; + case 257: { fputs(version, stdout); } return 0; + default: {} return -1; + } + } + + char *args, *dest; u64 size = 0; + + for (u64 i = 0; i < ac - opt.ind; i += 1) { + size += (strlen(av[opt.ind + i]) + 1); + } + + args = xcalloc(size, sizeof (*args)); dest = args; + + for (u64 i = 0; i < ac - opt.ind; i += 1) { + if (i != 0) { strncat(dest, " ", 1); dest += 1; } + strcat(dest, av[opt.ind + i]); dest += strlen(av[opt.ind + i]); + } + + eval(args, size); return 0; +} + +static const char *const help = + "eval - Execute arguments as a command\n" + "Usage:\n" + " eval [argument...]\n" + "Options:\n" + " --help Display help information\n" + " --version Display version information\n" +; diff --git a/src/eval.c b/src/eval.c index 742b8c1..6db9ab2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -13,11 +13,11 @@ bool Eflag, pflag; void eval(char *src, u64 len) { lex l = lex_init(src, len); if (Eflag) { lex_debug(&l); return; } - + for (ast *a; l.t.k != TK_EOF; ast_free(a)) { a = parse(&l); if (!a) { return; } if (pflag) { ast_debug(a, 0); continue; } - + _ret = execute(a, STDIN_FILENO, NULL); } } diff --git a/src/exec.c b/src/exec.c index 50ff146..240628b 100644 --- a/src/exec.c +++ b/src/exec.c @@ -12,6 +12,7 @@ #include <unistd.h> #include <errno.h> +#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -34,16 +35,16 @@ s32 execute(ast *a, int fdi, int fd[2]) { static s32 execute_comm(ast *a, int fdi, int fd[2]) { /* If the command is null, then do nothing */ if (!a->s) { return _ret; } - + /* Handle builtins */ for (bltn *b = bltns; b->name; b += 1) { if (strcmp(a->s, b->name) == 0) { return b->fn(a->c.al - 1, a->c.a); } } - + pid_t pid; int status, rin = -1, rout = -1, rerr = -1; - + /* 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; @@ -56,22 +57,22 @@ static s32 execute_comm(ast *a, int fdi, int fd[2]) { 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; - + /* Fork and exec the child process */ if ((pid = fork()) == 0) { signal(SIGINT, SIG_DFL); - + 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]); } else { perror((char *)av[0]); } exit(1); @@ -87,11 +88,11 @@ static s32 execute_comm(ast *a, int fdi, int fd[2]) { waitpid(pid, &status, 0); } } - + if (rin != -1) { close(rin); } if (rout != -1) { close(rout); } if (rerr != -1) { close(rerr); } - + return WEXITSTATUS(status); } diff --git a/src/lex.c b/src/lex.c index 44d54f1..c5945ca 100644 --- a/src/lex.c +++ b/src/lex.c @@ -33,20 +33,20 @@ tok lex_peek(lex *l) { return T; } tok lex_next(lex *l) { if (T.k == TK_EOF) { return T; } tok t = T; T = (tok){ .k = TK_VOID }; - + /* Skip null characters and whitespace */ skip:; for (; P != Q && (!P[0] || is_space(P[0])); P += 1); - + /* Return the current token immediately if EOF or END is reached */ if (P == Q) { T.k = TK_EOF; return t; } if (P[0] == '\n' || P[0] == ';') { P += 1; T.k = TK_END; return t; } - + /* Skip comments */ if (P[0] == '#') { for (P += 1; P != Q && P[0] != '\n'; P += 1); if (P[0] == '\n') { P += 1; } goto skip; } - + switch (P[0]) { case '|': { T.k = TK_PIPE; P += 1; } break; case '<': { T.k = TK_RIN; P += 1; } break; @@ -54,14 +54,14 @@ tok lex_next(lex *l) { 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); - + for (; P != Q && P[0] != '\n' && P[0] != ';' && P[0] != ' '; P += 1) { if (P[0] == '|' || P[0] == '<' || P[0] == '>') { break; } - + /* Handle single quotes */ else if (P[0] == '\'') for (P += 1;; P += 1) { /* FIXME memory leak upon missing closing ', needs refinement */ @@ -69,15 +69,15 @@ tok lex_next(lex *l) { else if (P[0] == '\'') { break; } else { stack_push(&s, P[0]); } } - + /* Handle all other characters */ else { stack_push(&s, P[0]); } } - + T.s = strndup(s.a, s.al); T.k = TK_WORD; stack_free(&s); } break; } - + return t; } diff --git a/src/lineread.c b/src/lineread/lineread.c similarity index 96% rename from src/lineread.c rename to src/lineread/lineread.c index 71050a3..562cc00 100644 --- a/src/lineread.c +++ b/src/lineread/lineread.c @@ -1,9 +1,9 @@ // Copyright (C) 2021, Jakob Wakeling // All rights reserved. +#include "../util/log.h" +#include "../util/util.h" #include "lineread.h" -#include "util/log.h" -#include "util/util.h" #include <sys/ioctl.h> #include <termios.h> @@ -16,19 +16,14 @@ #include <stdlib.h> #include <string.h> -typedef struct { - char *s; uptr sp, sl, sc; - char *prompt; uptr pl, cols; -} line; - -typedef struct { - line *a; uptr ap, ah, at, al, ac; -} hist; +typedef struct { char *s; uptr sp, sl, sc; } line; +typedef struct { line *a; uptr ap, ah, at, al, ac; } hist; static struct termios tco, tcn; static bool rawflag = false, ateflag = false; static hist h = { NULL, 0, 0, 0, 0 }; +static const char *const prompt = "$ "; static char *linentty(void); static char *lineedit(void); @@ -77,14 +72,14 @@ void linefree(void) { hist_free(&h); } static char *linentty(void) { line l; register char *r; l.sp = 0; l.sl = 0; l.sc = 1024; - + if (!(l.s = xmalloc(l.sc * sizeof (*l.s)))) { return NULL; } - + for (register int c; (c = fgetc(stdin));) { if (c == EOF) { if (l.sl) { break; } r = NULL; goto ret; } else { line_push(&l, c); } continue; } - + end:; r = strndup(l.s, l.sl); ret:; @@ -96,25 +91,24 @@ ret:; /* Dynamically read a line from stdin */ static char *lineedit(void) { register char *r; - + { if (h.a == NULL) { if (hist_init(&h)) { return NULL; } } - + line m = { NULL, 0, 0, 1024 }; - m.prompt = "$ "; m.pl = strlen(m.prompt); - - if (!(m.cols = getcols())) { return NULL; } + + // if (!(m.cols = getcols())) { return NULL; } if (!(m.s = xmalloc(m.sc * sizeof (*m.s)))) { return NULL; } m.s[0] = 0; - + hist_push(&h, m); } - - tcraw(); fputs(l.prompt, stdout); - + + tcraw(); fputs(prompt, stdout); + for (register int c; (c = fgetc(stdin));) { if (errno) { log_warn("FIXME: %s", strerror(errno)); errno = 0; } // printf("%02X\n", c); continue; - + switch (c) { case '\x01': { line_move_home(&l); } continue; // CTRL + A case '\x02': { line_move_left(&l); } continue; // CTRL + B @@ -162,7 +156,7 @@ static char *lineedit(void) { default: { line_insert(&l, c); } continue; } } - + end:; r = strndup(l.s, l.sl); ret:; @@ -192,10 +186,10 @@ static void line_esc(line *l, register int c) { /* Put stdin into raw mode */ static inline void tcraw(void) { if (rawflag) { return; } - + tcgetattr(STDIN_FILENO, &tco); tcn = tco; if (!ateflag) { atexit(tcrestore); ateflag = true; } - + /* No break, no CR to NL, no parity check, no strip bit, no flow control */ tcn.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* No post process, 8-bit chars */ @@ -204,7 +198,7 @@ static inline void tcraw(void) { tcn.c_lflag &= ~(ICANON | ECHO | IEXTEN | ISIG); /* Read every byte with no delay */ tcn.c_cc[VMIN] = 1; tcn.c_cc[VTIME] = 0; - + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tcn); rawflag = true; return; } @@ -228,8 +222,8 @@ static void clearscreen(line *l) { /* Refresh line */ static void line_refresh(line *l) { fputs("\r\x1B[0K", stdout); - fputs(l->prompt, stdout); fputs(l->s, stdout); - fprintf(stdout, "\r\x1B[%zuC", l->pl + l->sp); + fputs(prompt, stdout); fputs(l->s, stdout); + fprintf(stdout, "\r\x1B[%zuC", strlen(prompt) + l->sp); return; } @@ -312,10 +306,10 @@ static void line_delete(line *l) { /* Delete the word preceeding the cursor */ static void line_delete_word_home(line *l) { size_t p = l->sp; - + for (; l->sp && l->s[l->sp - 1] == ' '; --l->sp); for (; l->sp && l->s[l->sp - 1] != ' '; --l->sp); - + memmove(l->s + l->sp, l->s + p, l->sl - p + 1); l->sl -= p - l->sp; line_refresh(l); return; } @@ -323,10 +317,10 @@ static void line_delete_word_home(line *l) { /* Delete the word following the cursor */ static void line_delete_word_end(line *l) { size_t p = l->sp; - + for (; p != l->sl && l->s[p] == ' '; ++p); for (; p != l->sl && l->s[p] != ' '; ++p); - + memmove(l->s + l->sp, l->s + p, l->sl - p + 1); l->sl -= p - l->sp; line_refresh(l); return; } @@ -386,6 +380,6 @@ static void hist_push(hist *h, line l) { ++h->al; h->ap = h->at; if (++h->at == h->ac) { h->at = 0; } } else { h->ap = h->at; if (++h->at == h->ac) { h->at = 0; } h->ah = h->at; } - + free(h->a[h->ap].s); h->a[h->ap] = l; return; } diff --git a/src/lineread.h b/src/lineread/lineread.h similarity index 100% rename from src/lineread.h rename to src/lineread/lineread.h diff --git a/src/main.c b/src/main.c index 93ac722..ba0e3ee 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,7 @@ // All rights reserved. #include "eval.h" -#include "lineread.h" +#include "lineread/lineread.h" #include "util/log.h" #include "util/optget.h" #include "util/util.h" @@ -25,14 +25,14 @@ bool _loop = true; int main(int, char *av[]) { struct opt opt = OPTGET_INIT; opt.str = "Ep"; opt.lops = (struct lop[]){ - { "help", ARG_NUL, 256 }, - { "version", ARG_NUL, 257 }, - { "debug", ARG_NUL, 258 }, + { "help", ARG_NUL, 256 }, + { "version", ARG_NUL, 257 }, + { "debug", ARG_NUL, 258 }, { NULL, 0, 0 }, }; - + struct {} args = {}; - + for (int c; (c = optget(&opt, av, 1)) != -1;) { switch (c) { case 'E': { Eflag = true; } break; @@ -43,19 +43,19 @@ int main(int, char *av[]) { default: {} return -1; } } - + signal(SIGINT, &reset); signal(SIGQUIT, SIG_IGN); signal(SIGTSTP, SIG_IGN); atexit(&linefree); - + do { if (sigsetjmp(jmp, 1)) { fputc('\n', stdout); } jmpflag = true; - - char *line = lineread(); - if (!line) { if (errno) { log_warn("lineread: %s", strerror(errno)); } break; } - - eval(line, strlen(line)); free(line); + + char *l = lineread(); + if (!l) { if (errno) { log_warn("lineread: %s", strerror(errno)); } break; } + + eval(l, strlen(l)); free(l); } while (_loop); - + return __warned; } diff --git a/src/parse.c b/src/parse.c index 771cee3..6b40c4a 100644 --- a/src/parse.c +++ b/src/parse.c @@ -35,13 +35,13 @@ static ast *parse_pipe(lex *l, ast *lhs); extern ast *parse(lex *l) { for (; T.k == TK_END; lex_next(l)); if (T.k == TK_EOF) { return NULL; } - + if (T.k != TK_WORD) { log_warn("Unexpected \"%s\", was expecting a word", tok_ks[T.k]); return NULL; } - + ast *lhs = parse_comm(l); - + for (;;) switch (lex_next(l).k) { case TK_PIPE: { lhs = parse_pipe(l, lhs); } continue; default: {} return lhs; @@ -51,13 +51,13 @@ extern ast *parse(lex *l) { /* Parse a command statement. */ static ast *parse_comm(lex *l) { assert(T.k == TK_WORD); - + ast *a = ast_init(AK_COMM); a->c = stack_init(sizeof (char *), &free); - + /* Push each command argument onto the child stack */ stack_push(&a->c, (a->s = lex_next(l).s)); - + for (;;) switch (lex_peek(l).k) { case TK_WORD: { stack_push(&a->c, lex_next(l).s); } continue; case TK_RIN: { @@ -85,9 +85,9 @@ static ast *parse_pipe(lex *l, ast *lhs) { /* Print parser debug output. */ extern void ast_debug(ast *a, u64 indent) { for (u64 i = 0; i != indent; i += 1) { printf("\t"); } - + printf("%s: %s", ast_ks[a->k], a->s); - + if (a->k == AK_COMM) { for (u64 i = 1; i != a->c.al - 1; i += 1) { printf(" %s", ((char **)a->c.a)[i]); @@ -95,9 +95,9 @@ extern void ast_debug(ast *a, u64 indent) { 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/util/optget.c b/src/util/optget.c index a21e736..893d01e 100644 --- a/src/util/optget.c +++ b/src/util/optget.c @@ -19,20 +19,20 @@ int optget(struct opt *opt, char *av[], int flags) { if (!cur) { opt->ind -= opt->nop; opt->nop = 0; return -1; } } else if (!cur || (cur[0] != '-' || cur[1] == 0)) { return -1; } - + int optind = opt->ind, optret; - + if (cur[1] == '-') { if (cur[2] == 0) { if (opt->nop) { permute(av, opt->ind++, opt->nop); opt->ind -= opt->nop; opt->nop = 0; } else { ++opt->ind; } return -1; } - + int optend, lop; optret = '?'; opt->opt = 0; opt->lop = cur; if (!opt->lops) { goto nol; } for (optend = 2; cur[optend] != '=' && cur[optend] != 0; ++optend); - + for (lop = 0; opt->lops[lop].str; ++lop) { if (strncmp(&cur[2], opt->lops[lop].str, (size_t)optend - 2) == 0) { if (!opt->lops[lop].str[optend - 2]) { @@ -40,7 +40,7 @@ int optget(struct opt *opt, char *av[], int flags) { } } } - + if (opt->lops[lop].arg > ARG_NUL) { if (cur[optend]) { opt->arg = &cur[optend + 1]; } else if (av[opt->ind + 1]) { opt->arg = av[++opt->ind]; } @@ -50,13 +50,13 @@ int optget(struct opt *opt, char *av[], int flags) { } } else { opt->arg = NULL; } - + nol: opt->pos = 0; } else { optret = opt->opt = cur[opt->pos++]; opt->lop = NULL; const char *optchr = strchr(opt->str, opt->opt); - + if (!optchr) { optret = '?'; } else if (optchr[1] == ':') { if (cur[opt->pos]) { opt->arg = &cur[opt->pos]; } @@ -66,14 +66,14 @@ nol: opt->pos = 0; } else { opt->arg = NULL; } } - + if (!opt->pos || !cur[opt->pos]) { ++opt->ind; opt->pos = 1; if (opt->nop) for (; optind < opt->ind; ++optind) { permute(av, optind, opt->nop); } } - + if (optret == '?' && opt->str[0] != ':') { if (opt->opt) { fprintf(stderr, "%c: invalid option", opt->opt); } else if (opt->lop) { fprintf(stderr, "%s: invalid option", opt->lop); } @@ -82,7 +82,7 @@ nol: opt->pos = 0; if (opt->opt) { fprintf(stderr, "%c: option requires argument", opt->opt); } else if (opt->lop) { fprintf(stderr, "%s: option requires argument", opt->lop); } } - + return optret; } diff --git a/src/util/stack.c b/src/util/stack.c index a56b71f..17eed0f 100644 --- a/src/util/stack.c +++ b/src/util/stack.c @@ -18,7 +18,7 @@ void stack_free(stack *s) { if (s->free) for (u64 i = 0; i < s->al; i += 1) { void *e; memcpy(&e, s->a + i * s->el, s->el); s->free(e); } - + free(s->a); } }