From dad87a91281a3fc49b0380798d6902daf1351236 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Tue, 8 Jul 2025 15:07:05 -0400 Subject: [PATCH] Command line options, scripting, comments --- src/input.c | 47 ++++++++---- src/input.h | 3 + src/lex.c | 6 +- src/main.c | 216 ++++++++++++++++++++++++++++++++++------------------ 4 files changed, 182 insertions(+), 90 deletions(-) diff --git a/src/input.c b/src/input.c index 9d3115f..65fcdc4 100644 --- a/src/input.c +++ b/src/input.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -26,7 +27,24 @@ enum character { BACKSPACE = '\177', }; -static char buffer[BUFLEN + 2]; // Terminating ";" +char buffer[BUFLEN + 2]; // Terminating ";" + +char *strinput(char **strp) { + char *p; + size_t l; + + if (!**strp) return NULL; + p = *strp; + while (**strp && **strp != '\n') ++*strp; + l = (*strp)++ - p; + if (l > BUFLEN) + errx(EXIT_FAILURE, "Line is too long; exceeds %d characters", BUFLEN); + strncpy(buffer, p, l); + *(buffer + l++) = ';'; + *(buffer + l) = '\0'; + + return buffer; +} char *input(void) { char *cursor, *end; @@ -34,26 +52,29 @@ char *input(void) { signal(SIGCHLD, waitbg); // TODO: Use sigaction for portability -reset: end = cursor = buffer; *history.t = *buffer = '\0'; history.c = history.t; while (buffer == end) { fputs(PROMPT, stdout); while ((c = getchar()) != '\n') { - if (c >= ' ' && c <= '~') { - if (end - buffer == BUFLEN) continue; - memmove(cursor + 1, cursor, end - cursor); - *cursor++ = c; - *++end = '\0'; - - putchar(c); - fputs(cursor, stdout); - for (i = end - cursor; i > 0; --i) putchar('\b'); - } else switch (c) { + switch (c) { + default: + if (c >= ' ' && c <= '~') { + if (end - buffer == BUFLEN) continue; + memmove(cursor + 1, cursor, end - cursor); + *cursor++ = c; + *++end = '\0'; + + putchar(c); + fputs(cursor, stdout); + for (i = end - cursor; i > 0; --i) putchar('\b'); + } + break; case CTRLC: puts("^C"); - goto reset; + *buffer = '\0'; + return buffer; case CTRLD: puts("^D"); signal(SIGCHLD, SIG_DFL); diff --git a/src/input.h b/src/input.h index 652daa8..9f38eb7 100644 --- a/src/input.h +++ b/src/input.h @@ -1,3 +1,6 @@ #define BUFLEN 1000 +extern char buffer[BUFLEN + 2]; + +char *strinput(char **strp); char *input(void); diff --git a/src/lex.c b/src/lex.c index d032d5b..29410df 100644 --- a/src/lex.c +++ b/src/lex.c @@ -125,12 +125,14 @@ struct cmd *lex(char *b) { b += offset - 1; *(end + offset) = '\0'; break; + case '#': + *(b + 1) = '\0'; case '&': case '|': case ';': if (name && *c->args == name) c->args = NULL; if (c->args) { - if ((c->term = *b) != ';' && *b == *(b + 1)) { + if ((c->term = *b) == *(b + 1) && *b == '&' || *b == '|') { ++c->term; *b++ = '\0'; } @@ -160,7 +162,7 @@ struct cmd *lex(char *b) { } } - switch ((c - 1)->term) { + if (c-- != cmds) switch (c->term) { case AND: case PIPE: case OR: diff --git a/src/main.c b/src/main.c index c30e40f..4e1f58e 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -74,95 +76,159 @@ static int redirectfiles(struct redirect *r) { return 1; } -int main(void) { - struct cmd *cmd, *prev; - int ispipe, ispipestart, ispipeend, status; +void run(struct cmd *cmd) { + struct cmd *prev; + int ispipe, ispipestart, ispipeend; + static int status; pid_t cpid, jobid; struct job job; - initterm(); - readhist(); - - for (prev = ∅ (cmd = lex(input())); prev = &empty) { - for (; cmd->args; prev = cmd++) { - ispipe = cmd->term == PIPE || prev->term == PIPE; - ispipestart = ispipe && prev->term != PIPE; - ispipeend = ispipe && cmd->term != PIPE; - - if (ispipe) { - if (!ispipeend && pipe(cmd->pipe) == -1) { - warn("Unable to create pipe"); - if (!ispipestart) closepipe(prev); - break; - } - if ((jobid = cpid = fork()) == -1) { - warn("Unable to create child process"); - break; - } else if (cpid == 0) { - if (!ispipestart) { - if (dup2(prev->pipe[0], 0) == -1) - err(EXIT_FAILURE, "Unable to duplicate read end of `%s' pipe", - *prev->args); - if (!closepipe(prev)) exit(EXIT_FAILURE); - } - if (!ispipeend) { - if (dup2(cmd->pipe[1], 1) == -1) - err(EXIT_FAILURE, "Unable to duplicate write end of `%s' pipe", - *cmd->args); - if (!closepipe(cmd)) exit(EXIT_FAILURE); - } - - if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE); - if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS); - if (execvp(*cmd->args, cmd->args) == -1) - err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args); - } + for (prev = ∅ cmd->args; prev = cmd++) { + ispipe = cmd->term == PIPE || prev->term == PIPE; + ispipestart = ispipe && prev->term != PIPE; + ispipeend = ispipe && cmd->term != PIPE; + + if (ispipe) { + if (!ispipeend && pipe(cmd->pipe) == -1) { + warn("Unable to create pipe"); + if (!ispipestart) closepipe(prev); + break; + } + if ((jobid = cpid = fork()) == -1) { + warn("Unable to create child process"); + break; + } else if (cpid == 0) { if (!ispipestart) { - closepipe(prev); - jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id; + if (dup2(prev->pipe[0], 0) == -1) + err(EXIT_FAILURE, "Unable to duplicate read end of `%s' pipe", + *prev->args); + if (!closepipe(prev)) exit(EXIT_FAILURE); } - } else { - if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break; - if ((jobid = cpid = fork()) == -1) { - warn("Unable to create child process"); - break; - } else if (cpid == 0) { - if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE); - if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS); - if (execvp(*cmd->args, cmd->args) == -1) - err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args); + if (!ispipeend) { + if (dup2(cmd->pipe[1], 1) == -1) + err(EXIT_FAILURE, "Unable to duplicate write end of `%s' pipe", + *cmd->args); + if (!closepipe(cmd)) exit(EXIT_FAILURE); } + + if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE); + if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS); + if (execvp(*cmd->args, cmd->args) == -1) + err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args); } - if (setpgid(cpid, jobid) == -1) { - if (errno != ESRCH) { - warn("Unable to set pgid of `%s' command to %d", *cmd->args, jobid); - if (kill(cpid, SIGKILL) == -1) - warn("Unable to kill process %d; manual termination may be required", - cpid); - } + if (!ispipestart) { + closepipe(prev); + jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id; + } + } else { + if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break; + if ((jobid = cpid = fork()) == -1) { + warn("Unable to create child process"); break; + } else if (cpid == 0) { + if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE); + if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS); + if (execvp(*cmd->args, cmd->args) == -1) + err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args); + } + } + if (setpgid(cpid, jobid) == -1) { + if (errno != ESRCH) { + warn("Unable to set pgid of `%s' command to %d", *cmd->args, jobid); + if (kill(cpid, SIGKILL) == -1) + warn("Unable to kill process %d; manual termination may be required", + cpid); } + break; + } - job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND}; - if (ispipestart || cmd->term == BG) { - if (!push(&jobs, &job)) { - warn("Unable to add command to background; " - "too many processes in the background"); - if (ispipestart) closepipe(cmd); - break; - } - } else if (cmd->term != PIPE) { - if (!setfg(job)) break; - status = waitfg(job); - if (cmd->term == AND && status != 0) break; - if (cmd->term == OR && status == 0) break; + job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND}; + if (ispipestart || cmd->term == BG) { + if (!push(&jobs, &job)) { + warn("Unable to add command to background; " + "too many processes in the background"); + if (ispipestart) closepipe(cmd); + break; } + } else if (cmd->term != PIPE) { + if (!setfg(job)) break; + status = waitfg(job); + if (cmd->term == AND && status != 0) break; + if (cmd->term == OR && status == 0) break; } - waitbg(0); } + waitbg(0); +} - writehist(); +void usage(void) { + puts("TODO: PRINT USAGE MESSAGE"); +} + +char *getoptions(int argc, char **argv, int *l) { + int opt, help, fd; + struct cmd *cmd; + char *r; + struct stat fdstat; + + help = 0; + r = NULL; + while ((opt = getopt(argc, argv, ":c:h")) != -1) switch (opt) { + case 'c': + *l = strlen(optarg) + 2; + if (!(r = malloc(*l))) err(EXIT_FAILURE, "Memory allocation"); + strcpy(r, optarg); + *(r + *l - 1) = ';'; + *(r + *l) = '\0'; + break; + case 'h': + help = 1; + break; + case ':': + errx(EXIT_FAILURE, "Expected argument following `-%c'", optopt); + case '?': + default: + errx(EXIT_FAILURE, "Unknown command line option `-%c'", optopt); + } + argc -= optind; + argv += optind; + + if (help) { + usage(); + return EXIT_SUCCESS; + } + + if (r) *argv = NULL; + else if (*argv) { + if ((fd = open(*argv, O_RDONLY)) == -1) + err(EXIT_FAILURE, "Unable to open `%s'", *argv); + if (stat(*argv, &fdstat) == -1) + err(EXIT_FAILURE, "Unable to stat `%s'", *argv); + if ((r = mmap(NULL, *l = fdstat.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) + == MAP_FAILED) + err(EXIT_FAILURE, "Unable to memory map `%s'", *argv); + if (close(fd) == -1) err(EXIT_FAILURE, "Unable to close `%s'", *argv); + } + + return r; +} + +int main(int argc, char **argv) { + struct cmd *cmd; + char *o, *p; + int l; + + p = o = getoptions(argc, argv, &l); + + initterm(); + if (p) { + while ((cmd = lex(strinput(&p)))) run(cmd); + if (*argv) munmap(o, l); else free(o); + } else { + readhist(); + while ((cmd = lex(input()))) run(cmd); + writehist(); + } deinitterm(); - return 0; + return EXIT_SUCCESS; } -- 2.51.0