From: Trent Huber Date: Sun, 13 Jul 2025 23:19:41 +0000 (-0400) Subject: Login and interactive scripts X-Git-Url: https://trenthuber.com/code?a=commitdiff_plain;h=20f61467199fdeeb5a8c3933ea276be93ffe91a1;p=thus.git Login and interactive scripts --- diff --git a/build.c b/build.c index de2b9b1..aaecd9d 100644 --- a/build.c +++ b/build.c @@ -7,7 +7,9 @@ SRCDIR "input", \ SRCDIR "job", \ SRCDIR "lex", \ + SRCDIR "options", \ SRCDIR "main", \ + SRCDIR "run", \ SRCDIR "stack", \ SRCDIR "term", \ SRCDIR "utils" diff --git a/src/input.c b/src/input.c index 65fcdc4..5c56f9a 100644 --- a/src/input.c +++ b/src/input.c @@ -9,6 +9,7 @@ #include "input.h" #include "job.h" #include "stack.h" +#include "options.h" #define PROMPT "% " @@ -29,23 +30,6 @@ enum character { 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; int c, i; diff --git a/src/input.h b/src/input.h index 9f38eb7..1ca304b 100644 --- a/src/input.h +++ b/src/input.h @@ -2,5 +2,4 @@ extern char buffer[BUFLEN + 2]; -char *strinput(char **strp); char *input(void); diff --git a/src/main.c b/src/main.c index 4e1f58e..9c05d5c 100644 --- a/src/main.c +++ b/src/main.c @@ -1,232 +1,28 @@ -#include -#include -#include -#include // DEBUG #include -#include -#include -#include -#include #include -#include +#include // XXX -#include "builtins.h" -#include "stack.h" -#include "history.h" -#include "input.h" -#include "lex.h" #include "job.h" +#include "options.h" +#include "run.h" #include "term.h" -#include "utils.h" - -static int closepipe(struct cmd *cmd) { - int result; - - if (!cmd->args) return 1; - - result = 1; - if (close(cmd->pipe[0]) == -1) { - warn("Unable to close read end of `%s' pipe", *cmd->args); - result = 0; - } - if (close(cmd->pipe[1]) == -1) { - warn("Unable to close write end of `%s' pipe", *cmd->args); - result = 0; - } - return result; -} - -static int redirectfiles(struct redirect *r) { - int oflag, fd; - - for (; r->mode; ++r) { - if (r->oldname) { - switch (r->mode) { - case READ: - oflag = O_RDONLY; - break; - case WRITE: - oflag = O_WRONLY | O_CREAT | O_TRUNC; - break; - case READWRITE: - oflag = O_RDWR | O_CREAT | O_APPEND; - break; - case APPEND: - oflag = O_WRONLY | O_CREAT | O_APPEND; - break; - default:; - } - if ((fd = open(r->oldname, oflag, 0644)) == -1) { - warn("Unable to open `%s'", r->oldname); - return 0; - } - r->oldfd = fd; - } - if (dup2(r->oldfd, r->newfd) == -1) { - warn("Unable to redirect %d to %d", r->newfd, r->oldfd); - return 0; - } - if (r->oldname) { - if (close(r->oldfd) == -1) { - warn("Unable to close file descriptor %d", r->oldfd); - return 0; - } - } - } - return 1; -} - -void run(struct cmd *cmd) { - struct cmd *prev; - int ispipe, ispipestart, ispipeend; - static int status; - pid_t cpid, jobid; - struct job job; - - 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) { - 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); - } - 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; - } - } - waitbg(0); -} - -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); + // TODO: Have `cd' builtin affect $PWD$ env var + + options(&argc, &argv); - 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(); + initterm(); // <-- TODO: Set $SHLVL$ in this function + if (login) runhome(".ashlogin"); + if (string) { + runstr(string); + free(string); + } else if (*argv) runscript(*argv); + else { + runhome(".ashinteractive"); + runinteractive(); } deinitterm(); diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..3e07cc6 --- /dev/null +++ b/src/options.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include + +int login; +char *string; + +void options(int *argcp, char ***argvp) { + int opt, l; + char *usage = "TODO: WRITE USAGE"; + + login = ***argvp == '-'; + + // -h -> help message + // -l -> login shell + // -c "***" -> run string + // file.ash -> run file + while ((opt = getopt(*argcp, *argvp, ":c:hl")) != -1) switch (opt) { + case 'c': + l = strlen(optarg) + 2; + if (!(string = malloc(l))) err(EXIT_FAILURE, "Memory allocation"); + strcpy(string, optarg); + *(string + l - 1) = ';'; + *(string + l) = '\0'; + break; + case 'h': + errx(EXIT_SUCCESS, "%s", usage); + case 'l': + login = 1; + break; + case ':': + errx(EXIT_FAILURE, "Expected argument following `-%c'\n%s", optopt, usage); + case '?': + default: + errx(EXIT_FAILURE, "Unknown command line option `-%c'\n%s", optopt, usage); + } + *argcp -= optind; + *argvp+= optind; +} diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..6bb8fa6 --- /dev/null +++ b/src/options.h @@ -0,0 +1,4 @@ +extern int login; +extern char *string; + +void options(int *argcp, char ***argvp); diff --git a/src/run.c b/src/run.c new file mode 100644 index 0000000..8ba727e --- /dev/null +++ b/src/run.c @@ -0,0 +1,229 @@ +#include +#include +#include +#include // DEBUG +#include +#include +#include +#include +#include +#include +#include +#include + +#include "builtins.h" +#include "history.h" +#include "input.h" +#include "job.h" +#include "lex.h" +#include "stack.h" +#include "term.h" +#include "utils.h" + +static int closepipe(struct cmd *cmd) { + int result; + + if (!cmd->args) return 1; + + result = 1; + if (close(cmd->pipe[0]) == -1) { + warn("Unable to close read end of `%s' pipe", *cmd->args); + result = 0; + } + if (close(cmd->pipe[1]) == -1) { + warn("Unable to close write end of `%s' pipe", *cmd->args); + result = 0; + } + return result; +} + +static int redirectfiles(struct redirect *r) { + int oflag, fd; + + for (; r->mode; ++r) { + if (r->oldname) { + switch (r->mode) { + case READ: + oflag = O_RDONLY; + break; + case WRITE: + oflag = O_WRONLY | O_CREAT | O_TRUNC; + break; + case READWRITE: + oflag = O_RDWR | O_CREAT | O_APPEND; + break; + case APPEND: + oflag = O_WRONLY | O_CREAT | O_APPEND; + break; + default:; + } + if ((fd = open(r->oldname, oflag, 0644)) == -1) { + warn("Unable to open `%s'", r->oldname); + return 0; + } + r->oldfd = fd; + } + if (dup2(r->oldfd, r->newfd) == -1) { + warn("Unable to redirect %d to %d", r->newfd, r->oldfd); + return 0; + } + if (r->oldname) { + if (close(r->oldfd) == -1) { + warn("Unable to close file descriptor %d", r->oldfd); + return 0; + } + } + } + return 1; +} + +static void runcmd(struct cmd *cmd) { + struct cmd *prev; + int ispipe, ispipestart, ispipeend; + static int status; + pid_t cpid, jobid; + struct job job; + + 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) { + 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); + } + 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; + } + } + waitbg(0); +} + +void runstr(char *start) { + char *p; + size_t l; + + for (p = start; *p; start = ++p) { + while (*p && *p != '\n') ++p; + l = p - start; + if (l > BUFLEN) + errx(EXIT_FAILURE, "Line is too long; exceeds %d characters", BUFLEN); + strncpy(buffer, start, l); + *(buffer + l) = ';'; + *(buffer + l + 1) = '\0'; + runcmd(lex(buffer)); + } +} + +void runscript(char *filename) { + int fd; + struct stat sstat; + char *str; + + if ((fd = open(filename, O_RDONLY)) == -1) + err(EXIT_FAILURE, "Unable to open `%s'", filename); + if (stat(filename, &sstat) == -1) + err(EXIT_FAILURE, "Unable to stat `%s'", filename); + if (sstat.st_size == 0) return; + if ((str = mmap(NULL, sstat.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) + == MAP_FAILED) + err(EXIT_FAILURE, "Unable to memory map `%s'", filename); + if (close(fd) == -1) err(EXIT_FAILURE, "Unable to close `%s'", filename); + runstr(str); + if (munmap(str, sstat.st_size) == -1) + err(EXIT_FAILURE, "Unable to unmap `%s'", filename); +} + +void runinteractive(void) { + struct cmd *cmd; + + while ((cmd = lex(input()))) runcmd(cmd); +} + +#define MAXPATH 1000 +char *prependhome(char *filename) { + static char filepath[MAXPATH + 1]; + + strcpy(filepath, getenv("HOME")); + strcat(filepath, "/"); + strcat(filepath, filename); + + return filepath; +} + +void runhome(char *filename) { + char *filepath; + int fd; + + filepath = prependhome(filename); + if (access(filepath, R_OK) == -1) { + if (errno != ENOENT) + err(EXIT_FAILURE, "Unable to access `%s'", filepath); + if ((fd = open(filepath, O_RDONLY | O_CREAT, 0644)) == -1) + err(EXIT_FAILURE, "Unable to open `%s'", filepath); + if (close(fd) == -1) + err(EXIT_FAILURE, "Unable to close `%s'", filepath); + } else runscript(filepath); +} diff --git a/src/run.h b/src/run.h new file mode 100644 index 0000000..87eb890 --- /dev/null +++ b/src/run.h @@ -0,0 +1,6 @@ +char *prependhome(char *filename); + +void runstr(char *str); +void runscript(char *filename); +void runinteractive(void); +void runhome(char *filename);