From 8af800f078a9fb513b269e8fef43dad7b53992f7 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Fri, 18 Jul 2025 03:32:32 -0400 Subject: [PATCH] Major refactoring, cleaning --- build.c | 7 +- src/builtins.c | 24 +++--- src/config.h | 10 +++ src/history.c | 55 +++++--------- src/history.h | 5 +- src/input.c | 93 ++++++++++++++++++++--- src/input.h | 9 ++- src/job.c | 86 +++++++++++++++++++-- src/job.h | 9 ++- src/main.c | 29 +++---- src/options.c | 34 +++++---- src/options.h | 6 +- src/{lex.c => parse.c} | 55 ++++++-------- src/{lex.h => parse.h} | 3 +- src/run.c | 167 +++++++++-------------------------------- src/run.h | 7 +- src/stack.c | 10 +-- src/stack.h | 8 +- src/term.c | 86 --------------------- src/term.h | 6 -- src/utils.c | 57 +++++++++++++- src/utils.h | 6 +- 22 files changed, 382 insertions(+), 390 deletions(-) create mode 100644 src/config.h rename src/{lex.c => parse.c} (78%) rename src/{lex.h => parse.h} (89%) delete mode 100644 src/term.c delete mode 100644 src/term.h diff --git a/build.c b/build.c index aaecd9d..d84fbf4 100644 --- a/build.c +++ b/build.c @@ -3,15 +3,14 @@ #define SRCDIR "src/" #define SRC \ SRCDIR "builtins", \ - SRCDIR "history", \ SRCDIR "input", \ + SRCDIR "history", \ SRCDIR "job", \ - SRCDIR "lex", \ - SRCDIR "options", \ + SRCDIR "parse", \ SRCDIR "main", \ + SRCDIR "options", \ SRCDIR "run", \ SRCDIR "stack", \ - SRCDIR "term", \ SRCDIR "utils" #define BINDIR "bin/" #define ASH BINDIR "ash" diff --git a/src/builtins.c b/src/builtins.c index f42c542..5ec4be3 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -11,6 +10,7 @@ #include "job.h" #include "stack.h" #include "term.h" +#include "utils.h" #define BUILTINSIG(name) int name(char **tokens) @@ -19,10 +19,10 @@ struct builtin { BUILTINSIG((*func)); }; -BUILTINSIG(cd) { +BUILTINSIG(cd) { // TODO: Affect $PWD$ env var if (!tokens[1]) return 1; if (chdir(tokens[1]) != -1) return 0; - warn("Unable to change directory to `%s'", tokens[1]); + note("Unable to change directory to `%s'", tokens[1]); return 1; } @@ -34,16 +34,16 @@ BUILTINSIG(fg) { errno = 0; if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || jobid <= 0) { - warn("Invalid process group id"); + note("Invalid process group id"); return 1; } if (!(job = findjob((pid_t)jobid))) { - warnx("Unable to find process group %d", (pid_t)jobid); + note("Unable to find process group %d", (pid_t)jobid); return 1; } job = deletejob(); } else if (!(job = pull(&jobs))) { - warnx("No processes to bring into the foreground"); + note("No processes to bring into the foreground"); return 1; } if (!setfg(*job)) return 1; @@ -60,33 +60,33 @@ BUILTINSIG(bg) { errno = 0; if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || jobid <= 0) { - warn("Invalid job id"); + note("Invalid job id"); return 1; } if (!(job = findjob((pid_t)jobid))) { - warnx("Unable to find job %d", (pid_t)jobid); + note("Unable to find job %d", (pid_t)jobid); return 1; } if (job->type == BACKGROUND) { - warnx("Job %d already in background", (pid_t)jobid); + note("Job %d already in background", (pid_t)jobid); return 1; } } else { for (jobs.c = MINUSONE(jobs, t); jobs.c != MINUSONE(jobs, b); DEC(jobs, c)) if (CURRENT->type == SUSPENDED) break; if (jobs.c == MINUSONE(jobs, b)) { - warnx("No suspended jobs to run in background"); + note("No suspended jobs to run in background"); return 1; } } job = deletejob(); if (!(push(&jobs, job))) { - warnx("Unable to add job to background; too many jobs"); + note("Unable to add job to background; too many jobs"); return 1; } if (killpg(job->id, SIGCONT) == -1) { - warn("Unable to wake up suspended process group %d", job->id); + note("Unable to wake up suspended process group %d", job->id); return 1; } job->type = BACKGROUND; diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..add21bf --- /dev/null +++ b/src/config.h @@ -0,0 +1,10 @@ +#define HISTORYFILE ".ashhistory" +#define INTERACTIVEFILE ".ashinteractive" +#define LOGINFILE ".ashlogin" + +#define MAXCHARS 1000 // Maximum number of character per line +#define MAXCMDS 100 // Maximum number of commands per line +#define MAXHIST 100 // Maximum number of entries to store in history file +#define MAXJOBS 100 // Maximum number of suspended/background jobs at any time +#define MAXPATH 100 // Maximum number of character for a file path +#define MAXRDS 10 // Maximum number of file redirects per command diff --git a/src/history.c b/src/history.c index ca39b45..121b9a6 100644 --- a/src/history.c +++ b/src/history.c @@ -1,63 +1,48 @@ -#include #include #include #include #include +#include "config.h" #include "input.h" #include "stack.h" #include "utils.h" -#define HISTLEN 100 -#define HISTNAME ".ashhistory" +static char historyarr[MAXHIST + 1][MAXCHARS + 1]; +INITSTACK(history, historyarr, 1); -static char *histpath, histarr[HISTLEN + 1][BUFLEN + 1]; -struct stack INITSTACK(history, histarr, 1); +void readhistory(void) { + FILE *file; -void readhist(void) { - char *homepath, buffer[BUFLEN + 1]; - FILE *histfile; - int e; - - if (!(homepath = getenv("HOME"))) - errx(EXIT_FAILURE, "HOME environment variable does not exist"); - histpath = allocate(strlen(homepath) + 1 + strlen(HISTNAME) + 1); - strcpy(histpath, homepath); - strcat(histpath, "/"); - strcat(histpath, HISTNAME); - - if (!(histfile = fopen(histpath, "r"))) { + if (!(file = fopen(prependhome(HISTORYFILE), "r"))) { if (errno == ENOENT) return; - err(EXIT_FAILURE, "Unable to open history file for reading"); + fatal("Unable to open history file for reading"); } - while (fgets(buffer, history.size, histfile)) { + while (fgets(buffer, history.size, file)) { *(buffer + strlen(buffer) - 1) = '\0'; push(&history, buffer); } - - if (ferror(histfile) || !feof(histfile)) - err(EXIT_FAILURE, "Unable to read from history file"); - if (fclose(histfile) == EOF) err(EXIT_FAILURE, "Unable to close history file"); + if (ferror(file) || !feof(file)) + fatal("Unable to read from history file"); + if (fclose(file) == EOF) fatal("Unable to close history file"); } -void writehist(void) { - FILE *histfile; +void writehistory(void) { + FILE *file; - if (!(histfile = fopen(histpath, "w"))) { - warn("Unable to open history file for writing"); + if (!(file = fopen(prependhome(HISTORYFILE), "w"))) { + note("Unable to open history file for writing"); return; } - for (history.c = history.b; history.c != history.t; INC(history, c)) { - if (fputs(history.c, histfile) == EOF) { - warn("Unable to write to history file"); + if (fputs((char *)history.c, file) == EOF) { + note("Unable to write to history file"); break; } - if (fputc('\n', histfile) == EOF) { - warn("Unable to write newline to history file"); + if (fputc('\n', file) == EOF) { + note("Unable to terminate line of history file"); break; } } - - if (fclose(histfile) == EOF) warn("Unable to close history stream"); + if (fclose(file) == EOF) note("Unable to close history stream"); } diff --git a/src/history.h b/src/history.h index bb7130f..22d30e6 100644 --- a/src/history.h +++ b/src/history.h @@ -1,4 +1,5 @@ extern struct stack history; -void readhist(void); -void writehist(void); +void readhistory(void); +void writehistory(void); + diff --git a/src/input.c b/src/input.c index abef388..a7d01e3 100644 --- a/src/input.c +++ b/src/input.c @@ -1,17 +1,21 @@ -#include +#include +#include #include #include #include +#include +#include #include #include +#include "config.h" #include "history.h" #include "input.h" #include "job.h" #include "stack.h" -#include "options.h" +#include "utils.h" -#define PROMPT "% " +#define PROMPT "> " // TODO: Have prompt be an environment variable enum character { CTRLC = '\003', @@ -29,14 +33,80 @@ enum character { DEL = '\177', }; -char buffer[BUFLEN + 2]; // Terminating ";" +char *string, buffer[MAXCHARS + 2], *script; -char *input(void) { +INPUT(stringinput) { + char *start; + size_t l; + + if (!*string) return NULL; + start = string; + while (*string && *string != '\n') ++string; + l = string - start; + if (*string == '\n') ++string; + if (l > MAXCHARS) fatal("Line too long, exceeds %d character limit", MAXCHARS); + strncpy(buffer, start, l); + buffer[l] = ';'; + buffer[l + 1] = '\0'; + + return buffer; +} + +INPUT(scriptinput) { + int fd; + struct stat sstat; + char *result; + static char *map; + static size_t l; + + if (!map) { + if ((fd = open(script, O_RDONLY)) == -1) fatal("Unable to open `%s'", script); + if (stat(script, &sstat) == -1) fatal("Unable to stat `%s'", script); + if ((l = sstat.st_size) == 0) return NULL; + if ((map = string = mmap(NULL, l, PROT_READ, MAP_PRIVATE, fd, 0)) + == MAP_FAILED) + fatal("Unable to memory map `%s'", script); + if (close(fd) == -1) fatal("Unable to close `%s'", script); + } + + if (!(result = stringinput())) { + if (munmap(map, l) == -1) fatal("Unable to unmap %s from memory", script); + map = NULL; + } + + return result; +} + +char *config(char *name) { + char *result; + static char *origscript, *origstr; + + if (!origscript) { + origscript = script; + origstr = string; + script = prependhome(name); + } + + if (!(result = scriptinput())) { + script = origscript; + string = origstr; + origscript = NULL; + } + + return result; +} + +static void waitbgsig(int sig) { + (void)sig; + waitbg(); +} + +INPUT(userinput) { char *cursor, *end; unsigned int c; int i; - signal(SIGCHLD, waitbg); // TODO: Use sigaction for portability + signal(SIGCHLD, waitbgsig); // TODO: Use sigaction for portability end = cursor = buffer; *history.t = *buffer = '\0'; @@ -46,7 +116,7 @@ char *input(void) { while ((c = getchar()) != '\r') switch (c) { default: if (c >= ' ' && c <= '~') { - if (end - buffer == BUFLEN) continue; + if (end - buffer == MAXCHARS) continue; memmove(cursor + 1, cursor, end - cursor); *cursor++ = c; *++end = '\0'; @@ -62,7 +132,7 @@ char *input(void) { return buffer; case CTRLD: puts("^D\r"); - signal(SIGCHLD, SIG_DFL); + signal(SIGCHLD, SIG_DFL); // XXX return NULL; case CLEAR: fputs("\033[H\033[J", stdout); @@ -89,9 +159,10 @@ char *input(void) { for (i = end - buffer + strlen(PROMPT); i > 0; --i) putchar(' '); putchar('\r'); - if (strcmp(history.c, buffer) != 0) strcpy(history.t, buffer); + if (strcmp((char *)history.c, buffer) != 0) + strcpy((char *)history.t, buffer); if (c == UP) DEC(history, c); else INC(history, c); - strcpy(buffer, history.c); + strcpy(buffer, (char *)history.c); end = cursor = buffer + strlen(buffer); fputs(PROMPT, stdout); @@ -131,7 +202,7 @@ char *input(void) { *end++ = ';'; *end = '\0'; - signal(SIGCHLD, SIG_DFL); + signal(SIGCHLD, SIG_DFL); // XXX return buffer; } diff --git a/src/input.h b/src/input.h index 1ca304b..eae90c5 100644 --- a/src/input.h +++ b/src/input.h @@ -1,5 +1,8 @@ -#define BUFLEN 1000 +#define INPUT(name) char *name(void) -extern char buffer[BUFLEN + 2]; +extern char *string, buffer[MAXCHARS + 2], *script; -char *input(void); +INPUT(stringinput); +INPUT(scriptinput); +char *config(char *name); +INPUT(userinput); diff --git a/src/job.c b/src/job.c index 6573c4b..7c9bb09 100644 --- a/src/job.c +++ b/src/job.c @@ -1,17 +1,20 @@ -#include #include +#include +#include #include #include #include #include +#include +#include "config.h" #include "job.h" #include "stack.h" +#include "utils.h" -#define MAXJOBS 100 - -static struct job jobarray[MAXJOBS + 1]; -struct stack INITSTACK(jobs, jobarray, 0); +static struct job jobarr[MAXJOBS + 1]; +INITSTACK(jobs, jobarr, 0); +struct termios raw, canonical; void *findjob(pid_t jobid) { if (jobs.b == jobs.t) return NULL; @@ -26,17 +29,84 @@ void *deletejob(void) { return DEC(jobs, t); } -void waitbg(int sig) { +int setconfig(struct termios *mode) { + if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) { + note("Unable to set termios config"); + return 0; + } + return 1; +} + +int setfg(struct job job) { + if (!setconfig(&job.config)) return 0; + if (tcsetpgrp(STDIN_FILENO, job.id) == -1) { + note("Unable to bring job %d to foreground", job.id); + setconfig(&raw); + return 0; + } + if (killpg(job.id, SIGCONT) == -1) { + note("Unable to wake up job %d", job.id); + return 0; + } + return 1; +} + +int waitfg(struct job job) { + int status, pgid, result; + + do { + errno = 0; + waitpid(job.id, &status, WUNTRACED); + } while (errno == EINTR); + if (!errno && !WIFSTOPPED(status)) do { + errno = 0; + while (waitpid(-job.id, NULL, 0) != -1); + } while (errno == EINTR); + result = errno != ECHILD ? errno : 0; + + // TODO: Use sigaction >:( + if ((pgid = getpgid(0)) == -1 || signal(SIGTTOU, SIG_IGN) == SIG_ERR + || tcsetpgrp(STDIN_FILENO, pgid) == -1 + || signal(SIGTTOU, SIG_DFL) == SIG_ERR) { + note("Unable to reclaim foreground; terminating"); + deinitialize(); + exit(EXIT_FAILURE); + } + if (tcgetattr(STDIN_FILENO, &job.config) == -1) + note("Unable to save termios config of job %d", job.id); + setconfig(&raw); + if (result) return result; + + if (WIFSIGNALED(status)) { + result = WTERMSIG(status); + puts("\r"); + } else if (WIFSTOPPED(status)) { + result = WSTOPSIG(status); + job.type = SUSPENDED; + if (push(&jobs, &job)) return result; + note("Unable to add job %d to list; too many jobs\r\n" + "Press any key to continue", job.id); + getchar(); + if (setfg(job)) return waitfg(job); + note("Manual intervention required for job %d", job.id); + } else if (WIFEXITED(status)) result = WEXITSTATUS(status); + + return result; +} + +void waitbg(void) { int status; pid_t id; - (void)sig; for (jobs.c = jobs.b; jobs.c != jobs.t; INC(jobs, c)) { if (CURRENT->type != BACKGROUND) continue; id = CURRENT->id; + + // TODO: weird EINTR thing here too?? while ((id = waitpid(-id, &status, WNOHANG | WUNTRACED)) > 0) if (WIFSTOPPED(status)) CURRENT->type = SUSPENDED; + if (id == -1 && errno != ECHILD) - warn("Unable to wait on some child processes"); + note("Unable to wait on some child processes"); } } diff --git a/src/job.h b/src/job.h index 73b2ed1..12fa031 100644 --- a/src/job.h +++ b/src/job.h @@ -1,3 +1,5 @@ +#define CURRENT ((struct job *)jobs.c) + enum jobtype { BACKGROUND, SUSPENDED, @@ -9,9 +11,12 @@ struct job { enum jobtype type; }; -#define CURRENT ((struct job *)jobs.c) extern struct stack jobs; +extern struct termios raw, canonical; void *findjob(pid_t jobid); void *deletejob(void); -void waitbg(int sig); +int setconfig(struct termios *mode); +int setfg(struct job job); +int waitfg(struct job job); +void waitbg(void); diff --git a/src/main.c b/src/main.c index 9c05d5c..30a92fe 100644 --- a/src/main.c +++ b/src/main.c @@ -1,30 +1,23 @@ #include -#include -#include // XXX -#include "job.h" +#include "config.h" +#include "input.h" #include "options.h" +#include "parse.h" #include "run.h" -#include "term.h" +#include "utils.h" int main(int argc, char **argv) { - struct cmd *cmd; + options(&argc, &argv); - // TODO: Have `cd' builtin affect $PWD$ env var + initialize(); - options(&argc, &argv); + if (login) while (run(parse(config(LOGINFILE)))); + if (interactive) while (run(parse(config(INTERACTIVEFILE)))); + + while (run(parse(input()))); - 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(); + deinitialize(); return EXIT_SUCCESS; } diff --git a/src/options.c b/src/options.c index 3e07cc6..4536139 100644 --- a/src/options.c +++ b/src/options.c @@ -1,29 +1,27 @@ +#include #include -#include #include -#include -#include -int login; -char *string; +#include "config.h" +#include "input.h" +#include "options.h" + +int login, interactive; +Input input; void options(int *argcp, char ***argvp) { int opt, l; char *usage = "TODO: WRITE USAGE"; login = ***argvp == '-'; + interactive = 1; + input = userinput; - // -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'; + interactive = 0; + input = stringinput; + string = optarg; break; case 'h': errx(EXIT_SUCCESS, "%s", usage); @@ -37,5 +35,11 @@ void options(int *argcp, char ***argvp) { errx(EXIT_FAILURE, "Unknown command line option `-%c'\n%s", optopt, usage); } *argcp -= optind; - *argvp+= optind; + *argvp += optind; + + if (!string && **argvp) { + interactive = 0; + input = scriptinput; + script = **argvp; + } } diff --git a/src/options.h b/src/options.h index 6bb8fa6..724c3aa 100644 --- a/src/options.h +++ b/src/options.h @@ -1,4 +1,6 @@ -extern int login; -extern char *string; +typedef INPUT((*Input)); + +extern int login, interactive; +extern Input input; void options(int *argcp, char ***argvp); diff --git a/src/lex.c b/src/parse.c similarity index 78% rename from src/lex.c rename to src/parse.c index 29410df..7a93f74 100644 --- a/src/lex.c +++ b/src/parse.c @@ -1,25 +1,21 @@ -#include -#include #include -#include #include #include -#include // XXX +#include "config.h" #include "input.h" -#include "lex.h" +#include "parse.h" +#include "utils.h" -#define MAXCMDS 100 - -static char *tokens[BUFLEN + 1]; +static char *tokens[MAXCHARS + 1]; static struct cmd cmds[MAXCMDS + 1]; struct cmd empty = {0}; -struct cmd *lex(char *b) { - char **t, *end, *p, *env, *name, *value; - int e, offset; +struct cmd *parse(char *b) { + char **t, *name, *value, *end, *p, *env; struct cmd *c; long l; + int e, offset; if (!b) return NULL; t = tokens; @@ -38,7 +34,7 @@ struct cmd *lex(char *b) { if (*(b - 1)) { if (c->args == --t) c->args = NULL; if ((l = strtol(*t, &end, 10)) < 0 || l > INT_MAX || end != b) { - warnx("Invalid file redirection"); + note("Incorrect syntax for file redirection\r"); return ∅ } c->r->newfd = l; @@ -60,31 +56,25 @@ struct cmd *lex(char *b) { if (*end == '\\') ++end; } if (!b) { - warnx("Open-ended quote"); + note("Quote left open-ended\r"); return ∅ } - - // "..."...\0 - // ^ ^ ^ - // p b end memmove(p, p + 1, end-- - p); --b; - // ..."...\0 - // ^ ^ ^ - // p b end + while (p != b) if (*p++ == '\\') { switch (*p) { - case 'n': - *p = '\n'; - break; case 't': *p = '\t'; break; + case 'v': + *p = '\v'; + break; case 'r': *p = '\r'; break; - case 'v': - *p = '\v'; + case 'n': + *p = '\n'; break; } memmove(p - 1, p, end-- - p); @@ -105,17 +95,14 @@ struct cmd *lex(char *b) { p = b++; while (*b && *b != '$') ++b; if (!*b) { - warnx("Open-ended environment variable"); + note("Environment variable lacks a terminating `$'\r"); return ∅ } *b++ = '\0'; for (end = b; *end; ++end); - // $...\0...\0 - // ^ ^ ^ - // p b end if ((env = getenv(p + 1)) == NULL) { - warnx("Unknown environment variable"); + note("Environment variable does not exist\r"); return ∅ } e = strlen(env); @@ -132,7 +119,7 @@ struct cmd *lex(char *b) { case ';': if (name && *c->args == name) c->args = NULL; if (c->args) { - if ((c->term = *b) == *(b + 1) && *b == '&' || *b == '|') { + if ((c->term = *b) == *(b + 1) && (*b == '&' || *b == '|')) { ++c->term; *b++ = '\0'; } @@ -140,7 +127,7 @@ struct cmd *lex(char *b) { c->r->mode = END; for (c->r = c->rds; c->r->mode; ++c->r) if (*c->r->oldname == '&') { if ((l = strtol(++c->r->oldname, &end, 10)) < 0 || l > INT_MAX || *end) { - warnx("Invalid file redirection"); + note("Incorrect syntax for file redirection\r"); return ∅ } c->r->oldfd = l; @@ -155,7 +142,7 @@ struct cmd *lex(char *b) { *b = '\0'; if (value) { if (setenv(name, value, 1) == -1) { - warn("Unable to set environment variable"); + note("Unable to set environment variable\r"); return ∅ } value = name = NULL; @@ -166,7 +153,7 @@ struct cmd *lex(char *b) { case AND: case PIPE: case OR: - warnx("Command left open-ended"); + note("Expected another command\r"); return ∅ default: break; diff --git a/src/lex.h b/src/parse.h similarity index 89% rename from src/lex.h rename to src/parse.h index 21aace6..4d1a430 100644 --- a/src/lex.h +++ b/src/parse.h @@ -20,7 +20,6 @@ enum terminator { OR, }; -#define MAXRDS 25 struct cmd { char **args; struct redirect *r, rds[MAXRDS + 1]; @@ -30,4 +29,4 @@ struct cmd { extern struct cmd empty; -struct cmd *lex(char *b); +struct cmd *parse(char *b); diff --git a/src/run.c b/src/run.c index 4e1d1b4..a93f65f 100644 --- a/src/run.c +++ b/src/run.c @@ -1,89 +1,66 @@ -#include #include #include -#include // DEBUG #include -#include #include -#include -#include -#include #include #include #include "builtins.h" -#include "history.h" -#include "input.h" +#include "config.h" #include "job.h" -#include "lex.h" +#include "parse.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; - } + result = close(cmd->pipe[0]) == 0; + result &= close(cmd->pipe[1]) == 0; + if (!result) note("Unable to close `%s' pipe", *cmd->args); return result; } -static int redirectfiles(struct redirect *r) { - int oflag, fd; +static void redirectfiles(struct redirect *r) { + int mode, fd; for (; r->mode; ++r) { if (r->oldname) { switch (r->mode) { case READ: - oflag = O_RDONLY; + mode = O_RDONLY; break; case WRITE: - oflag = O_WRONLY | O_CREAT | O_TRUNC; + mode = O_WRONLY | O_CREAT | O_TRUNC; break; case READWRITE: - oflag = O_RDWR | O_CREAT | O_APPEND; + mode = O_RDWR | O_CREAT | O_APPEND; break; case APPEND: - oflag = O_WRONLY | O_CREAT | O_APPEND; + mode = O_WRONLY | O_CREAT | O_APPEND; + default: break; - default:; - } - if ((fd = open(r->oldname, oflag, 0644)) == -1) { - warn("Unable to open `%s'", r->oldname); - return 0; } + if ((fd = open(r->oldname, mode, 0644)) == -1) + fatal("Unable to open `%s'", r->oldname); 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; - } - } + if (dup2(r->oldfd, r->newfd) == -1) + fatal("Unable to redirect %d to %d", r->newfd, r->oldfd); + if (r->oldname && close(r->oldfd) == -1) + fatal("Unable to close `%s'", r->oldname); } - return 1; } -static void runcmd(struct cmd *cmd) { +int run(struct cmd *cmd) { struct cmd *prev; int ispipe, ispipestart, ispipeend; static int status; pid_t cpid, jobid; struct job job; + if (!cmd) return 0; + for (prev = ∅ cmd->args; prev = cmd++) { ispipe = cmd->term == PIPE || prev->term == PIPE; ispipestart = ispipe && prev->term != PIPE; @@ -91,31 +68,29 @@ static void runcmd(struct cmd *cmd) { if (ispipe) { if (!ispipeend && pipe(cmd->pipe) == -1) { - warn("Unable to create pipe"); + note("Unable to create pipe"); if (!ispipestart) closepipe(prev); break; } if ((jobid = cpid = fork()) == -1) { - warn("Unable to create child process"); + note("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); + fatal("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); + fatal("Unable to duplicate write end of `%s' pipe", *cmd->args); if (!closepipe(cmd)) exit(EXIT_FAILURE); } - if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE); + redirectfiles(cmd->rds); 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); + fatal("Couldn't find `%s' command", *cmd->args); } if (!ispipestart) { closepipe(prev); @@ -124,108 +99,40 @@ static void runcmd(struct cmd *cmd) { } else { if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break; if ((jobid = cpid = fork()) == -1) { - warn("Unable to create child process"); + note("Unable to create child process"); break; } else if (cpid == 0) { - if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE); + redirectfiles(cmd->rds); 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); + fatal("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); + note("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); + note("Unable to kill process %d; may need to manually terminate", 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"); + note("Unable to add job to background; too many background jobs"); 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; + waitbg(); - 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; - - readhist(); - while ((cmd = lex(input()))) runcmd(cmd); - writehist(); -} - -#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); + return 1; } diff --git a/src/run.h b/src/run.h index 87eb890..bf296d0 100644 --- a/src/run.h +++ b/src/run.h @@ -1,6 +1 @@ -char *prependhome(char *filename); - -void runstr(char *str); -void runscript(char *filename); -void runinteractive(void); -void runhome(char *filename); +int run(struct cmd *cmd); diff --git a/src/stack.c b/src/stack.c index 4d21bd7..a5df54e 100644 --- a/src/stack.c +++ b/src/stack.c @@ -1,18 +1,16 @@ #include +#include #include "stack.h" int push(struct stack *s, void *d) { - int result; - - result = 0; if (PLUSONE(*s, t) == s->b) { - if (!s->overwrite) return result; + if (!s->overwrite) return 0; INC(*s, b); - } else result = 1; + } memmove(s->t, d, s->size); INC(*s, t); - return result; + return 1; } void *peek(struct stack *s) { diff --git a/src/stack.h b/src/stack.h index d33cbe5..e0dbe60 100644 --- a/src/stack.h +++ b/src/stack.h @@ -5,10 +5,12 @@ #define DEC(s, m) ((s).m = MINUSONE(s, m)) #define INITSTACK(s, d, o) \ - s = {sizeof*d, sizeof d, (char *)d, (char *)d, (char *)d, (char *)d, o} + struct stack s = {sizeof*d, sizeof d, (uint8_t *)d, (uint8_t *)d, \ + (uint8_t *)d, (uint8_t *)d, o} + struct stack { - size_t size, cap; // In bytes - char *b, *c, *t, *data; + size_t size, cap; + uint8_t *b, *c, *t, *data; int overwrite; }; diff --git a/src/term.c b/src/term.c deleted file mode 100644 index f7bd4a7..0000000 --- a/src/term.c +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "job.h" -#include "history.h" -#include "stack.h" - -struct termios raw, canonical; - -static int setconfig(struct termios *mode) { - if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) { - warn("Unable to set termios config"); - return 0; - } - return 1; -} - -void initterm(void) { - cfmakeraw(&raw); - if (tcgetattr(STDIN_FILENO, &canonical) == -1) - err(EXIT_FAILURE, "Unable to get default termios config"); - if (!setconfig(&raw)) exit(EXIT_FAILURE); -} - -void deinitterm(void) { - setconfig(&canonical); -} - -int setfg(struct job job) { - if (!setconfig(&job.config)) return 0; - if (tcsetpgrp(STDIN_FILENO, job.id) == -1) { - warn("Unable to bring job %d to foreground\r", job.id); - setconfig(&raw); - return 0; - } - if (killpg(job.id, SIGCONT) == -1) { - warn("Unable to wake up job %d\r", job.id); - return 0; - } - return 1; -} - -int waitfg(struct job job) { - int status, pgid, result; - - errno = 0; - do waitpid(job.id, &status, WUNTRACED); while (errno == EINTR); - if (!errno && !WIFSTOPPED(status)) - do while (waitpid(-job.id, NULL, 0) != -1); while (errno == EINTR); - result = errno; - - // TODO: Use sigaction >:( - if ((pgid = getpgid(0)) == -1 || signal(SIGTTOU, SIG_IGN) == SIG_ERR - || tcsetpgrp(STDIN_FILENO, pgid) == -1 - || signal(SIGTTOU, SIG_DFL) == SIG_ERR) { - warn("Unable to reclaim foreground; terminating\r"); - writehist(); - deinitterm(); - exit(EXIT_FAILURE); - } - if (tcgetattr(STDIN_FILENO, &job.config) == -1) - warn("Unable to save termios config of job %d\r", job.id); - setconfig(&raw); - if (result) return 1; - - if (WIFSIGNALED(status)) { - result = WTERMSIG(status); - puts("\r"); - } else if (WIFSTOPPED(status)) { - result = WSTOPSIG(status); - job.type = SUSPENDED; - if (push(&jobs, &job)) return result; - warnx("Unable to add job %d to list; too many jobs\r\n" - "Press any key to continue\r", job.id); - getchar(); - if (setfg(job)) return waitfg(job); - warnx("Manual intervention required for job %d\r", job.id); - } else if (WIFEXITED(status)) result = WEXITSTATUS(status); - - return result; -} diff --git a/src/term.h b/src/term.h deleted file mode 100644 index 3bc6806..0000000 --- a/src/term.h +++ /dev/null @@ -1,6 +0,0 @@ -extern struct termios raw, canonical; - -void initterm(void); -void deinitterm(void); -int setfg(struct job job); -int waitfg(struct job job); diff --git a/src/utils.c b/src/utils.c index 07327e4..f943c20 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,12 +1,61 @@ #include +#include +#include #include #include +#include +#include +#include -void *allocate(size_t s) { - void *r; +#include "config.h" +#include "history.h" +#include "input.h" +#include "job.h" +#include "options.h" +#include "utils.h" - if (!(r = malloc(s))) err(EXIT_FAILURE, "Memory allocation"); +void note(char *fmt, ...) { + va_list args; + va_start(args, fmt); + (errno ? vwarn : vwarnx)(fmt, args); + va_end(args); + putchar('\r'); +} + +void fatal(char *fmt, ...) { + va_list args; + va_start(args, fmt); + (errno ? vwarn : vwarnx)(fmt, args); + va_end(args); + putchar('\r'); + exit(EXIT_FAILURE); +} + +char *prependhome(char *name) { + static char *p, path[MAXPATH + 1]; + + if (!p) { + if (!(p = getenv("HOME"))) + fatal("Unable to access $HOME$ environment variable"); + strcpy(path, p); + strcat(path, "/"); + p = path + strlen(path); + } + *p = '\0'; + strcat(path, name); - return memset(r, 0, s); + return path; } +void initialize(void) { // <-- TODO: Set $SHLVL$ in this function + cfmakeraw(&raw); + if (tcgetattr(STDIN_FILENO, &canonical) == -1) + fatal("Unable to get default termios config"); + if (!setconfig(&raw)) exit(EXIT_FAILURE); + if (interactive) readhistory(); +} + +void deinitialize(void) { + if (interactive) writehistory(); + setconfig(&canonical); +} diff --git a/src/utils.h b/src/utils.h index 8954d59..a78c43b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1 +1,5 @@ -void *allocate(size_t s); +void note(char *fmt, ...); +void fatal(char *fmt, ...); +char *prependhome(char *name); +void initialize(void); +void deinitialize(void); -- 2.51.0