From: Trent Huber Date: Thu, 7 Aug 2025 01:01:25 +0000 (-0400) Subject: shell struct, source builtin X-Git-Url: https://trenthuber.com/code?a=commitdiff_plain;h=dbb9a8f92447202c5ced08dcf1bb9caac5dfd521;p=thus.git shell struct, source builtin --- diff --git a/external/cbs b/external/cbs index 4711a83..5ab49da 160000 --- a/external/cbs +++ b/external/cbs @@ -1 +1 @@ -Subproject commit 4711a83fafa0282fadeaba701a25539b3e73d073 +Subproject commit 5ab49da8d4ab0b8e6cb23c3cb1f927e4402db4fc diff --git a/src/build.c b/src/build.c index 1f74e83..8f2965f 100644 --- a/src/build.c +++ b/src/build.c @@ -1,6 +1,8 @@ #include "../external/cbs/cbs.c" #include "../external/cbsfile.c" +#define BUILTINS LIST("-Ibuiltin/") + int main(void) { build("./"); @@ -8,13 +10,13 @@ int main(void) { buildfiles((struct cbsfile []){{"../bin/ash", NONE, 'x'}, - {"input", NONE}, {"history", NONE}, + {"input", NONE}, {"job", NONE}, - {"parse", NONE}, - {"main", NONE}, + {"main", BUILTINS}, {"options", NONE}, - {"run", LIST("-Ibuiltin/")}, + {"parse", NONE}, + {"run", BUILTINS}, {"stack", NONE}, {"utils", NONE}, diff --git a/src/builtin/alias.c b/src/builtin/alias.c new file mode 100644 index 0000000..716be35 --- /dev/null +++ b/src/builtin/alias.c @@ -0,0 +1,81 @@ +#include +#include +#include + +#include "builtin.h" +#include "utils.h" +#include "stack.h" +#include "input.h" +#include "shell.h" +#include "parse.h" + +#define MAXALIAS 25 +#define ALIAS ((struct alias *)aliases.c) + +struct alias { + char lhs[MAXCHARS - 5], *rhs; + struct shell shell; +}; + +static struct alias aliasarr[MAXALIAS + 1]; +static INITSTACK(aliases, aliasarr, 0); + +void applyaliases(struct shell *shell) { + struct cmd *cmd, *p; + char **end; + size_t l, a; + + p = cmd = shell->cmds; + + end = p->args; + while ((p = p->next)) if (p->args) end = p->args; + if (end) while (*end) ++end; + + 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; + + strcpy(ALIAS->shell.buffer, ALIAS->rhs); + l = strlen(ALIAS->shell.buffer); + ALIAS->shell.buffer[l + 1] = '\0'; + ALIAS->shell.buffer[l] = ';'; + + parse(&ALIAS->shell); + + for (a = 0; ALIAS->shell.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); + while ((p = p->next)) p->args += a - 1; + } +} + +BUILTINSIG(alias) { + 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); + break; + case 3: + 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; + } + strcpy(ALIAS->lhs, argv[1]); + strcpy(ALIAS->rhs = ALIAS->lhs + strlen(ALIAS->lhs) + 1, argv[2]); + if (aliases.c == aliases.t) INC(aliases, t); + break; + default: + puts("Usage: alias [lhs rhs]\r"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/builtin/alias.h b/src/builtin/alias.h new file mode 100644 index 0000000..540e935 --- /dev/null +++ b/src/builtin/alias.h @@ -0,0 +1 @@ +void applyaliases(struct shell *shell); diff --git a/src/builtin/bg.c b/src/builtin/bg.c index 1e090f0..8e90c58 100644 --- a/src/builtin/bg.c +++ b/src/builtin/bg.c @@ -35,7 +35,7 @@ BUILTINSIG(bg) { } } else { for (jobs.c = MINUSONE(jobs, t); jobs.c != MINUSONE(jobs, b); DEC(jobs, c)) - if (CURRENT->type == SUSPENDED) break; + if (JOB->type == SUSPENDED) break; if (jobs.c == MINUSONE(jobs, b)) { note("No suspended jobs to run in background"); return EXIT_FAILURE; diff --git a/src/builtin/build.c b/src/builtin/build.c index 30de555..9f52650 100644 --- a/src/builtin/build.c +++ b/src/builtin/build.c @@ -6,7 +6,8 @@ #include "../../external/cbs/cbs.c" #include "../../external/cbsfile.c" -#include "../config.h" + +#define MAXBUILTINS 50 int main(void) { int listfd, l; @@ -23,7 +24,7 @@ int main(void) { if (!(dir = opendir("./"))) err(EXIT_FAILURE, "Unable to open current directory"); - dprintf(listfd, "#include \n\n#include \"builtin.h\"\n" + dprintf(listfd, "#include \n\n#include \"builtin.h\"\n" "#include \"list.h\"\n\n"); errno = i = 0; @@ -35,7 +36,8 @@ int main(void) { if (!(name = strrchr(entry->d_name, '.')) || strcmp(name, ".c") != 0) continue; if (i == 1 + MAXBUILTINS + 1) - errx(EXIT_FAILURE, "Maximum allowed builtins (%d) exceeded", MAXBUILTINS); + errx(EXIT_FAILURE, "Unable to add built-in `%s', maximum reached (%d)", + name, MAXBUILTINS); if (!(name = strdup(entry->d_name))) err(EXIT_FAILURE, "Unable to duplicate directory entry"); name[strlen(name) - strlen(".c")] = '\0'; diff --git a/src/builtin/cd.c b/src/builtin/cd.c index 518df50..4eb01fe 100644 --- a/src/builtin/cd.c +++ b/src/builtin/cd.c @@ -1,27 +1,25 @@ #include +#include #include +#include #include "builtin.h" #include "utils.h" BUILTINSIG(cd) { - char *fullpath; + char *path; - if (!argv[1]) fullpath = home; - else if (!(fullpath = realpath(argv[1], NULL))) { - note("Could not resolve path name"); + path = argc == 1 ? home : argv[1]; + + if (chdir(path) == -1) { + note("Unable to change directory to `%s'", path); return EXIT_FAILURE; } - if (chdir(fullpath) == -1) { - note("Unable to change directory to `%s'", argv[1]); + if (setenv("PWD", path, 1) == -1) { + note("Unable to change $PWD$ to `%s'", path); return EXIT_FAILURE; } - if (setenv("PWD", fullpath, 1) == -1) - note("Unable to change $PWD$ to `%s'", fullpath); - - if (fullpath != home) free(fullpath); - return EXIT_SUCCESS; } diff --git a/src/builtin/fg.c b/src/builtin/fg.c index c2f035c..6a4958c 100644 --- a/src/builtin/fg.c +++ b/src/builtin/fg.c @@ -1,14 +1,58 @@ #include #include +#include #include #include #include +#include #include "builtin.h" #include "job.h" #include "stack.h" #include "utils.h" +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) { + while (waitpid(job.id, NULL, 0) != -1); + errno = 0; + + if (sigaction(SIGTTOU, &sigign, NULL) == -1 + || tcsetpgrp(STDIN_FILENO, getpgrp()) == -1 + || sigaction(SIGTTOU, &sigdfl, NULL) == -1) { + 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 (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); + } + return WSTOPSIG(fgstatus); +} + BUILTINSIG(fg) { long jobid; struct job *job; @@ -36,7 +80,7 @@ BUILTINSIG(fg) { } if (sigaction(SIGCHLD, &sigchld, NULL) == -1) { - note("Unable to install SIGCHLD handler"); + note("Unable to reinstall SIGCHLD handler"); return EXIT_FAILURE; } diff --git a/src/builtin/fg.h b/src/builtin/fg.h new file mode 100644 index 0000000..00b6eaa --- /dev/null +++ b/src/builtin/fg.h @@ -0,0 +1,2 @@ +int setfg(struct job job); +int waitfg(struct job job); diff --git a/src/builtin/source.c b/src/builtin/source.c new file mode 100644 index 0000000..de2f166 --- /dev/null +++ b/src/builtin/source.c @@ -0,0 +1,27 @@ +#include +#include +#include + +#include "input.h" +#include "shell.h" +#include "parse.h" +#include "run.h" +#include "utils.h" +#include "builtin.h" + +BUILTINSIG(source) { + struct shell shell; + + shell.script = argv[1]; + shell.input = scriptinput; + + while (run(parse(shell.input(&shell)))); + + return EXIT_SUCCESS; // TODO: Handle exit status +} + +void config(char *name) { + char path[PATH_MAX]; + + source(2, (char *[]){name, catpath(home, name, path), NULL}); +} diff --git a/src/builtin/source.h b/src/builtin/source.h new file mode 100644 index 0000000..55e2527 --- /dev/null +++ b/src/builtin/source.h @@ -0,0 +1 @@ +void config(char *name); diff --git a/src/builtin/which.c b/src/builtin/which.c index 9c4c48a..85d4427 100644 --- a/src/builtin/which.c +++ b/src/builtin/which.c @@ -1,54 +1,56 @@ -#include #include -#include -#include +#include #include +#include +#include +#include #include "builtin.h" #include "list.h" #include "utils.h" -static int inpath(char *dir, char *filename) { - char *filepath; - struct stat estat; - - if (stat(filepath = catpath(dir, filename), &estat) != -1) { - if (estat.st_mode & S_IXUSR) { - puts(filepath); - putchar('\r'); - return 1; - } - } else if (errno != ENOENT) note("Unable to check if `%s' exists", filepath); - - return 0; -} - BUILTINSIG(which) { + int result; + size_t i, l; struct builtin *builtin; - char *path, *dir, *p; + char *entry, *end, dir[PATH_MAX], path[PATH_MAX]; + struct stat pstat; - if (!argv[1]) return EXIT_FAILURE; - for (builtin = builtins; builtin->func; ++builtin) - if (strcmp(argv[1], builtin->name) == 0) { - printf("%s is built-in\r\n", argv[1]); - return EXIT_SUCCESS; + if (argc == 1) return EXIT_FAILURE; + + result = EXIT_SUCCESS; + for (i = 1; argv[i]; ++i) { + for (builtin = builtins; builtin->func; ++builtin) + if (strcmp(argv[i], builtin->name) == 0) { + printf("%s is built-in\r\n", argv[i]); + break; + } + if (builtin->func) continue; + + if (!(entry = getenv("PATH"))) { + note("Unable to examine $PATH$"); + return EXIT_FAILURE; + } + for (end = entry; end; entry = end + 1) { + l = (end = strchr(entry, ':')) ? end - entry : strlen(entry); + strncpy(dir, entry, l); + dir[l] = '\0'; + if (!catpath(dir, argv[i], path)) return EXIT_FAILURE; + if (stat(path, &pstat) != -1) { + if (pstat.st_mode & S_IXUSR) { + printf("%s\r\n", path); + break; + } + } else if (errno != ENOENT) { + note("Unable to check if `%s' exists", path); + return EXIT_FAILURE; + } } + if (entry != end + 1) continue; - if (!(path = getenv("PATH"))) { - note("Unable to examine $PATH$"); - return EXIT_FAILURE; - } - if (!(path = p = strdup(path))) { - note("Unable to duplicate $PATH$"); - return EXIT_FAILURE; + printf("%s not found\r\n", argv[i]); + result = EXIT_FAILURE; } - do { - if (!(dir = p)) break; - if ((p = strchr(dir, ':'))) *p++ = '\0'; - } while (!inpath(dir, argv[1])); - free(path); - if (dir) return EXIT_SUCCESS; - printf("%s not found\r\n", argv[1]); - return EXIT_FAILURE; + return result; } diff --git a/src/config.h b/src/config.h deleted file mode 100644 index b16bd76..0000000 --- a/src/config.h +++ /dev/null @@ -1,13 +0,0 @@ -#define DEFAULTPROMPT ">" - -#define HISTORYFILE ".ashhistory" -#define INTFILE ".ashint" -#define LOGINFILE ".ashlogin" - -#define MAXBUILTINS 100 // Maximum number of builtin commands -#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 cc48df9..ecba616 100644 --- a/src/history.c +++ b/src/history.c @@ -1,26 +1,40 @@ -#include #include +#include +#include #include +#include #include -#include "config.h" #include "input.h" +#include "shell.h" #include "stack.h" #include "utils.h" -static char historyarr[MAXHIST + 1][MAXCHARS + 1]; +#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); +/* TODO + void addhistory(char *buffer); + int prevhistory(char *buffer); + int nexthistory(char *buffer); +*/ + void readhistory(void) { + char *p; FILE *file; - - if (!(file = fopen(catpath(home, HISTORYFILE), "r"))) { + + if (!catpath(home, HISTORYFILE, filepath)) exit(EXIT_FAILURE); + if (!(file = fopen(filepath, "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(BUFFER, history.size, file)) { + *(BUFFER + strlen(BUFFER) - 1) = '\0'; + push(&history, BUFFER); } if (ferror(file) || !feof(file)) fatal("Unable to read from history file"); @@ -28,9 +42,10 @@ void readhistory(void) { } void writehistory(void) { + char *filename; FILE *file; - if (!(file = fopen(catpath(home, HISTORYFILE), "w"))) { + if (!(file = fopen(filepath, "w"))) { note("Unable to open history file for writing"); return; } diff --git a/src/input.c b/src/input.c index dea4330..7808220 100644 --- a/src/input.c +++ b/src/input.c @@ -1,20 +1,20 @@ #include -#include +#include #include #include #include #include #include -#include #include -#include "config.h" -#include "history.h" #include "input.h" -#include "job.h" +#include "shell.h" +#include "history.h" #include "stack.h" #include "utils.h" +#define DEFAULTPROMPT ">" + enum { CTRLC = '\003', CTRLD, @@ -31,73 +31,65 @@ enum { DEL = '\177', }; -char *string, buffer[MAXCHARS + 2], *script; - 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 (!*shell->string) { + if (shell->script && munmap(shell->map.m, shell->map.l) == -1) + note("Unable to unmap memory associated with `%s'", shell->script); + 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(buffer, start, l); - buffer[l] = ';'; - buffer[l + 1] = '\0'; + strncpy(shell->buffer, start, l); + shell->buffer[l] = ';'; + shell->buffer[l + 1] = '\0'; - return buffer; + return shell; } 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; + 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); } -char *config(char *name) { +static struct shell *config(char *name) { int fd; - char *result; - static char *origscript, *origstr; - - if (!origscript) { - origscript = script; - origstr = string; - script = catpath(home, name); - } - - if ((fd = open(script, O_RDONLY | O_CREAT, 0644)) == -1) - fatal("Unable to open `%s'\n", script); - if (close(fd) == -1) - fatal("Unable to close `%s'", script); - - if (!(result = scriptinput())) { - script = origscript; - string = origstr; - origscript = NULL; + 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); } - return result; + result = shell.input(&shell); + if (result) return result; + shell.script = NULL; + return NULL; } static size_t prompt(void) { @@ -110,20 +102,20 @@ static size_t prompt(void) { } INPUT(userinput) { - char *cursor, *end; + char *start, *cursor, *end; size_t promptlen; unsigned int c; int i; - end = cursor = buffer; - *history.t = *buffer = '\0'; + end = cursor = start = shell->buffer; + *history.t = *start = '\0'; history.c = history.t; - while (buffer == end) { + while (start == end) { promptlen = prompt(); while ((c = getchar()) != '\r') switch (c) { default: if (c >= ' ' && c <= '~') { - if (end - buffer == MAXCHARS) continue; + if (end - start == MAXCHARS) continue; memmove(cursor + 1, cursor, end - cursor); *cursor++ = c; *++end = '\0'; @@ -135,15 +127,15 @@ INPUT(userinput) { break; case CTRLC: puts("^C\r"); - *buffer = '\0'; - return buffer; + *start = '\0'; + return shell; case CTRLD: puts("^D\r"); return NULL; case CLEAR: fputs("\033[H\033[J", stdout); prompt(); - fputs(buffer, stdout); + fputs(start, stdout); continue; case ESCAPE: switch ((c = getchar())) { @@ -152,8 +144,8 @@ INPUT(userinput) { while (cursor != end && *cursor == ' ') putchar(*cursor++); break; case BACKWARD: - while (cursor != buffer && *(cursor - 1) == ' ') putchar((--cursor, '\b')); - while (cursor != buffer && *(cursor - 1) != ' ') putchar((--cursor, '\b')); + while (cursor != start && *(cursor - 1) == ' ') putchar((--cursor, '\b')); + while (cursor != start && *(cursor - 1) != ' ') putchar((--cursor, '\b')); break; case ARROW: switch ((c = getchar())) { @@ -162,20 +154,20 @@ INPUT(userinput) { if (history.c == (c == UP ? history.b : history.t)) continue; putchar('\r'); - for (i = end - buffer + promptlen; i > 0; --i) putchar(' '); + for (i = end - start + promptlen; i > 0; --i) putchar(' '); putchar('\r'); - if (strcmp((char *)history.c, buffer) != 0) - strcpy((char *)history.t, buffer); + if (strcmp((char *)history.c, start) != 0) + strcpy((char *)history.t, start); if (c == UP) DEC(history, c); else INC(history, c); - strcpy(buffer, (char *)history.c); - end = cursor = buffer + strlen(buffer); + strcpy(start, (char *)history.c); + end = cursor = start + strlen(start); prompt(); - fputs(buffer, stdout); + fputs(start, stdout); break; case LEFT: - if (cursor > buffer) putchar((--cursor, '\b')); + if (cursor > start) putchar((--cursor, '\b')); break; case RIGHT: if (cursor < end) putchar(*cursor++); @@ -188,7 +180,7 @@ INPUT(userinput) { break; case BACKSPACE: case DEL: - if (cursor == buffer) continue; + if (cursor == start) continue; memmove(cursor - 1, cursor, end - cursor); --cursor; *--end = '\0'; @@ -202,10 +194,10 @@ INPUT(userinput) { } puts("\r"); } - push(&history, buffer); + push(&history, start); *end++ = ';'; *end = '\0'; - return buffer; + return shell; } diff --git a/src/input.h b/src/input.h index eae90c5..50dc132 100644 --- a/src/input.h +++ b/src/input.h @@ -1,8 +1,8 @@ -#define INPUT(name) char *name(void) +#define INPUT(name) struct shell *name(struct shell *shell) -extern char *string, buffer[MAXCHARS + 2], *script; +typedef INPUT((*Input)); INPUT(stringinput); INPUT(scriptinput); -char *config(char *name); +// struct shell *config(char *name); INPUT(userinput); diff --git a/src/job.c b/src/job.c index a412e89..857dfe4 100644 --- a/src/job.c +++ b/src/job.c @@ -1,26 +1,25 @@ -#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 jobarr[MAXJOBS + 1]; INITSTACK(jobs, jobarr, 0); -struct termios raw, canonical; -struct sigaction sigchld, sigdfl, sigign; -static int fgstatus; +int fgstatus; + +/* TODO + addjob + popjob + findjob +*/ void *findjob(pid_t jobid) { if (jobs.b == jobs.t) return NULL; - for (jobs.c = jobs.b; CURRENT->id != jobid; INC(jobs, c)) + for (jobs.c = jobs.b; JOB->id != jobid; INC(jobs, c)) if (jobs.c == jobs.t) return NULL; return jobs.c; } @@ -30,71 +29,3 @@ void *deletejob(void) { memmove(jobs.c, PLUSONE(jobs, c), jobs.t - jobs.c); return DEC(jobs, t); } - -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) { - while (waitpid(job.id, NULL, 0) != -1); - errno = 0; - - if (sigaction(SIGTTOU, &sigign, NULL) == -1 - || tcsetpgrp(STDIN_FILENO, getpgrp()) == -1 - || sigaction(SIGTTOU, &sigdfl, NULL) == -1) { - 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 (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); - } - return WSTOPSIG(fgstatus); -} - -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 (CURRENT->id == id) { - if (WIFSTOPPED(status)) CURRENT->type = SUSPENDED; - else deletejob(); - break; - } - if (jobs.c == jobs.t) { - fgstatus = status; - if (!WIFSTOPPED(fgstatus)) while (waitpid(-id, NULL, 0) != -1); - } - } -} diff --git a/src/job.h b/src/job.h index 7b33124..1a6f288 100644 --- a/src/job.h +++ b/src/job.h @@ -1,4 +1,4 @@ -#define CURRENT ((struct job *)jobs.c) +#define JOB ((struct job *)jobs.c) enum jobtype { BACKGROUND, @@ -12,12 +12,7 @@ struct job { }; extern struct stack jobs; -extern struct termios raw, canonical; -extern struct sigaction sigchld, sigdfl, sigign; +extern int fgstatus; void *findjob(pid_t jobid); void *deletejob(void); -int setconfig(struct termios *mode); -int setfg(struct job job); -int waitfg(struct job job); -void sigchldhandler(int sig); diff --git a/src/main.c b/src/main.c index a547092..12b278e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,24 +1,25 @@ #include -#include "config.h" #include "input.h" +#include "shell.h" #include "options.h" #include "parse.h" #include "run.h" #include "utils.h" +#include "source.h" -int main(int localargc, char **localargv) { - argc = localargc; - argv = localargv; +int main(int c, char **v) { + argc = c; + argv = v; options(); initialize(); - if (login) while (run(parse(config(LOGINFILE)))); - if (interactive) while (run(parse(config(INTFILE)))); + if (login) config(".ashlogin"); + if (interactive) config(".ashint"); - while (run(parse(input()))); + while (run(parse(shell.input(&shell)))); deinitialize(); diff --git a/src/options.c b/src/options.c index 0476c18..d54ca9b 100644 --- a/src/options.c +++ b/src/options.c @@ -3,14 +3,14 @@ #include #include -#include "config.h" #include "input.h" -#include "options.h" +#include "shell.h" int login, interactive, argc; -Input input; char **argv; +struct shell shell; + static void usage(char *program, int code) { printf("Usage: %s [file] [-c string] [-hl]\n" " ... Run script\n" @@ -25,14 +25,14 @@ void options(void) { login = **argv == '-'; interactive = 1; - input = userinput; + shell.input = userinput; while ((opt = getopt(argc, argv, ":c:hl")) != -1) { switch (opt) { case 'c': interactive = 0; - input = stringinput; - string = optarg; + shell.string = optarg; + shell.input = stringinput; break; case 'h': usage(*argv, EXIT_SUCCESS); @@ -49,10 +49,10 @@ void options(void) { } if (opt == 'c') break; } - if (!string && argv[optind]) { + if (!shell.string && argv[optind]) { interactive = 0; - input = scriptinput; - script = argv[optind]; + shell.script = argv[optind]; + shell.input = scriptinput; } if (!interactive) { argc -= optind; diff --git a/src/options.h b/src/options.h index 9918207..3186fad 100644 --- a/src/options.h +++ b/src/options.h @@ -1,7 +1,5 @@ -typedef INPUT((*Input)); - extern int login, interactive, argc; -extern Input input; extern char **argv; +extern struct shell shell; void options(void); diff --git a/src/parse.c b/src/parse.c index e377aa5..37ad6eb 100644 --- a/src/parse.c +++ b/src/parse.c @@ -3,16 +3,12 @@ #include #include -#include "config.h" #include "input.h" +#include "shell.h" #include "options.h" -#include "parse.h" #include "run.h" #include "utils.h" -static char *tokens[MAXCHARS + 1]; -static struct cmd cmds[MAXCMDS + 1]; - static void initcmd(struct cmd *cmd) { cmd->args = NULL; cmd->r = cmd->rds; @@ -21,18 +17,19 @@ static void initcmd(struct cmd *cmd) { cmd->next = NULL; } -struct cmd *parse(char *b) { - char **t, *name, *value, *stlend, *p, *end, *env; +struct shell *parse(struct shell *shell) { + char *b, **t, *name, *value, *stlend, *p, *end, *env; struct cmd *c; long l; int e, offset; - if (!b) return NULL; - t = tokens; - c = cmds; - c->next = c + 1; - value = name = NULL; - for (initcmd(++c); *b; ++b) switch (*b) { + if (!shell) return NULL; + b = shell->buffer; + t = shell->tokens; + c = shell->cmds + 1; + shell->cmds->next = NULL; + *t = value = name = NULL; + for (initcmd(c); *b; ++b) switch (*b) { default: if (c->r->mode) break; if (!c->args) c->args = t; @@ -44,14 +41,14 @@ struct cmd *parse(char *b) { case '>': if (c->r->mode) { note("File redirections should be separated by spaces"); - return c; + return shell; } 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 c; + return shell; } c->r->newfd = l; } @@ -73,7 +70,7 @@ struct cmd *parse(char *b) { } if (!b) { note("Quote left open-ended"); - return c; + return shell; } memmove(p, p + 1, end-- - p); --b; @@ -112,7 +109,7 @@ struct cmd *parse(char *b) { while (*b && *b != '$') ++b; if (!*b) { note("Environment variable lacks a terminating `$'"); - return c; + return shell; } *b++ = '\0'; for (end = b; *end; ++end); @@ -122,11 +119,11 @@ struct cmd *parse(char *b) { else if (strcmp(p + 1, "?") == 0) { if (!sprintf(env = (char [12]){0}, "%d", status)) { note("Unable to get previous command status"); - return c; + return shell; } } else if ((env = getenv(p + 1)) == NULL) { note("Environment variable does not exist"); - return c; + return shell; } e = strlen(env); @@ -162,7 +159,7 @@ struct cmd *parse(char *b) { if ((l = strtol(++c->r->oldname, &stlend, 10)) < 0 || l > INT_MAX || *stlend) { note("Incorrect syntax for file redirection"); - return c; + return shell; } c->r->oldfd = l; c->r->oldname = NULL; @@ -178,7 +175,7 @@ struct cmd *parse(char *b) { if (value) { if (setenv(name, value, 1) == -1) { note("Unable to set environment variable"); - return c; + return shell; } value = name = NULL; } @@ -191,10 +188,11 @@ struct cmd *parse(char *b) { case PIPE: case OR: note("Expected another command"); - return c; + return shell; default: break; } - return cmds; + shell->cmds->next = shell->cmds + 1; + return shell; } diff --git a/src/parse.h b/src/parse.h index 2962394..4053bf5 100644 --- a/src/parse.h +++ b/src/parse.h @@ -1,31 +1 @@ -enum accessmode { - END, - READ = '<', - READWRITE, - WRITE = '>', - APPEND, -}; - -struct redirect { - enum accessmode mode; - int oldfd, newfd; - char *oldname; -}; - -enum terminator { - SEMI = ';', - BG = '&', - AND, - PIPE = '|', - OR, -}; - -struct cmd { - char **args; - struct redirect *r, rds[MAXRDS + 1]; - enum terminator term; - int pipe[2]; - struct cmd *prev, *next; -}; - -struct cmd *parse(char *b); +struct shell *parse(struct shell *shell); diff --git a/src/run.c b/src/run.c index e78656b..f66e3ed 100644 --- a/src/run.c +++ b/src/run.c @@ -4,11 +4,15 @@ #include #include #include +#include +#include // XXX +#include "input.h" +#include "shell.h" +#include "alias.h" #include "builtin.h" -#include "config.h" #include "job.h" -#include "parse.h" +#include "fg.h" #include "stack.h" #include "utils.h" @@ -55,23 +59,25 @@ static void redirectfiles(struct redirect *r) { } static void exec(struct cmd *cmd) { - char *cwd; + char cwd[PATH_MAX]; execvp(*cmd->args, cmd->args); - if (!(cwd = getcwd(NULL, 0))) - fatal("Unable to check current working directory"); + if (!getcwd(cwd, PATH_MAX)) fatal("Unable to check current working directory"); execvP(*cmd->args, cwd, cmd->args); - free(cwd); fatal("Couldn't find `%s' command", *cmd->args); } -int run(struct cmd *cmd) { +int run(struct shell *shell) { + struct cmd *cmd; int ispipe, ispipestart, ispipeend; pid_t cpid, jobid; struct job job; - if (!cmd) return 0; + if (!shell) return 0; + applyaliases(shell); + + cmd = shell->cmds; while ((cmd = cmd->next)) { if (!cmd->args) { if (!cmd->r->mode) break; diff --git a/src/run.h b/src/run.h index 7760e69..cebcc90 100644 --- a/src/run.h +++ b/src/run.h @@ -1,3 +1,3 @@ extern int status; -int run(struct cmd *cmd); +int run(struct shell *shell); diff --git a/src/shell.h b/src/shell.h new file mode 100644 index 0000000..a153acd --- /dev/null +++ b/src/shell.h @@ -0,0 +1,43 @@ +#define MAXCHARS 500 +#define MAXCMDS (MAXCHARS + 1) / 2 +#define MAXRDS (MAXCHARS / 3) + +enum accessmode { + END, + READ = '<', + READWRITE, + WRITE = '>', + APPEND, +}; + +struct redirect { + enum accessmode mode; + int oldfd, newfd; + char *oldname; +}; + +enum terminator { + SEMI = ';', + BG = '&', + AND, + PIPE = '|', + OR, +}; + +struct cmd { + char **args; + struct redirect *r, rds[MAXRDS + 1]; + enum terminator term; + int pipe[2]; + struct cmd *prev, *next; +}; + +struct shell { + char buffer[MAXCHARS + 1 + 1], *tokens[MAXCMDS + 1], *script, *string; + struct { + char *m; + size_t l; + } map; + struct cmd cmds[MAXCMDS + 1]; + Input input; +}; diff --git a/src/utils.c b/src/utils.c index b8cf6b9..30e3799 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -8,13 +9,14 @@ #include #include -#include "config.h" #include "history.h" #include "input.h" #include "job.h" #include "options.h" -#include "utils.h" +#include "stack.h" +struct termios raw, canonical; +struct sigaction sigchld, sigdfl, sigign; char *home; void note(char *fmt, ...) { @@ -35,54 +37,78 @@ void fatal(char *fmt, ...) { exit(EXIT_FAILURE); } -char *catpath(char *dir, char *filename) { - static char path[MAXPATH + 1]; - - strcpy(path, dir); - strcat(path, "/"); - strcat(path, filename); +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; - return path; + (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; + char *shlvlstr, buffer[19 + 1]; long shlvl; - // Raw mode if (tcgetattr(STDIN_FILENO, &canonical) == -1) err(EXIT_FAILURE, "Unable to get default termios config"); cfmakeraw(&raw); if (!setconfig(&raw)) exit(EXIT_FAILURE); - // Set signal actions 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"); - // Initialize `home' if (!(home = getenv("HOME"))) fatal("Unable to locate user's home directory, $HOME$ not set"); - // Update $SHLVL$ if (!(shlvlstr = getenv("SHLVL"))) shlvlstr = "0"; if ((shlvl = strtol(shlvlstr, NULL, 10)) < 0) shlvl = 0; - asprintf(&shlvlstr, "%ld", shlvl + 1); + sprintf(shlvlstr = buffer, "%ld", shlvl + 1); if (setenv("SHLVL", shlvlstr, 1) == -1) note("Unable to update $SHLVL$ environment variable"); - free(shlvlstr); - // History read if (interactive) readhistory(); } -void deinitialize(void) { +char *catpath(char *dir, char *filename, char *buffer) { + int slash; - // History write + slash = dir[strlen(dir) - 1] == '/'; + if (strlen(dir) + slash + strlen(filename) + 1 > PATH_MAX) { + note("Path name `%s%s%s' too long", dir, slash ? "/" : "", filename); + return NULL; + } + + strcpy(buffer, dir); + if (!slash) strcat(buffer, "/"); + strcat(buffer, filename); + + return buffer; +} + +void deinitialize(void) { if (interactive) writehistory(); - // Canonical mode setconfig(&canonical); } diff --git a/src/utils.h b/src/utils.h index 24bd6e8..1cbc15e 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,7 +1,10 @@ +extern struct termios raw, canonical; +extern struct sigaction sigchld, sigdfl, sigign; extern char *home; void note(char *fmt, ...); void fatal(char *fmt, ...); -char *catpath(char *dir, char *filename); +int setconfig(struct termios *mode); void initialize(void); +char *catpath(char *dir, char *filename, char *buffer); void deinitialize(void);