From: Trent Huber Date: Mon, 11 Aug 2025 03:07:40 +0000 (-0400) Subject: History, jobs, and aliases use their own data structures X-Git-Url: https://trenthuber.com/code?a=commitdiff_plain;h=98b0a4645da4e34a8defd8e7abdf335a16c08efb;p=thus.git History, jobs, and aliases use their own data structures --- diff --git a/src/build.c b/src/build.c index 8f2965f..2f0999e 100644 --- a/src/build.c +++ b/src/build.c @@ -17,8 +17,7 @@ int main(void) { {"options", NONE}, {"parse", NONE}, {"run", BUILTINS}, - {"stack", NONE}, - {"utils", NONE}, + {"utils", BUILTINS}, {"builtin.a"}, diff --git a/src/builtin/alias.c b/src/builtin/alias.c index 716be35..22d5723 100644 --- a/src/builtin/alias.c +++ b/src/builtin/alias.c @@ -1,31 +1,30 @@ +#include #include #include -#include #include "builtin.h" -#include "utils.h" -#include "stack.h" #include "input.h" -#include "shell.h" +#include "context.h" #include "parse.h" +#include "utils.h" #define MAXALIAS 25 -#define ALIAS ((struct alias *)aliases.c) -struct alias { - char lhs[MAXCHARS - 5], *rhs; - struct shell shell; -}; +static struct { + struct { + char lhs[MAXCHARS - 5], *rhs; + struct context context; + } entries[MAXALIAS + 1]; + size_t size; +} aliases; -static struct alias aliasarr[MAXALIAS + 1]; -static INITSTACK(aliases, aliasarr, 0); - -void applyaliases(struct shell *shell) { - struct cmd *cmd, *p; +void applyaliases(struct cmd *cmd) { + struct cmd *p; char **end; - size_t l, a; + size_t i, l, a; + struct context *context; - p = cmd = shell->cmds; + p = cmd; end = p->args; while ((p = p->next)) if (p->args) end = p->args; @@ -33,44 +32,53 @@ void applyaliases(struct shell *shell) { while ((p = cmd = cmd->next)) { if (!cmd->args) continue; - for (aliases.c = aliases.b; aliases.c != aliases.t; INC(aliases, c)) - if (strcmp(ALIAS->lhs, *cmd->args) == 0) break; - if (aliases.c == aliases.t) continue; + for (i = 0; i < aliases.size; ++i) + if (strcmp(aliases.entries[i].lhs, *cmd->args) == 0) break; + if (i == aliases.size) continue; + context = &aliases.entries[i].context; - strcpy(ALIAS->shell.buffer, ALIAS->rhs); - l = strlen(ALIAS->shell.buffer); - ALIAS->shell.buffer[l + 1] = '\0'; - ALIAS->shell.buffer[l] = ';'; + strcpy(context->buffer, aliases.entries[i].rhs); + l = strlen(context->buffer); + context->buffer[l + 1] = '\0'; + context->buffer[l] = ';'; - parse(&ALIAS->shell); + parse(context); - for (a = 0; ALIAS->shell.tokens[a]; ++a); + for (a = 0; context->tokens[a]; ++a); memmove(cmd->args + a, cmd->args + 1, (end - cmd->args + 1) * sizeof*cmd->args); - memcpy(cmd->args, ALIAS->shell.tokens, a * sizeof*cmd->args); + memcpy(cmd->args, context->tokens, a * sizeof*cmd->args); while ((p = p->next)) p->args += a - 1; } } BUILTINSIG(alias) { + size_t i; + char *lhs; + switch (argc) { case 1: - for (aliases.c = aliases.b; aliases.c != aliases.t; INC(aliases, c)) - printf("%s = \"%s\"\r\n", ALIAS->lhs, ALIAS->rhs); + for (i = 0; i < aliases.size; ++i) + printf("%s = \"%s\"\r\n", aliases.entries[i].lhs, aliases.entries[i].rhs); break; case 3: + if (aliases.size == MAXALIAS) { + note("Unable to add alias `%s', maximum reached (%d)", argv[1], MAXALIAS); + return EXIT_FAILURE; + } if (!*argv[2]) { note("Cannot add empty alias"); return EXIT_FAILURE; } - for (aliases.c = aliases.b; aliases.c != aliases.t; INC(aliases, c)) - if (strcmp(ALIAS->lhs, argv[1]) == 0) break; - if (PLUSONE(aliases, c) == aliases.b) { - note("Unable to add alias `%s', maximum reached (%d)", argv[1], MAXALIAS); - return EXIT_FAILURE; + for (i = 0; i < aliases.size; ++i) + if (strcmp(aliases.entries[i].lhs, argv[1]) == 0) break; + + lhs = aliases.entries[i].lhs; + if (i == aliases.size) { + strcpy(lhs, argv[1]); + ++aliases.size; } - strcpy(ALIAS->lhs, argv[1]); - strcpy(ALIAS->rhs = ALIAS->lhs + strlen(ALIAS->lhs) + 1, argv[2]); - if (aliases.c == aliases.t) INC(aliases, t); + strcpy(aliases.entries[i].rhs = lhs + strlen(lhs) + 1, argv[2]); + break; default: puts("Usage: alias [lhs rhs]\r"); diff --git a/src/builtin/alias.h b/src/builtin/alias.h index 540e935..02b4643 100644 --- a/src/builtin/alias.h +++ b/src/builtin/alias.h @@ -1 +1 @@ -void applyaliases(struct shell *shell); +void applyaliases(struct cmd *cmd); diff --git a/src/builtin/bg.c b/src/builtin/bg.c index 8e90c58..09c483c 100644 --- a/src/builtin/bg.c +++ b/src/builtin/bg.c @@ -6,44 +6,34 @@ #include "builtin.h" #include "job.h" -#include "stack.h" #include "utils.h" BUILTINSIG(bg) { - long jobid; + long l; + pid_t id; struct job *job; - if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) { - note("Unable to acquire lock on the job stack"); - return EXIT_FAILURE; - } - - if (argv[1]) { + if (argc > 1) { errno = 0; - if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno - || jobid <= 0) { - note("Invalid job id"); + if ((l = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || l <= 0) { + note("Invalid job id %ld", l); return EXIT_FAILURE; } - if (!(job = findjob((pid_t)jobid))) { - note("Unable to find job %d", (pid_t)jobid); + if (!(job = searchjobid(id = (pid_t)l))) { + note("Unable to find job %d", id); return EXIT_FAILURE; } if (job->type == BACKGROUND) { - note("Job %d already in background", (pid_t)jobid); - return EXIT_FAILURE; - } - } else { - for (jobs.c = MINUSONE(jobs, t); jobs.c != MINUSONE(jobs, b); DEC(jobs, c)) - if (JOB->type == SUSPENDED) break; - if (jobs.c == MINUSONE(jobs, b)) { - note("No suspended jobs to run in background"); + note("Job %d already in background", id); return EXIT_FAILURE; } + } else if (!(job = searchjobtype(SUSPENDED))) { + note("No suspended jobs to run in background"); + return EXIT_FAILURE; } - job = deletejob(); + deletejobid(job->id); - if (!(push(&jobs, job))) { + if (!pushjob(job)) { note("Unable to add job to background; too many jobs"); return EXIT_FAILURE; } @@ -53,10 +43,5 @@ BUILTINSIG(bg) { } job->type = BACKGROUND; - if (sigaction(SIGCHLD, &sigchld, NULL) == -1) { - note("Unable to install SIGCHLD handler"); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; } diff --git a/src/builtin/builtin.c b/src/builtin/builtin.c index 03731af..08904e0 100644 --- a/src/builtin/builtin.c +++ b/src/builtin/builtin.c @@ -2,15 +2,16 @@ #include "builtin.h" #include "list.h" +#include "utils.h" -int isbuiltin(char **args, int *statusp) { +int isbuiltin(char **args) { struct builtin *builtinp; size_t n; for (builtinp = builtins; builtinp->func; ++builtinp) if (strcmp(*args, builtinp->name) == 0) { for (n = 0; args[n]; ++n); - *statusp = builtinp->func(n, args); + status = builtinp->func(n, args); return 1; } return 0; diff --git a/src/builtin/builtin.h b/src/builtin/builtin.h index cc96b3a..07fc387 100644 --- a/src/builtin/builtin.h +++ b/src/builtin/builtin.h @@ -1,3 +1,3 @@ #define BUILTINSIG(name) int name(int argc, char **argv) -int isbuiltin(char **args, int *statusp); +int isbuiltin(char **args); diff --git a/src/builtin/cd.c b/src/builtin/cd.c index 4eb01fe..0986e25 100644 --- a/src/builtin/cd.c +++ b/src/builtin/cd.c @@ -1,7 +1,5 @@ #include -#include #include -#include #include "builtin.h" #include "utils.h" diff --git a/src/builtin/fg.c b/src/builtin/fg.c index 6a4958c..c471f46 100644 --- a/src/builtin/fg.c +++ b/src/builtin/fg.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -8,9 +9,47 @@ #include "builtin.h" #include "job.h" -#include "stack.h" #include "utils.h" +static int fgstatus; +struct sigaction sigchld, sigdfl; +struct termios raw, canonical; + +static int setconfig(struct termios *mode) { + if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) { + note("Unable to set termios config"); + return 0; + } + return 1; +} + +static void sigchldhandler(int sig) { + pid_t id; + struct job *job; + + (void)sig; + while ((id = waitpid(-1, &fgstatus, WNOHANG | WUNTRACED)) > 0) + if ((job = searchjobid(id))) { + if (WIFSTOPPED(fgstatus)) job->type = SUSPENDED; else deletejobid(id); + } else if (!WIFSTOPPED(fgstatus)) while (waitpid(-id, NULL, 0) != -1); +} + +void setsigchld(struct sigaction *act) { + if (sigaction(SIGCHLD, act, NULL) == -1) + fatal("Unable to install SIGCHLD handler"); +} + +void initterm(void) { + if (tcgetattr(STDIN_FILENO, &canonical) == -1) + err(EXIT_FAILURE, "Unable to get default termios config"); + cfmakeraw(&raw); + if (!setconfig(&raw)) exit(EXIT_FAILURE); + + sigchld.sa_handler = sigchldhandler; + sigdfl.sa_handler = SIG_DFL; + setsigchld(&sigchld); +} + int setfg(struct job job) { if (!setconfig(&job.config)) return 0; if (tcsetpgrp(STDIN_FILENO, job.id) == -1) { @@ -25,11 +64,18 @@ int setfg(struct job job) { return 1; } -int waitfg(struct job job) { +void waitfg(struct job job) { + struct sigaction sigign; + + /* SIGCHLD handler is really the function that reaps the foreground process, + * the waitpid() below is just to block the current thread of execution until + * the foreground process has been reaped */ + setsigchld(&sigchld); while (waitpid(job.id, NULL, 0) != -1); errno = 0; + setsigchld(&sigdfl); - if (sigaction(SIGTTOU, &sigign, NULL) == -1 + if (sigaction(SIGTTOU, &(struct sigaction){{SIG_IGN}}, NULL) == -1 || tcsetpgrp(STDIN_FILENO, getpgrp()) == -1 || sigaction(SIGTTOU, &sigdfl, NULL) == -1) { note("Unable to reclaim foreground; terminating"); @@ -40,52 +86,45 @@ int waitfg(struct job job) { note("Unable to save termios config of job %d", job.id); setconfig(&raw); - if (WIFEXITED(fgstatus)) return WEXITSTATUS(fgstatus); - else if (WIFSIGNALED(fgstatus)) return WTERMSIG(fgstatus); - job.type = SUSPENDED; - if (!push(&jobs, &job)) { - 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); + if (WIFEXITED(fgstatus)) status = WEXITSTATUS(fgstatus); + else if (WIFSIGNALED(fgstatus)) status = WTERMSIG(fgstatus); + else { + status = WSTOPSIG(fgstatus); + job.type = SUSPENDED; + if (!pushjob(&job)) { + 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); + } } - return WSTOPSIG(fgstatus); +} + +void deinitterm(void) { + setconfig(&canonical); } BUILTINSIG(fg) { - long jobid; + long l; + pid_t id; struct job *job; - if (sigaction(SIGCHLD, &sigdfl, NULL) == -1) { - note("Unable to acquire lock on the job stack"); - return EXIT_FAILURE; - } - - if (argv[1]) { + if (argc > 1) { errno = 0; - if ((jobid = strtol(argv[1], NULL, 10)) == LONG_MAX && errno - || jobid <= 0) { - note("Invalid process group id"); + if ((l = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || l <= 0) { + note("Invalid process group id %ld", l); return EXIT_FAILURE; } - if (!(job = findjob((pid_t)jobid))) { - note("Unable to find process group %d", (pid_t)jobid); + if (!(job = searchjobid(id))) { + note("Unable to find process group %d", id); return EXIT_FAILURE; } - job = deletejob(); - } else if (!(job = pull(&jobs))) { + deletejobid(id); + } else if (!(job = pulljob())) { note("No processes to bring into the foreground"); return EXIT_FAILURE; } - if (sigaction(SIGCHLD, &sigchld, NULL) == -1) { - note("Unable to reinstall SIGCHLD handler"); - return EXIT_FAILURE; - } - - if (!setfg(*job)) return EXIT_FAILURE; - waitfg(*job); - - return EXIT_SUCCESS; + return setfg(*job) ? (waitfg(*job), EXIT_SUCCESS) : EXIT_FAILURE; } diff --git a/src/builtin/fg.h b/src/builtin/fg.h index 00b6eaa..da4eff7 100644 --- a/src/builtin/fg.h +++ b/src/builtin/fg.h @@ -1,2 +1,8 @@ +extern struct sigaction sigchld, sigdfl; +extern struct termios raw, canonical; + +void setsigchld(struct sigaction *act); +void initterm(void); int setfg(struct job job); -int waitfg(struct job job); +void waitfg(struct job job); +void deinitterm(void); diff --git a/src/builtin/source.c b/src/builtin/source.c index de2f166..90f15cc 100644 --- a/src/builtin/source.c +++ b/src/builtin/source.c @@ -1,23 +1,27 @@ +#include #include #include -#include +#include "builtin.h" #include "input.h" -#include "shell.h" +#include "context.h" #include "parse.h" #include "run.h" #include "utils.h" -#include "builtin.h" BUILTINSIG(source) { - struct shell shell; + struct context context; - shell.script = argv[1]; - shell.input = scriptinput; + if (argc != 2) { + note("Usage: source file"); + return EXIT_FAILURE; + } - while (run(parse(shell.input(&shell)))); + context.script = argv[1]; + context.input = scriptinput; + while (run(parse(context.input(&context)))); - return EXIT_SUCCESS; // TODO: Handle exit status + return EXIT_SUCCESS; } void config(char *name) { diff --git a/src/builtin/which.c b/src/builtin/which.c index 85d4427..4d8c58a 100644 --- a/src/builtin/which.c +++ b/src/builtin/which.c @@ -1,9 +1,9 @@ +#include #include #include #include #include #include -#include #include "builtin.h" #include "list.h" diff --git a/src/shell.h b/src/context.h similarity index 97% rename from src/shell.h rename to src/context.h index a153acd..87e9d64 100644 --- a/src/shell.h +++ b/src/context.h @@ -32,7 +32,7 @@ struct cmd { struct cmd *prev, *next; }; -struct shell { +struct context { char buffer[MAXCHARS + 1 + 1], *tokens[MAXCMDS + 1], *script, *string; struct { char *m; diff --git a/src/history.c b/src/history.c index ecba616..093c95c 100644 --- a/src/history.c +++ b/src/history.c @@ -1,56 +1,66 @@ -#include -#include +#include #include +#include #include -#include #include #include "input.h" -#include "shell.h" -#include "stack.h" +#include "context.h" #include "utils.h" -#define HISTORYFILE ".ashhistory" #define MAXHIST 100 -#define BUFFER ((char *)history.t) - -static char historyarr[MAXHIST + 1][MAXCHARS + 1], filepath[PATH_MAX]; -INITSTACK(history, historyarr, 1); +#define INC(v) (history.v = (history.v + 1) % (MAXHIST + 1)) +#define DEC(v) (history.v = (history.v + MAXHIST) % (MAXHIST + 1)) -/* TODO - void addhistory(char *buffer); - int prevhistory(char *buffer); - int nexthistory(char *buffer); -*/ +static struct { + char path[PATH_MAX], entries[MAXHIST + 1][MAXCHARS + 1]; + size_t b, c, t; +} history; void readhistory(void) { - char *p; FILE *file; - if (!catpath(home, HISTORYFILE, filepath)) exit(EXIT_FAILURE); - if (!(file = fopen(filepath, "r"))) { + if (!catpath(home, ".ashhistory", history.path)) exit(EXIT_FAILURE); + if (!(file = fopen(history.path, "r"))) { if (errno == ENOENT) return; fatal("Unable to open history file for reading"); } - while (fgets(BUFFER, history.size, file)) { - *(BUFFER + strlen(BUFFER) - 1) = '\0'; - push(&history, BUFFER); + while (fgets(history.entries[history.t], sizeof*history.entries, file)) { + history.entries[history.t][strlen(history.entries[history.t]) - 1] = '\0'; + if ((history.c = INC(t)) == history.b) INC(b); } if (ferror(file) || !feof(file)) fatal("Unable to read from history file"); if (fclose(file) == EOF) fatal("Unable to close history file"); } +int gethistory(char direction, char *buffer) { + if (history.c == (direction == UP ? history.b : history.t)) return 0; + + // Save the most recently modified history entry at the top of the list + if (strcmp(history.entries[history.c], buffer) != 0) + strcpy(history.entries[history.t], buffer); + + strcpy(buffer, history.entries[direction == UP ? DEC(c) : INC(c)]); + + return 1; +} + +void sethistory(char *buffer) { + strcpy(history.entries[history.t], buffer); + if ((history.c = INC(t)) == history.b) INC(b); + *history.entries[history.t] = '\0'; +} + void writehistory(void) { - char *filename; FILE *file; - if (!(file = fopen(filepath, "w"))) { + if (!(file = fopen(history.path, "w"))) { note("Unable to open history file for writing"); return; } - for (history.c = history.b; history.c != history.t; INC(history, c)) { - if (fputs((char *)history.c, file) == EOF) { + for (history.c = history.b; history.c != history.t; INC(c)) { + if (fputs(history.entries[history.c], file) == EOF) { note("Unable to write to history file"); break; } diff --git a/src/history.h b/src/history.h index 22d30e6..60b4bac 100644 --- a/src/history.h +++ b/src/history.h @@ -1,5 +1,4 @@ -extern struct stack history; - void readhistory(void); +int gethistory(char direction, char *buffer); +void sethistory(char *buffer); void writehistory(void); - diff --git a/src/input.c b/src/input.c index 7808220..559053f 100644 --- a/src/input.c +++ b/src/input.c @@ -7,89 +7,61 @@ #include #include -#include "input.h" -#include "shell.h" #include "history.h" -#include "stack.h" +#include "input.h" +#include "context.h" #include "utils.h" #define DEFAULTPROMPT ">" -enum { - CTRLC = '\003', - CTRLD, - BACKSPACE = '\010', - CLEAR = '\014', - ESCAPE = '\033', - FORWARD = 'f', - BACKWARD = 'b', - ARROW = '[', - UP = 'A', - DOWN, - RIGHT, - LEFT, - DEL = '\177', -}; - INPUT(stringinput) { char *start; size_t l; - if (!*shell->string) { - if (shell->script && munmap(shell->map.m, shell->map.l) == -1) - note("Unable to unmap memory associated with `%s'", shell->script); + if (!*context->string) { + if (context->script && munmap(context->map.m, context->map.l) == -1) + note("Unable to unmap memory associated with `%s'", context->script); + return NULL; + } + start = context->string; + while (*context->string && *context->string != '\n') ++context->string; + l = context->string - start; + if (*context->string == '\n') ++context->string; + if (l > MAXCHARS) { + note("Line too long, exceeds %d character limit", MAXCHARS); return NULL; } - start = shell->string; - while (*shell->string && *shell->string != '\n') ++shell->string; - l = shell->string - start; - if (*shell->string == '\n') ++shell->string; -// puts("test\r\n"); - if (l > MAXCHARS) fatal("Line too long, exceeds %d character limit", MAXCHARS); - strncpy(shell->buffer, start, l); - shell->buffer[l] = ';'; - shell->buffer[l + 1] = '\0'; - - return shell; + strncpy(context->buffer, start, l); + context->buffer[l] = ';'; + context->buffer[l + 1] = '\0'; + + return context; } INPUT(scriptinput) { int fd; struct stat sstat; - if ((fd = open(shell->script, O_RDONLY)) == -1) - fatal("Unable to open `%s'", shell->script); - if (stat(shell->script, &sstat) == -1) - fatal("Unable to stat `%s'", shell->script); - if ((shell->map.l = sstat.st_size) == 0) return NULL; - if ((shell->string = shell->map.m = mmap(NULL, shell->map.l, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) - fatal("Unable to memory map `%s'", shell->script); - if (close(fd) == -1) fatal("Unable to close `%s'", shell->script); - shell->input = stringinput; - - return shell->input(shell); -} - -static struct shell *config(char *name) { - int fd; - struct shell *result; - static struct shell shell; - static char path[PATH_MAX]; - - if (!shell.script) { - shell.script = catpath(home, name, path); - shell.input = scriptinput; - - if ((fd = open(shell.script, O_RDONLY | O_CREAT, 0644)) == -1) - fatal("Unable to open `%s'", shell.script); - if (close(fd) == -1) - fatal("Unable to close `%s'", shell.script); + if ((fd = open(context->script, O_RDONLY)) == -1) { + note("Unable to open `%s'", context->script); + return NULL; } + if (stat(context->script, &sstat) == -1) { + note("Unable to stat `%s'", context->script); + return NULL; + } + if ((context->map.l = sstat.st_size) == 0) return NULL; + if ((context->string = context->map.m = mmap(NULL, context->map.l, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + note("Unable to memory map `%s'", context->script); + return NULL; + } + if (close(fd) == -1) { + note("Unable to close `%s'", context->script); + return NULL; + } + context->input = stringinput; - result = shell.input(&shell); - if (result) return result; - shell.script = NULL; - return NULL; + return context->input(context); } static size_t prompt(void) { @@ -103,15 +75,14 @@ static size_t prompt(void) { INPUT(userinput) { char *start, *cursor, *end; - size_t promptlen; unsigned int c; int i; + size_t oldlen, newlen; - end = cursor = start = shell->buffer; - *history.t = *start = '\0'; - history.c = history.t; + end = cursor = start = context->buffer; + *start = '\0'; while (start == end) { - promptlen = prompt(); + prompt(); while ((c = getchar()) != '\r') switch (c) { default: if (c >= ' ' && c <= '~') { @@ -128,7 +99,7 @@ INPUT(userinput) { case CTRLC: puts("^C\r"); *start = '\0'; - return shell; + return context; case CTRLD: puts("^D\r"); return NULL; @@ -151,20 +122,16 @@ INPUT(userinput) { switch ((c = getchar())) { case UP: case DOWN: - if (history.c == (c == UP ? history.b : history.t)) continue; + oldlen = strlen(start); + if (!gethistory(c, start)) continue; + newlen = strlen(start); + end = cursor = start + newlen; putchar('\r'); - for (i = end - start + promptlen; i > 0; --i) putchar(' '); - putchar('\r'); - - if (strcmp((char *)history.c, start) != 0) - strcpy((char *)history.t, start); - if (c == UP) DEC(history, c); else INC(history, c); - strcpy(start, (char *)history.c); - end = cursor = start + strlen(start); - prompt(); fputs(start, stdout); + for (i = oldlen - newlen; i > 0; --i) putchar(' '); + for (i = oldlen - newlen; i > 0; --i) putchar('\b'); break; case LEFT: if (cursor > start) putchar((--cursor, '\b')); @@ -194,10 +161,10 @@ INPUT(userinput) { } puts("\r"); } - push(&history, start); + sethistory(start); *end++ = ';'; *end = '\0'; - return shell; + return context; } diff --git a/src/input.h b/src/input.h index 50dc132..0b6dc8e 100644 --- a/src/input.h +++ b/src/input.h @@ -1,8 +1,23 @@ -#define INPUT(name) struct shell *name(struct shell *shell) +#define INPUT(name) struct context *name(struct context *context) typedef INPUT((*Input)); +enum { + CTRLC = '\003', + CTRLD, + BACKSPACE = '\010', + CLEAR = '\014', + ESCAPE = '\033', + FORWARD = 'f', + BACKWARD = 'b', + ARROW = '[', + UP = 'A', + DOWN, + RIGHT, + LEFT, + DEL = '\177', +}; + INPUT(stringinput); INPUT(scriptinput); -// struct shell *config(char *name); INPUT(userinput); diff --git a/src/job.c b/src/job.c index 857dfe4..3829255 100644 --- a/src/job.c +++ b/src/job.c @@ -1,31 +1,83 @@ -#include #include #include +// #include // XXX #include "job.h" -#include "stack.h" +#include "utils.h" #define MAXJOBS 100 -static struct job jobarr[MAXJOBS + 1]; -INITSTACK(jobs, jobarr, 0); -int fgstatus; - -/* TODO - addjob - popjob - findjob -*/ - -void *findjob(pid_t jobid) { - if (jobs.b == jobs.t) return NULL; - for (jobs.c = jobs.b; JOB->id != jobid; INC(jobs, c)) - if (jobs.c == jobs.t) return NULL; - return jobs.c; +struct joblink { + struct job job; + struct joblink *next; +}; + +static struct { + struct joblink entries[MAXJOBS + 1], *active, *free; +} jobs; + +struct job *pushjob(struct job *job) { + struct joblink *p; + + if (!jobs.free) return NULL; + + (p = jobs.free)->job = *job; + jobs.free = p->next; + p->next = jobs.active; + jobs.active = p; + + return &p->job; +} + +struct job *peekjob(void) { + return jobs.active ? &jobs.active->job : NULL; } -void *deletejob(void) { - memcpy(jobs.t, jobs.c, jobs.size); - memmove(jobs.c, PLUSONE(jobs, c), jobs.t - jobs.c); - return DEC(jobs, t); +struct job *pulljob(void) { + struct joblink *p; + + if (!jobs.active) return NULL; + + p = jobs.active; + jobs.active = p->next; + p->next = jobs.free; + jobs.free = p; + + return &p->job; +} + +struct job *searchjobid(pid_t id) { + struct joblink *p; + + for (p = jobs.active; p; p = p->next) if (p->job.id == id) return &p->job; + + return NULL; +} + +struct job *searchjobtype(enum jobtype type) { + struct joblink *p; + + for (p = jobs.active; p; p = p->next) if (p->job.type == type) return &p->job; + + return NULL; +} + +struct job *deletejobid(pid_t id) { + struct joblink *p, *prev; + + for (prev = NULL, p = jobs.active; p; prev = p, p = p->next) if (p->job.id == id) break; + if (!p) return NULL; + + if (prev) prev->next = p->next; else jobs.active = p->next; + p->next = jobs.free; + jobs.free = p; + + return &p->job; +} + +void initjobs(void) { + size_t i; + + for (i = 0; i < MAXJOBS - 1; ++i) jobs.entries[i].next = &jobs.entries[i + 1]; + jobs.free = jobs.entries; } diff --git a/src/job.h b/src/job.h index 1a6f288..f043286 100644 --- a/src/job.h +++ b/src/job.h @@ -1,5 +1,3 @@ -#define JOB ((struct job *)jobs.c) - enum jobtype { BACKGROUND, SUSPENDED, @@ -11,8 +9,10 @@ struct job { enum jobtype type; }; -extern struct stack jobs; -extern int fgstatus; - -void *findjob(pid_t jobid); -void *deletejob(void); +struct job *pushjob(struct job *job); +struct job *peekjob(void); +struct job *pulljob(void); +struct job *searchjobid(pid_t id); +struct job *searchjobtype(enum jobtype); +struct job *deletejobid(pid_t id); +void initjobs(void); diff --git a/src/main.c b/src/main.c index 12b278e..982704f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,12 @@ #include #include "input.h" -#include "shell.h" +#include "context.h" #include "options.h" #include "parse.h" #include "run.h" -#include "utils.h" #include "source.h" +#include "utils.h" int main(int c, char **v) { argc = c; @@ -19,7 +19,7 @@ int main(int c, char **v) { if (login) config(".ashlogin"); if (interactive) config(".ashint"); - while (run(parse(shell.input(&shell)))); + while (run(parse(context.input(&context)))); deinitialize(); diff --git a/src/options.c b/src/options.c index d54ca9b..eb0e6db 100644 --- a/src/options.c +++ b/src/options.c @@ -4,12 +4,12 @@ #include #include "input.h" -#include "shell.h" +#include "context.h" int login, interactive, argc; char **argv; -struct shell shell; +struct context context; static void usage(char *program, int code) { printf("Usage: %s [file] [-c string] [-hl]\n" @@ -25,14 +25,14 @@ void options(void) { login = **argv == '-'; interactive = 1; - shell.input = userinput; + context.input = userinput; while ((opt = getopt(argc, argv, ":c:hl")) != -1) { switch (opt) { case 'c': interactive = 0; - shell.string = optarg; - shell.input = stringinput; + context.string = optarg; + context.input = stringinput; break; case 'h': usage(*argv, EXIT_SUCCESS); @@ -49,10 +49,10 @@ void options(void) { } if (opt == 'c') break; } - if (!shell.string && argv[optind]) { + if (!context.string && argv[optind]) { interactive = 0; - shell.script = argv[optind]; - shell.input = scriptinput; + context.script = argv[optind]; + context.input = scriptinput; } if (!interactive) { argc -= optind; diff --git a/src/options.h b/src/options.h index 3186fad..022538f 100644 --- a/src/options.h +++ b/src/options.h @@ -1,5 +1,5 @@ extern int login, interactive, argc; extern char **argv; -extern struct shell shell; +extern struct context context; void options(void); diff --git a/src/parse.c b/src/parse.c index 37ad6eb..10ac748 100644 --- a/src/parse.c +++ b/src/parse.c @@ -4,9 +4,8 @@ #include #include "input.h" -#include "shell.h" +#include "context.h" #include "options.h" -#include "run.h" #include "utils.h" static void initcmd(struct cmd *cmd) { @@ -17,17 +16,17 @@ static void initcmd(struct cmd *cmd) { cmd->next = NULL; } -struct shell *parse(struct shell *shell) { +struct context *parse(struct context *context) { char *b, **t, *name, *value, *stlend, *p, *end, *env; struct cmd *c; long l; int e, offset; - if (!shell) return NULL; - b = shell->buffer; - t = shell->tokens; - c = shell->cmds + 1; - shell->cmds->next = NULL; + if (!context) return NULL; + b = context->buffer; + t = context->tokens; + c = context->cmds + 1; + context->cmds->next = NULL; *t = value = name = NULL; for (initcmd(c); *b; ++b) switch (*b) { default: @@ -41,14 +40,14 @@ struct shell *parse(struct shell *shell) { case '>': if (c->r->mode) { note("File redirections should be separated by spaces"); - return shell; + return context; } c->r->newfd = *b == '>'; if (*(b - 1)) { if (c->args == --t) c->args = NULL; if ((l = strtol(*t, &stlend, 10)) < 0 || l > INT_MAX || stlend != b) { note("Invalid value for a file redirection"); - return shell; + return context; } c->r->newfd = l; } @@ -70,7 +69,7 @@ struct shell *parse(struct shell *shell) { } if (!b) { note("Quote left open-ended"); - return shell; + return context; } memmove(p, p + 1, end-- - p); --b; @@ -109,21 +108,21 @@ struct shell *parse(struct shell *shell) { while (*b && *b != '$') ++b; if (!*b) { note("Environment variable lacks a terminating `$'"); - return shell; + return context; } *b++ = '\0'; for (end = b; *end; ++end); l = strtol(p + 1, &stlend, 10); if (stlend == b - 1) env = l >= 0 && l < argc ? argv[l] : b - 1; - else if (strcmp(p + 1, "?") == 0) { + else if (strcmp(p + 1, "^") == 0) { if (!sprintf(env = (char [12]){0}, "%d", status)) { note("Unable to get previous command status"); - return shell; + return context; } } else if ((env = getenv(p + 1)) == NULL) { note("Environment variable does not exist"); - return shell; + return context; } e = strlen(env); @@ -159,7 +158,7 @@ struct shell *parse(struct shell *shell) { if ((l = strtol(++c->r->oldname, &stlend, 10)) < 0 || l > INT_MAX || *stlend) { note("Incorrect syntax for file redirection"); - return shell; + return context; } c->r->oldfd = l; c->r->oldname = NULL; @@ -175,7 +174,7 @@ struct shell *parse(struct shell *shell) { if (value) { if (setenv(name, value, 1) == -1) { note("Unable to set environment variable"); - return shell; + return context; } value = name = NULL; } @@ -188,11 +187,11 @@ struct shell *parse(struct shell *shell) { case PIPE: case OR: note("Expected another command"); - return shell; + return context; default: break; } - shell->cmds->next = shell->cmds + 1; - return shell; + context->cmds->next = context->cmds + 1; + return context; } diff --git a/src/parse.h b/src/parse.h index 4053bf5..c76ce9f 100644 --- a/src/parse.h +++ b/src/parse.h @@ -1 +1 @@ -struct shell *parse(struct shell *shell); +struct context *parse(struct context *context); diff --git a/src/run.c b/src/run.c index f66e3ed..add19cc 100644 --- a/src/run.c +++ b/src/run.c @@ -1,23 +1,20 @@ #include +#include #include #include #include #include #include -#include -#include // XXX +// #include // XXX +#include "builtin.h" #include "input.h" -#include "shell.h" +#include "context.h" #include "alias.h" -#include "builtin.h" #include "job.h" #include "fg.h" -#include "stack.h" #include "utils.h" -int status; - static int closepipe(struct cmd *cmd) { int result; @@ -61,23 +58,26 @@ static void redirectfiles(struct redirect *r) { static void exec(struct cmd *cmd) { char cwd[PATH_MAX]; + redirectfiles(cmd->rds); + if (isbuiltin(cmd->args)) exit(status); execvp(*cmd->args, cmd->args); if (!getcwd(cwd, PATH_MAX)) fatal("Unable to check current working directory"); execvP(*cmd->args, cwd, cmd->args); fatal("Couldn't find `%s' command", *cmd->args); } -int run(struct shell *shell) { +int run(struct context *context) { struct cmd *cmd; int ispipe, ispipestart, ispipeend; pid_t cpid, jobid; struct job job; - if (!shell) return 0; + if (!context) return 0; - applyaliases(shell); + applyaliases(cmd = context->cmds); + + setsigchld(&sigdfl); - cmd = shell->cmds; while ((cmd = cmd->next)) { if (!cmd->args) { if (!cmd->r->mode) break; @@ -115,24 +115,17 @@ int run(struct shell *shell) { fatal("Unable to duplicate write end of `%s' pipe", *cmd->args); if (!closepipe(cmd)) exit(EXIT_FAILURE); } - - redirectfiles(cmd->rds); - if (isbuiltin(cmd->args, &status)) exit(status); exec(cmd); } if (!ispipestart) { closepipe(cmd->prev); - jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id; + jobid = (ispipeend ? pulljob : peekjob)()->id; } - } else if (!cmd->rds->mode && isbuiltin(cmd->args, &status)) cpid = 0; + } else if (!cmd->rds->mode && isbuiltin(cmd->args)) cpid = 0; else if ((jobid = cpid = fork()) == -1) { note("Unable to create child process"); break; - } else if (cpid == 0) { - redirectfiles(cmd->rds); - if (isbuiltin(cmd->args, &status)) exit(status); - exec(cmd); - } + } else if (cpid == 0) exec(cmd); if (cpid) { if (setpgid(cpid, jobid) == -1) { @@ -145,14 +138,14 @@ int run(struct shell *shell) { } job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND}; if (ispipestart || cmd->term == BG) { - if (!push(&jobs, &job)) { + if (!pushjob(&job)) { 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); + waitfg(job); } } @@ -160,5 +153,7 @@ int run(struct shell *shell) { if (cmd->term == OR && status == EXIT_SUCCESS) break; } + setsigchld(&sigchld); + return 1; } diff --git a/src/run.h b/src/run.h index cebcc90..4f79635 100644 --- a/src/run.h +++ b/src/run.h @@ -1,3 +1,3 @@ extern int status; -int run(struct shell *shell); +int run(struct context *context); diff --git a/src/stack.c b/src/stack.c deleted file mode 100644 index a5df54e..0000000 --- a/src/stack.c +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include - -#include "stack.h" - -int push(struct stack *s, void *d) { - if (PLUSONE(*s, t) == s->b) { - if (!s->overwrite) return 0; - INC(*s, b); - } - memmove(s->t, d, s->size); - INC(*s, t); - return 1; -} - -void *peek(struct stack *s) { - if (s->t == s->b) return NULL; - return MINUSONE(*s, t); -} - -void *pull(struct stack *s) { - if (s->t == s->b) return NULL; - return DEC(*s, t); -} diff --git a/src/stack.h b/src/stack.h deleted file mode 100644 index e0dbe60..0000000 --- a/src/stack.h +++ /dev/null @@ -1,19 +0,0 @@ -#define PLUSONE(s, m) ((s).data + ((s).m - (s).data + (s).size) % (s).cap) -#define MINUSONE(s, m) \ - ((s).data + ((s).m - (s).data - (s).size + (s).size * (s).cap) % (s).cap) -#define INC(s, m) ((s).m = PLUSONE(s, m)) -#define DEC(s, m) ((s).m = MINUSONE(s, m)) - -#define INITSTACK(s, 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; - uint8_t *b, *c, *t, *data; - int overwrite; -}; - -int push(struct stack *s, void *d); -void *peek(struct stack *s); -void *pull(struct stack *s); diff --git a/src/utils.c b/src/utils.c index 30e3799..dfcb36f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -10,14 +9,12 @@ #include #include "history.h" -#include "input.h" #include "job.h" +#include "fg.h" #include "options.h" -#include "stack.h" -struct termios raw, canonical; -struct sigaction sigchld, sigdfl, sigign; char *home; +int status; void note(char *fmt, ...) { va_list args; @@ -37,47 +34,11 @@ void fatal(char *fmt, ...) { exit(EXIT_FAILURE); } -int setconfig(struct termios *mode) { - if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) { - note("Unable to set termios config"); - return 0; - } - return 1; -} - -static void sigchldhandler(int sig) { - int status; - pid_t id; - - (void)sig; - while ((id = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { - for (jobs.c = jobs.b; jobs.c != jobs.t; INC(jobs, c)) - if (JOB->id == id) { - if (WIFSTOPPED(status)) JOB->type = SUSPENDED; - else deletejob(); - break; - } - if (jobs.c == jobs.t) { - fgstatus = status; - if (!WIFSTOPPED(fgstatus)) while (waitpid(-id, NULL, 0) != -1); - } - } -} - void initialize(void) { char *shlvlstr, buffer[19 + 1]; long shlvl; - if (tcgetattr(STDIN_FILENO, &canonical) == -1) - err(EXIT_FAILURE, "Unable to get default termios config"); - cfmakeraw(&raw); - if (!setconfig(&raw)) exit(EXIT_FAILURE); - - sigchld.sa_handler = sigchldhandler; - sigdfl.sa_handler = SIG_DFL; - sigign.sa_handler = SIG_IGN; - if (sigaction(SIGCHLD, &sigchld, NULL) == -1) - fatal("Unable to install SIGCHLD handler"); + initterm(); if (!(home = getenv("HOME"))) fatal("Unable to locate user's home directory, $HOME$ not set"); @@ -89,6 +50,7 @@ void initialize(void) { note("Unable to update $SHLVL$ environment variable"); if (interactive) readhistory(); + initjobs(); } char *catpath(char *dir, char *filename, char *buffer) { @@ -108,7 +70,7 @@ char *catpath(char *dir, char *filename, char *buffer) { } void deinitialize(void) { - if (interactive) writehistory(); + deinitterm(); - setconfig(&canonical); + if (interactive) writehistory(); } diff --git a/src/utils.h b/src/utils.h index 1cbc15e..3884832 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,10 +1,8 @@ -extern struct termios raw, canonical; -extern struct sigaction sigchld, sigdfl, sigign; extern char *home; +extern int status; void note(char *fmt, ...); void fatal(char *fmt, ...); -int setconfig(struct termios *mode); void initialize(void); char *catpath(char *dir, char *filename, char *buffer); void deinitialize(void);