From ef8eb37404e7416a44921aed45ff1107f50e0405 Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Sun, 14 Jun 2026 20:15:24 -0400 Subject: [PATCH] Major refactors - Removing list operators (&& and ||) - Command execution - Background and foreground job control (bg.c and fg.c) - Build automation for built-ins (list.c replaced by builtins.c) - Exit with errno where applicable - Bug fixes and feature consistency - Update to cbs.c --- INSTALL.md | 2 +- LICENSE | 2 +- README.md | 31 +++-- external/cbs | 2 +- src/build.c | 4 +- src/builtins/.gitignore | 2 +- src/builtins/README.md | 26 +++-- src/builtins/alias.c | 53 ++++----- src/builtins/bg.c | 169 ++++++++++++---------------- src/builtins/bg.h | 15 +-- src/builtins/build.c | 39 +++---- src/builtins/builtin.c | 22 ---- src/builtins/builtin.h | 7 -- src/builtins/builtins.h | 4 + src/builtins/cd.c | 16 +-- src/builtins/exec.c | 37 +++--- src/builtins/exec.h | 2 +- src/builtins/exeunt.c | 1 - src/builtins/fg.c | 175 ++++++++++++----------------- src/builtins/fg.h | 5 +- src/builtins/mode.c | 7 +- src/builtins/mode.h | 1 + src/builtins/pwd.c | 15 +-- src/builtins/set.c | 8 +- src/builtins/source.c | 27 ++--- src/builtins/unalias.c | 3 +- src/builtins/unset.c | 6 +- src/builtins/which.c | 43 +++---- src/builtins/which.h | 1 + src/context.c | 9 +- src/context.h | 49 ++++---- src/history.c | 91 ++++++--------- src/input.c | 74 ++++++------ src/main.c | 2 +- src/options.c | 8 +- src/options.h | 2 +- src/parse.c | 40 +++---- src/run.c | 243 +++++++++++++++++----------------------- src/run.h | 2 - src/signals.c | 57 ++++------ src/signals.h | 5 +- src/utils.c | 90 +++++++-------- src/utils.h | 7 +- tools/install.c | 4 +- tools/uninstall.c | 13 ++- 45 files changed, 615 insertions(+), 806 deletions(-) delete mode 100644 src/builtins/builtin.c delete mode 100644 src/builtins/builtin.h create mode 100644 src/builtins/builtins.h create mode 100644 src/builtins/mode.h diff --git a/INSTALL.md b/INSTALL.md index c220c9a..56a3d09 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -17,7 +17,7 @@ presenting thus as the default shell in an interactive session. ## Example configuration Here is a minimal example which assumes sh as the user's default shell (a -similar approach can be used for other shells). We'll need to modify the scripts +similar approach is feasible for other shells). We'll need to modify the scripts that are executed at login and during an interactive session. For sh, the login configuration file is `.profile`. diff --git a/LICENSE b/LICENSE index 4236baa..5c51ff4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Trent Huber +Copyright (c) 2026 Trent Huber Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 4e6af18..61841c8 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,9 @@ thus is a custom Unix shell for POSIX platforms. - Job control (`fg`, `bg`, `&`, `^Z`) - Pipelines -- Conditional execution (`&&`, `||`) - File redirection (`&1`, etc.) - Globbing (`*`, `?`, `[...]`) -- Quoting (`'...', "..."`) +- Quotes (`'...', "..."`) - Variables (`set`, `unset`, `$VAR$`, etc.) - Aliasing (`alias`, `unalias`) - Configuration files (`~.thuslogin`, `~.thusrc`) @@ -47,20 +46,29 @@ location. While thus operates for the most part like Bourne shell, there are a few places where it takes a subtly different approach. +### Sequential command execution + +Most other shells by default will execute all the commands in a line regardless +of the previous one's exit status. Typically, in order to control whether a +command runs based on the exit status of the last, these shells provide list +operators like `&&` and `||`. Neither of the these operators are included in +this shell. Instead, the `&&` operator is the default behavior between commands, +while the `||` operator is forgone entirely. If any command fails, the whole +string of commands fail whether from a script or from an interactive session. + ### Quotes -Like most other shells, variables, tildes, and escape sequences will be expanded +Like other shells, variables, tildes, and escape sequences will be expanded inside of double quotes, but not single quotes. *Unlike* other shells however, quotes do not concatenate with other arguments that are not separated from the quote by whitespace. For example, the command `echo "foo"bar` would print -`foo bar` whereas other shells would combine them into a single argument and -print `foobar`. +`foo bar` whereas other shells would print `foobar`. ### Variables and aliases -Variables are referred to by strings of characters that begin and end with a +Variables are referred to by strings of characters that begin *and end* with a `$`. For example, evaluating the path variable would look like `$PATH$`. Setting -variables is done with the `set` built-in command, not with the `name=value` +variables is done with the `set` built-in command, not with a `name=value` syntax. This syntax is similarly avoided when declaring aliases with the `alias` built-in command. @@ -82,13 +90,18 @@ substituted in the shell, it will retain the trailing slash. If there is whitespace between a file redirection operator and a filename following it, then it is *not* parsed as a file redirection, but instead as two separate arguments. Something like `ls >file` would redirect the output of the -`ls` command to `file`, whereas `ls > file` would list any files named `>` and -`file`. +`ls` command to `file`, whereas `ls > file` would list any files that might be +named `>` or `file`. ## Resources - [TTY Demystified](http://www.linusakesson.net/programming/tty/) - [Process Groups and Terminal Signaling]( https://cs162.org/static/readings/ic221_s16_lec17.html) + +## References + - [Terminal Input Sequences]( https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_input_sequences) +- [C Preprocessor Stringification]( +https://gcc.gnu.org/onlinedocs/gcc-6.5.0/cpp/Stringification.html) diff --git a/external/cbs b/external/cbs index 8ca54de..a275505 160000 --- a/external/cbs +++ b/external/cbs @@ -1 +1 @@ -Subproject commit 8ca54de4dab957a5cb5889c9b84ec41a00840e10 +Subproject commit a2755058e34465134d49bf8b75ec2ea939158418 diff --git a/src/build.c b/src/build.c index 1b0c587..2d5b03b 100644 --- a/src/build.c +++ b/src/build.c @@ -1,7 +1,7 @@ #include "../external/cbs/cbs.c" -#define SRC1 "context", "history", "input", "signals" -#define SRC2 "main", "options", "parse", "run", "utils" +#define SRC1 "context", "history", "input", "options", "signals" +#define SRC2 "main", "parse", "run", "utils" int main(void) { char **src; diff --git a/src/builtins/.gitignore b/src/builtins/.gitignore index 3309b20..03bc79e 100644 --- a/src/builtins/.gitignore +++ b/src/builtins/.gitignore @@ -1 +1 @@ -list.c +builtins.c diff --git a/src/builtins/README.md b/src/builtins/README.md index 575227a..5c6fb99 100644 --- a/src/builtins/README.md +++ b/src/builtins/README.md @@ -10,7 +10,6 @@ built-in). #include -#include "builtin.h" #include "utils.h" int foo(char **args, size_t numargs) { @@ -26,12 +25,19 @@ and `argc` parameters found in the prototypical C `main()` function. Arguments here are also passed as a NULL-terminated array of C strings. For a consistent user interface, usage messages can be shown with the `usage()` -function, defined in [`src/builtins/builtin.c`](builtin.c), and errors can be -explained with the `note()` function defined in [`src/utils.c`](../utils.c). -Since built-ins are usually run directly by the shell, calls to functions like -`exit()` could cause the shell itself to terminate, a behavior that isn't -typically intended. Errors should instead be reported by returning an error code -from the built-in function for the shell to handle. - -Once finished, simply rebuild the shell and it will automatically incorporate -the new built-in. +function and errors can be explained with the `note()` or `fatal()` functions, +all of which are defined in [`src/utils.c`](../utils.c). It should be noted that +calling functions like `fatal()` or `exit()` will cause the *shell itself* to +terminate since these functions are run directly inside the shell, not in a +separate process. The value returned from the built-in's function will be used +as the exit status for that built-in in the shell. Of course, the shell comes +with several examples. + +Because this is running in the same environment as the shell, certain aspects of +signals are configured differently than would normally be the case. The signals +SIGTSTP, SIGTTIN, and SIGTTOU are blocked in the signal mask, and the signals +SIGHUP, SIGINT, SIGQUIT, SIGTERM, and SIGWINCH have custom handlers defined in +[`src/signals.c`](../signals.c). + +Once finished writing the function, simply rebuild the shell and it will +automatically incorporate it as a new built-in. diff --git a/src/builtins/alias.c b/src/builtins/alias.c index c5229a6..40d2553 100644 --- a/src/builtins/alias.c +++ b/src/builtins/alias.c @@ -2,25 +2,21 @@ #include #include -#include "builtin.h" #include "context.h" #include "parse.h" #include "utils.h" #define MAXALIAS 50 -static struct { - struct entry { - char name[MAXCHARS - 5], *value; - } entries[MAXALIAS + 1]; - size_t size; -} aliases; +static struct alias { + char name[MAXCHARS - 5], *value; +} aliases[MAXALIAS + 1]; +static size_t numaliases; static size_t getindex(char *name) { size_t i; - for (i = 0; i < aliases.size; ++i) - if (strcmp(aliases.entries[i].name, name) == 0) break; + for (i = 0; i < numaliases; ++i) if (strcmp(aliases[i].name, name) == 0) break; return i; } @@ -28,9 +24,9 @@ static size_t getindex(char *name) { char *getalias(char *name) { size_t i; - if ((i = getindex(name)) == aliases.size) return NULL; + if ((i = getindex(name)) == numaliases) return NULL; - return aliases.entries[i].value; + return aliases[i].value; } char **parsealias(char *value) { @@ -52,14 +48,13 @@ char **parsealias(char *value) { int removealias(char *name) { size_t i; - struct entry *entry; + struct alias *a; - if ((i = getindex(name)) == aliases.size) return 0; + if ((i = getindex(name)) == numaliases) return 0; - entry = aliases.entries + i; - memmove(entry, entry + 1, (--aliases.size - i) * sizeof*entry); - for (; i < aliases.size; ++i, ++entry) - entry->value = (void *)entry->value - sizeof*entry; + a = aliases + i; + memmove(a, a + 1, (--numaliases - i) * sizeof*a); + for (; i < numaliases; ++i, ++a) a->value = (void *)a->value - sizeof*a; return 1; } @@ -67,19 +62,17 @@ int removealias(char *name) { int alias(char **args, size_t numargs) { size_t i; char *end; - struct entry *entry; + struct alias *a; switch (numargs) { case 1: - for (i = 0; i < aliases.size; ++i) - printf("%s = %s\n", quoted(aliases.entries[i].name), - aliases.entries[i].value); + for (i = 0; i < numaliases; ++i) + printf("%s = %s\n", quoted(aliases[i].name), aliases[i].value); break; case 3: - if (aliases.size == MAXALIAS) { - note("Unable to add `%s' alias, maximum reached (%d)", args[1], MAXALIAS); - return EXIT_FAILURE; - } + if (numaliases == MAXALIAS) + return note("Unable to add `%s' alias, maximum reached (%d)", + args[1], MAXALIAS); if (!parsealias(args[2])) return EXIT_FAILURE; for (i = 1; i <= 2; ++i) { @@ -89,12 +82,12 @@ int alias(char **args, size_t numargs) { *end = '\0'; } - entry = aliases.entries + (i = getindex(args[1])); - if (i == aliases.size) { - strcpy(entry->name, args[1]); - ++aliases.size; + a = aliases + (i = getindex(args[1])); + if (i == numaliases) { + strcpy(a->name, args[1]); + ++numaliases; } - strcpy(entry->value = entry->name + strlen(entry->name) + 1, args[2]); + strcpy(a->value = a->name + strlen(a->name) + 1, args[2]); break; default: diff --git a/src/builtins/bg.c b/src/builtins/bg.c index 1191c24..6afb6c7 100644 --- a/src/builtins/bg.c +++ b/src/builtins/bg.c @@ -6,149 +6,118 @@ #include #include "bg.h" -#include "builtin.h" -#include "fg.h" #include "utils.h" -#define MAXBG 100 - -static struct { - struct bglink { - struct bgjob job; - struct bglink *next; - } entries[MAXBG], *active, *free; -} bgjobs; -struct sigaction bgaction; - -void removebg(pid_t id) { - struct bglink *p, *prev; - - for (prev = NULL, p = bgjobs.active; p; prev = p, p = p->next) - if (p->job.id == id) break; - if (!p) return; - - if (prev) prev->next = p->next; else bgjobs.active = p->next; - p->next = bgjobs.free; - bgjobs.free = p; +#define MAXBGS 100 + +static struct bgjob { + struct job job; + struct bgjob *previous, *next; +} bgjobs[MAXBGS], *used, *available; + +void initbg(void) { + size_t i; + + for (i = 0; i < MAXBGS - 1; ++i) { + bgjobs[i + 1].previous = &bgjobs[i]; + bgjobs[i].next = &bgjobs[i + 1]; + } + available = bgjobs; } -void sigchldbghandler(int sig) { +void removebg(struct job *j) { + struct bgjob *b; + + b = (struct bgjob *)j; + + if (b->next) b->next->previous = b->previous; + if (b->previous) { + b->previous->next = b->next; + b->previous = NULL; + } else used = b->next; + if ((b->next = available)) available->previous = b; + available = b; +} + +void sigchldbghandler(int signal) { int e, s; - struct bglink *p; - pid_t id; + struct bgjob *b, *p; + pid_t cpid; - (void)sig; + (void)signal; e = errno; - p = bgjobs.active; - while (p) { - while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0) + b = used; + while (b) { + while ((cpid = waitpid(-b->job.id, &s, WNOHANG | WUNTRACED)) > 0) if (WIFSTOPPED(s)) { - p->job.suspended = 1; + b->job.suspended = 1; break; } - if (id == -1) { - id = p->job.id; - p = p->next; - removebg(id); - } else p = p->next; + if (cpid == -1) p = b; + b = b->next; + if (cpid == -1) removebg(&p->job); } errno = e; } -void initbg(void) { - size_t i; - - bgaction = (struct sigaction){.sa_handler = sigchldbghandler}; - - for (i = 0; i < MAXBG - 1; ++i) - bgjobs.entries[i].next = bgjobs.entries + i + 1; - bgjobs.free = bgjobs.entries; -} - int bgfull(void) { - return !bgjobs.free; + return !available; } -int pushbg(struct bgjob job) { - struct bglink *p; - - if (bgfull()) return 0; - - (p = bgjobs.free)->job = job; - bgjobs.free = p->next; - p->next = bgjobs.active; - bgjobs.active = p; +void pushbg(struct job job) { + struct bgjob *b; - return 1; + (b = available)->job = job; + if ((available = b->next)) available->previous = NULL; + if ((b->next = used)) used->previous = b; + used = b; } -int pushbgid(pid_t id) { - return pushbg((struct bgjob){.id = id, .config = canonical}); +struct job *peekbg(void) { + return used ? &used->job : NULL; } -int peekbg(struct bgjob *job) { - if (bgjobs.active && job) *job = bgjobs.active->job; - return bgjobs.active != NULL; -} - -int searchbg(pid_t id, struct bgjob *job) { - struct bglink *p; +struct job *searchbg(pid_t id) { + struct bgjob *b; - for (p = bgjobs.active; p; p = p->next) if (p->job.id == id) { - if (job) *job = p->job; - return 1; - } + for (b = used; b; b = b->next) if (b->job.id == id) return &b->job; - return 0; + return NULL; } void deinitbg(void) { - struct bglink *p; + struct bgjob *b; - for (p = bgjobs.active; p; p = p->next) killpg(p->job.id, SIGKILL); + for (b = used; b; b = b->next) killpg(b->job.id, SIGKILL); } int bg(char **args, size_t numargs) { - struct bglink *p; - struct bgjob job; + struct bgjob *b; + struct job *j, job; long l; switch (numargs) { case 1: - for (p = bgjobs.active; p; p = p->next) if (p->job.suspended) { - job = p->job; - break; - } - if (!p) { - note("No suspended jobs to run in the background"); - return EXIT_FAILURE; - } + for (b = used; b; b = b->next) if (b->job.suspended) break; + if (!b) return note("No suspended jobs to run in the background"); + j = &b->job; break; case 2: - errno = 0; - if ((l = strtol(args[1], NULL, 10)) == LONG_MAX && errno || l <= 0) { - note("Invalid job id %ld", l); - return EXIT_FAILURE; - } - if (!searchbg(l, &job)) { - note("Unable to find job %d", (pid_t)l); - return EXIT_FAILURE; - } - if (!job.suspended) { - note("Job %d already in background", job.id); - return EXIT_FAILURE; - } + if ((l = strtol(args[1], NULL, 10)) == LONG_MAX && errno || l <= 0) + return note("Invalid job id %ld", l); + if (!(j = searchbg(l))) return note("Unable to find job %d", (pid_t)l); + if (!j->suspended) return note("Job %d already in background", j->id); break; default: return usage(args[0], "[pgid]"); } - if (killpg(job.id, SIGCONT) == -1) { - note("Unable to wake up suspended job %d", job.id); - return EXIT_FAILURE; - } - removebg(job.id); + if (killpg(j->id, SIGCONT) == -1) + fatal("Unable to wake up suspended job %d", j->id); + + job = *j; + removebg(j); job.suspended = 0; pushbg(job); diff --git a/src/builtins/bg.h b/src/builtins/bg.h index 3d34bbe..948d678 100644 --- a/src/builtins/bg.h +++ b/src/builtins/bg.h @@ -1,17 +1,14 @@ -struct bgjob { +struct job { pid_t id; struct termios config; int suspended; }; -extern struct sigaction bgaction; - -void removebg(pid_t id); -void sigchldbghandler(int sig); void initbg(void); +void removebg(struct job *j); +void sigchldbghandler(int signal); int bgfull(void); -int pushbg(struct bgjob job); -int pushbgid(pid_t id); -int peekbg(struct bgjob *job); -int searchbg(pid_t id, struct bgjob *job); +void pushbg(struct job job); +struct job *peekbg(void); +struct job *searchbg(pid_t id); void deinitbg(void); diff --git a/src/builtins/build.c b/src/builtins/build.c index 30ad344..4d2e118 100644 --- a/src/builtins/build.c +++ b/src/builtins/build.c @@ -1,27 +1,22 @@ -#include -#include #include -#include -#include #include "../../external/cbs/cbs.c" #define MAXBUILTINS 50 int main(void) { - int listfd, d; + int fd, d; DIR *dir; char *srcs[MAXBUILTINS + 2 + 1], **src, *decl; struct dirent *entry; build("./"); - if ((listfd = open("list.c", O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) - err(EXIT_FAILURE, "Unable to open/create `list.c'"); - if (!(dir = opendir("./"))) - err(EXIT_FAILURE, "Unable to open current directory"); + if ((fd = open("builtins.c", O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) + err(errno, "Unable to open/create `builtins.c'"); + if (!(dir = opendir("./"))) err(errno, "Unable to open current directory"); - dprintf(listfd, "#include \n\n#include \"builtin.h\"\n\n"); + dprintf(fd, "#include \n\n#include \"builtins.h\"\n\n"); src = srcs; errno = 0; @@ -30,29 +25,27 @@ int main(void) { || !(*src = strrchr(entry->d_name, '.')) || strcmp(*src, ".c") != 0) continue; if (!(*src = strdup(entry->d_name))) - err(EXIT_FAILURE, "Unable to duplicate directory entry"); + err(errno, "Unable to duplicate directory entry"); (*src)[strlen(*src) - 2] = '\0'; if (src - srcs == 2 + MAXBUILTINS + 1) errx(EXIT_FAILURE, "Unable to add `%s' built-in, maximum reached (%d)", *src, MAXBUILTINS); - if (strcmp(*src, "builtin") != 0 && strcmp(*src, "list") != 0) - dprintf(listfd, "int %s(char **args, size_t numargs);\n", *src); + if (strcmp(*src, "builtins") != 0) + dprintf(fd, "int %s(char **args, size_t numargs);\n", *src); ++src; } - if (errno) err(EXIT_FAILURE, "Unable to read from current directory"); + if (errno) err(errno, "Unable to read from current directory"); *src = NULL; - decl = "struct builtin builtins[] = {"; - d = (int)strlen(decl); - dprintf(listfd, "\n%s", decl); + d = (int)strlen(decl = "struct builtin *builtins = (struct builtin []){"); + dprintf(fd, "\n%s", decl); for (src = srcs; *src; ++src) - if (strcmp(*src, "builtin") != 0 && strcmp(*src, "list") != 0) - dprintf(listfd, "{\"%s\", %s},\n%*s", *src, *src, d, ""); - dprintf(listfd, "{NULL}};\n"); + if (strcmp(*src, "builtins") != 0) + dprintf(fd, "{\"%s\", %s},\n%*s", *src, *src, d, ""); + dprintf(fd, "{NULL}};\n"); - if (closedir(dir) == -1) - err(EXIT_FAILURE, "Unable to close current directory"); - if (close(listfd) == -1) err(EXIT_FAILURE, "Unable to close `list.c'"); + if (closedir(dir) == -1) err(errno, "Unable to close current directory"); + if (close(fd) == -1) err(errno, "Unable to close `builtins.c'"); cflags = LIST("-I../"); for (src = srcs; *src; ++src) compile(*src); diff --git a/src/builtins/builtin.c b/src/builtins/builtin.c deleted file mode 100644 index 870c22f..0000000 --- a/src/builtins/builtin.c +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include - -#include "builtin.h" -#include "utils.h" - -int (*getbuiltin(char *name))(char **args, size_t numargs) { - struct builtin *builtin; - - for (builtin = builtins; builtin->func; ++builtin) - if (strcmp(name, builtin->name) == 0) return builtin->func; - - return NULL; -} - -int usage(char *program, char *options) { - fprintf(stderr, "usage: %s", program); - if (options) fprintf(stderr, " %s", options); - fputc('\n', stderr); - return EXIT_FAILURE; -} diff --git a/src/builtins/builtin.h b/src/builtins/builtin.h deleted file mode 100644 index 0aeaf3a..0000000 --- a/src/builtins/builtin.h +++ /dev/null @@ -1,7 +0,0 @@ -extern struct builtin { - char *name; - int (*func)(char **args, size_t numargs); -} builtins[]; - -int (*getbuiltin(char *name))(char **args, size_t numargs); -int usage(char *program, char *options); diff --git a/src/builtins/builtins.h b/src/builtins/builtins.h new file mode 100644 index 0000000..7a6ca06 --- /dev/null +++ b/src/builtins/builtins.h @@ -0,0 +1,4 @@ +extern struct builtin { + char *name; + int (*func)(char **args, size_t numargs); +} *builtins; diff --git a/src/builtins/cd.c b/src/builtins/cd.c index acd9c4f..a8d27e2 100644 --- a/src/builtins/cd.c +++ b/src/builtins/cd.c @@ -3,7 +3,6 @@ #include #include -#include "builtin.h" #include "utils.h" int cd(char **args, size_t numargs) { @@ -15,10 +14,7 @@ int cd(char **args, size_t numargs) { path = home; break; case 2: - if (!(path = realpath(args[1], buffer))) { - note(args[1]); - return EXIT_FAILURE; - } + if (!(path = realpath(args[1], buffer))) return note(args[1]); l = strlen(buffer); buffer[l++] = '/'; buffer[l] = '\0'; @@ -27,15 +23,9 @@ int cd(char **args, size_t numargs) { return usage(args[0], "[directory]"); } - if (chdir(path) == -1) { - note(path); - return EXIT_FAILURE; - } + if (chdir(path) == -1) return note(path); - if (setenv("PWD", path, 1) == -1) { - note("Unable to set $PWD$"); - return EXIT_FAILURE; - } + if (setenv("PWD", path, 1) == -1) fatal("Unable to set $PWD$"); return EXIT_SUCCESS; } diff --git a/src/builtins/exec.c b/src/builtins/exec.c index dd07207..f05b9f4 100644 --- a/src/builtins/exec.c +++ b/src/builtins/exec.c @@ -1,25 +1,19 @@ -#include #include #include -#include #include -#include "builtin.h" #include "context.h" -#include "fg.h" -#include "signals.h" #include "utils.h" #include "which.h" extern char **environ; -void execute(struct context *c) { - if (sigprocmask(SIG_SETMASK, &childsigmask, NULL) == -1) - note("Unable to unblock TTY signals"); +int execute(struct context *c) { + if (!c->t) return EXIT_SUCCESS; + if (c->builtin) return c->builtin(c->tokens, c->numtokens); - if (c->current.builtin) exit(c->current.builtin(c->tokens, c->numtokens)); - execve(c->current.path, c->tokens, environ); - fatal("Couldn't find `%s' command", c->current.name); + execve(c->path, c->tokens, environ); + return note("Unable to run `%s' command", c->name); } int exec(char **args, size_t numargs) { @@ -28,17 +22,12 @@ int exec(char **args, size_t numargs) { if (numargs < 2) return usage(args[0], "command [args ...]"); clear(&c); - memcpy(c.tokens, args + 1, (numargs - 1) * sizeof*args); - strcpy(c.current.name, args[1]); - if (!(c.current.builtin = getbuiltin(args[1])) - && !(c.current.path = getpath(c.current.name))) { - note("Couldn't find `%s' command", args[1]); - return EXIT_FAILURE; - } - setconfig(&canonical); - execute(&c); - - /* execute() is guaranteed not to return, this statement just appeases the - * compiler */ - exit(EXIT_FAILURE); + memcpy(c.tokens, args + 1, (c.numtokens = numargs - 1) * sizeof*args); + strcpy(c.name, args[1]); + if (!(c.builtin = getbuiltin(c.name)) && !(c.path = getpath(c.name))) + return note("Unable to find `%s' command", c.name); + + deinit(); + + exit(execute(&c)); } diff --git a/src/builtins/exec.h b/src/builtins/exec.h index 6a30f53..c9a1459 100644 --- a/src/builtins/exec.h +++ b/src/builtins/exec.h @@ -1 +1 @@ -void execute(struct context *c); +int execute(struct context *c); diff --git a/src/builtins/exeunt.c b/src/builtins/exeunt.c index afaba43..e3f2177 100644 --- a/src/builtins/exeunt.c +++ b/src/builtins/exeunt.c @@ -1,6 +1,5 @@ #include -#include "builtin.h" #include "utils.h" int exeunt(char **args, size_t numargs) { diff --git a/src/builtins/fg.c b/src/builtins/fg.c index 5b94fc8..a21ec8f 100644 --- a/src/builtins/fg.c +++ b/src/builtins/fg.c @@ -2,94 +2,74 @@ #include #include #include -#include #include #include #include #include #include "bg.h" -#include "builtin.h" #include "context.h" #include "options.h" -#include "run.h" #include "signals.h" #include "utils.h" -static struct { - pid_t id; - int status, done; -} fgjob; -static struct sigaction fgaction; static pid_t pgid; -struct termios canonical; -static struct termios raw; +struct job fgjob; +static int done; -static void sigchldfghandler(int sig) { +void initfg(void) { + pid_t pid; + + pid = getpid(); + pgid = getpgrp(); + if (login && pid != pgid && setpgid(0, pgid = pid) == -1) + fatal("Unable to set process group id for self"); + + if (tcsetpgrp(STDIN_FILENO, pgid) == -1) + fatal("Unable to set self as foreground process group"); +} + +static void sigchldfghandler(int signal) { int e, s; - pid_t id; + pid_t cpid; e = errno; - while ((id = waitpid(-fgjob.id, &s, WNOHANG | WUNTRACED)) > 0) { - if (id == fgjob.id) fgjob.status = s; - if (WIFSTOPPED(s)) { - fgjob.status = s; - fgjob.done = 1; + while ((cpid = waitpid(-fgjob.id, &s, WNOHANG | WUNTRACED)) != 0) { + if (cpid == fgjob.id) { + if (WIFEXITED(s)) status = WEXITSTATUS(s); + else if (WIFSTOPPED(s)) { + status = WSTOPSIG(s); + fgjob.suspended = 1; + } else status = WTERMSIG(s); + } + if (cpid == -1 || WIFSTOPPED(s)) { + done = 1; break; } } - if (id == -1) fgjob.done = 1; - sigchldbghandler(sig); + sigchldbghandler(signal); errno = e; } -int setconfig(struct termios *mode) { - if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) { - note("Unable to configure TTY"); - return 0; +int runfg(void) { + struct termios canonical; + + if (tcgetattr(STDIN_FILENO, &canonical) == -1) + fatal("Unable to save TTY mode"); + if (tcsetattr(STDIN_FILENO, TCSANOW, &fgjob.config) == -1) + fatal("Unable to set TTY mode for job %d", fgjob.id); + if (tcsetpgrp(STDIN_FILENO, fgjob.id) == -1) + fatal("Unable to set foreground process group for job %d", fgjob.id); + if (fgjob.suspended) { + if (killpg(fgjob.id, SIGCONT) == -1) fatal("Unable to wake job %d", fgjob.id); + fgjob.suspended = 0; } - return 1; -} - -void initfg(void) { - pid_t pid; - - fgaction = (struct sigaction){.sa_handler = sigchldfghandler}; - pid = getpid(); - pgid = getpgrp(); - if (login && pid != pgid && setpgid(0, pgid = pid) == -1) exit(errno); - - if (tcsetpgrp(STDIN_FILENO, pgid) == -1 - || tcgetattr(STDIN_FILENO, &canonical) == -1) - exit(errno); - raw = canonical; - raw.c_lflag &= ~(ICANON | ECHO); - if (!setconfig(&raw)) exit(EXIT_FAILURE); -} - -int runfg(pid_t id) { - struct bgjob job; - - if (!searchbg(id, &job)) job = (struct bgjob){.id = id, .config = canonical}; - if (!setconfig(&job.config)) return 0; - if (tcsetpgrp(STDIN_FILENO, id) == -1) { - note("Unable to bring job %d to foreground", id); - setconfig(&raw); - return 0; - } - if (killpg(id, SIGCONT) == -1) { - note("Unable to wake up job %d", id); - return 0; - } - removebg(id); - - /* The handler in `fgaction' is really what reaps the foreground process; the - * `sigsuspend()' just blocks the current thread of execution until the - * foreground process has been reaped */ - fgjob.id = id; - setsig(SIGCHLD, &fgaction); - while (!fgjob.done) { + /* `sigchldfghandler()' is what reaps the foreground process. `sigsuspend()' + * just blocks the current thread of execution until that foreground process + * has been reaped. */ + sethandler(SIGCHLD, sigchldfghandler); + while (!done) { sigsuspend(&shellsigmask); if (sigquit) { deinit(); @@ -98,62 +78,49 @@ int runfg(pid_t id) { if (sigint) sigint = 0; if (sigwinch) sigwinch = 0; } - setsig(SIGCHLD, &defaultaction); - fgjob.done = errno = 0; + sethandler(SIGCHLD, SIG_DFL); + done = errno = 0; - if (tcsetpgrp(STDIN_FILENO, pgid) == -1) { - deinit(); - exit(errno); + if (tcsetpgrp(STDIN_FILENO, pgid) == -1) + fatal("Unable to set self as foreground process group"); + if (tcgetattr(STDIN_FILENO, &fgjob.config) == -1) + fatal("Unable to save TTY mode from job %d", fgjob.id); + if (tcsetattr(STDIN_FILENO, TCSANOW, &canonical) == -1) + fatal("Unable to restore TTY mode"); + + if (!fgjob.suspended) return status; + + if (!bgfull()) { + pushbg(fgjob); + return status; } - if (tcgetattr(STDIN_FILENO, &job.config) == -1) - note("Unable to save termios config of job %d", id); - setconfig(&raw); - - if (WIFEXITED(fgjob.status)) status = WEXITSTATUS(fgjob.status); - else if (WIFSTOPPED(fgjob.status)) { - status = WSTOPSIG(fgjob.status); - job.suspended = 1; - if (!pushbg(job)) { - note("Unable to suspend current job; too many background jobs\n" - "(Press any key to continue)"); - getchar(); - return runfg(id); - } - } else status = WTERMSIG(fgjob.status); - return 1; + note("Unable to suspend current job; too many background jobs\n" + "(Press any key to continue)"); + getchar(); + return runfg(); } int fg(char **args, size_t numargs) { - struct bgjob job; + struct job *j; long l; - pid_t id; switch (numargs) { case 1: - if (!peekbg(&job)) { - note("No job to bring into the foreground"); - return EXIT_FAILURE; - } - id = job.id; + if (!(j = peekbg())) + return note("No suspended jobs to run in the foreground"); break; case 2: - errno = 0; - if ((l = strtol(args[1], NULL, 10)) == LONG_MAX && errno || l <= 0) { - note("Invalid job id %ld", l); - return EXIT_FAILURE; - } - id = (pid_t)l; - if (!searchbg(id, NULL)) { - note("Unable to find job %d", id); - return EXIT_FAILURE; - } + if ((l = strtol(args[1], NULL, 10)) == LONG_MAX && errno || l <= 0) + return note("Invalid job id %ld", l); + if (!(j = searchbg((pid_t)l))) return note("Unable to find job %d", (pid_t)l); break; default: return usage(args[0], "[pgid]"); } - if (!runfg(id)) return EXIT_FAILURE; + fgjob = *j; + removebg(j); - return EXIT_SUCCESS; + return runfg(); } diff --git a/src/builtins/fg.h b/src/builtins/fg.h index 1308bcc..a95efd9 100644 --- a/src/builtins/fg.h +++ b/src/builtins/fg.h @@ -1,5 +1,4 @@ -extern struct termios canonical; +extern struct job fgjob; -int setconfig(struct termios *mode); void initfg(void); -int runfg(pid_t id); +int runfg(void); diff --git a/src/builtins/mode.c b/src/builtins/mode.c index a875c27..b9253cc 100644 --- a/src/builtins/mode.c +++ b/src/builtins/mode.c @@ -2,9 +2,9 @@ #include #include -#include "builtin.h" -#include "context.h" -#include "run.h" +#include "utils.h" + +int verbose; int mode(char **args, size_t numargs) { switch (numargs) { @@ -18,5 +18,6 @@ int mode(char **args, size_t numargs) { default: return usage(args[0], "[verbose | quiet]"); } + return EXIT_SUCCESS; } diff --git a/src/builtins/mode.h b/src/builtins/mode.h new file mode 100644 index 0000000..60d6807 --- /dev/null +++ b/src/builtins/mode.h @@ -0,0 +1 @@ +extern int verbose; diff --git a/src/builtins/pwd.c b/src/builtins/pwd.c index 2195382..1bd6ab2 100644 --- a/src/builtins/pwd.c +++ b/src/builtins/pwd.c @@ -4,27 +4,22 @@ #include #include -#include "builtin.h" #include "utils.h" int pwd(char **args, size_t numargs) { - char *cwd, buffer[PATH_MAX]; + char buffer[PATH_MAX], *cwd; size_t l; if (numargs != 1) return usage(args[0], NULL); - if (!(cwd = getcwd(buffer, PATH_MAX))) { - note("Unable to get current working directory"); - return EXIT_FAILURE; - } + if (!(cwd = getcwd(buffer, PATH_MAX))) + fatal("Unable to get current working directory"); if (buffer[(l = strlen(buffer)) - 1] != '/') { buffer[l++] = '/'; buffer[l] = '\0'; } - if (setenv("PWD", buffer, 1) == -1) { - note("Unable to set $PWD$"); - return EXIT_FAILURE; - } + + if (setenv("PWD", cwd, 1) == -1) fatal("Unable to set $PWD$"); puts(cwd); diff --git a/src/builtins/set.c b/src/builtins/set.c index fb85866..29e6b20 100644 --- a/src/builtins/set.c +++ b/src/builtins/set.c @@ -1,19 +1,17 @@ #include -#include "builtin.h" #include "utils.h" int set(char **args, size_t numargs) { switch (numargs) { case 3: - if (setenv(args[1], args[2], 1) == -1) { - note("Unable to set `%s' to `%s'", args[1], args[2]); - return EXIT_FAILURE; - } + if (setenv(args[1], args[2], 1) == -1) + fatal("Unable to set `%s' to `%s'", args[1], args[2]); case 2: break; default: return usage(args[0], "name [value]"); } + return EXIT_SUCCESS; } diff --git a/src/builtins/source.c b/src/builtins/source.c index 7e2890f..187df3b 100644 --- a/src/builtins/source.c +++ b/src/builtins/source.c @@ -1,9 +1,8 @@ #include #include -#include +#include #include -#include "builtin.h" #include "context.h" #include "input.h" #include "options.h" @@ -12,9 +11,9 @@ int source(char **args, size_t numargs) { struct context c; - char **vector; size_t count; - + char **vector; + if (numargs < 2) return usage(args[0], "file [args ...]"); c = (struct context){.script = args[1], .input = scriptinput}; @@ -22,33 +21,29 @@ int source(char **args, size_t numargs) { /* See comment in `src/options.c' */ args[1] = argvector[0]; - vector = argvector; count = argcount; - argvector = args + 1; + vector = argvector; argcount = numargs - 1; + argvector = args + 1; while (run(&c)); argvector = vector; argcount = count; - return EXIT_SUCCESS; + return status; } void config(char *name) { char path[PATH_MAX]; int fd; - if (!catpath(home, name, path)) return; + strcpy(path, home); + strcat(path, name); - if ((fd = open(path, O_RDONLY | O_CREAT, 0644)) == -1) { - note("Unable to create `%s'", path); - return; - } - if (close(fd) == -1) { - note("Unable to close `%s'", path); - return; - } + if ((fd = open(path, O_RDONLY | O_CREAT, 0644)) == -1) + fatal("Unable to create `%s'", path); + if (close(fd) == -1) fatal("Unable to close `%s'", path); source((char *[]){"source", path, NULL}, 2); } diff --git a/src/builtins/unalias.c b/src/builtins/unalias.c index 2e18ad0..22437be 100644 --- a/src/builtins/unalias.c +++ b/src/builtins/unalias.c @@ -1,9 +1,10 @@ #include #include "alias.h" -#include "builtin.h" +#include "utils.h" int unalias(char **args, size_t numargs) { if (numargs != 2) return usage(args[0], "name"); + return removealias(args[1]) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/builtins/unset.c b/src/builtins/unset.c index f3b0061..7e36626 100644 --- a/src/builtins/unset.c +++ b/src/builtins/unset.c @@ -1,15 +1,11 @@ #include -#include "builtin.h" #include "utils.h" int unset(char **args, size_t numargs) { if (numargs != 2) return usage(args[0], "name"); - if (unsetenv(args[1]) == -1) { - note("Unable to unset `%s'", args[1]); - return EXIT_FAILURE; - } + if (unsetenv(args[1]) == -1) fatal("Unable to unset `%s'", args[1]); return EXIT_SUCCESS; } diff --git a/src/builtins/which.c b/src/builtins/which.c index 3016d87..fda9ff5 100644 --- a/src/builtins/which.c +++ b/src/builtins/which.c @@ -6,48 +6,53 @@ #include #include "alias.h" -#include "builtin.h" +#include "builtins.h" #include "utils.h" +int (*getbuiltin(char *name))(char **args, size_t numargs) { + struct builtin *b; + + for (b = builtins; b->func; ++b) + if (strcmp(name, b->name) == 0) return b->func; + + return NULL; +} + static int exists(char *path) { struct stat pstat; mode_t mask; - if (stat(path, &pstat) != -1) { - mask = S_IFREG | S_IXUSR; - if ((pstat.st_mode & mask) == mask) return 1; - } else if (errno != ENOENT) note("Unable to check if `%s' exists", path); - else errno = 0; + if (stat(path, &pstat) == -1) return errno = 0; + + mask = S_IFREG | S_IXUSR; + if ((pstat.st_mode & mask) != mask) return 0; - return 0; + return 1; } char *getpath(char *file) { - char *slash, *entry, *end, dir[PATH_MAX]; + char *slash, *entry, *end; size_t l; static char path[PATH_MAX]; if (!(slash = strchr(file, '/'))) { - if (!(entry = getenv("PATH"))) { - note("Unable to examine $PATH$"); - return NULL; - } + if (!(entry = getenv("PATH"))) fatal("Unable to examine $PATH$"); for (end = entry; end; entry = end + 1) { l = (end = strchr(entry, ':')) ? end - entry : strlen(entry); - strncpy(dir, entry, l); - if (dir[l - 1] != '/') dir[l++] = '/'; - dir[l] = '\0'; - if (!catpath(dir, file, path)) return NULL; + strncpy(path, entry, l); + if (path[l - 1] != '/') path[l++] = '/'; + path[l] = '\0'; + strcat(path, file); if (exists(path)) return path; } } - if (!realpath(file, path)) { - if (errno != ENOENT) note("Unable to expand `%s'", file); else errno = 0; + if (!realpath(file, path) || !exists(path)) { + errno = 0; return NULL; } - return exists(path) ? path : NULL; + return path; } int which(char **args, size_t numargs) { diff --git a/src/builtins/which.h b/src/builtins/which.h index f645a0f..485766d 100644 --- a/src/builtins/which.h +++ b/src/builtins/which.h @@ -1 +1,2 @@ +int (*getbuiltin(char *name))(char **args, size_t numargs); char *getpath(char *file); diff --git a/src/context.c b/src/context.c index 276458b..93d910f 100644 --- a/src/context.c +++ b/src/context.c @@ -1,14 +1,16 @@ -#include +#include #include "context.h" #include "input.h" +int status; + int clear(struct context *c) { c->b = NULL; c->t = NULL; c->r = NULL; - c->current.name[0] = c->buffer[0] = '\0'; - c->previous.term = c->current.term = SEMI; + c->name[0] = c->buffer[0] = '\0'; + c->previous.type = c->current.type = ';'; return 1; } @@ -16,5 +18,6 @@ int clear(struct context *c) { int quit(struct context *c) { clear(c); + if (status == EXIT_SUCCESS) status = EXIT_FAILURE; return c->input == userinput; } diff --git a/src/context.h b/src/context.h index 1e42574..a364ee3 100644 --- a/src/context.h +++ b/src/context.h @@ -1,39 +1,30 @@ #define MAXCHARS 1000 -#define MAXCOMMANDS (MAXCHARS + 1) / 2 #define MAXREDIRECTS (MAXCHARS / 3) -struct redirect { - enum { - NONE, - READ = '<', - READWRITE, - WRITE = '>', - APPEND, - } mode; - int oldfd, newfd; - char *oldname; -}; - -struct command { - char name[MAXCHARS + 1], *path; - int (*builtin)(char **args, size_t numargs), pipe[2]; - enum { - SEMI, - BG = '&', - AND, - PIPE = '|', - OR, - } term; -}; - struct context { char *string, *script, *map, buffer[MAXCHARS + 1 + 1], *b, - *tokens[MAXCOMMANDS + 1], **t; + *tokens[(MAXCHARS + 1) / 2 + 1], **t, name[MAXCHARS + 1], *path; size_t maplen, numtokens; - int (*input)(struct context *c), alias; - struct redirect redirects[MAXREDIRECTS + 1], *r; - struct command current, previous; + int (*input)(struct context *c), alias, + (*builtin)(char **args, size_t numargs); + struct { + enum { + NONE, + READ = '<', + READWRITE, + WRITE = '>', + APPEND, + } mode; + int oldfd, newfd; + char *oldname; + } redirects[MAXREDIRECTS + 1], *r; + struct { + char type; + int pipe[2]; + } previous, current; }; +extern int status; + int clear(struct context *c); int quit(struct context *c); diff --git a/src/history.c b/src/history.c index 717cfbf..fe966c0 100644 --- a/src/history.c +++ b/src/history.c @@ -1,40 +1,35 @@ #include #include #include -#include #include #include #include "context.h" -#include "options.h" #include "utils.h" #define MAXHIST 100 -#define INC(x) (history.x = (history.x + 1) % (MAXHIST + 1)) -#define DEC(x) (history.x = (history.x + MAXHIST) % (MAXHIST + 1)) +static char history[MAXHIST + 1][MAXCHARS + 1], *b, *s, *c, *t, path[PATH_MAX]; -static struct { - char path[PATH_MAX], entries[MAXHIST + 1][MAXCHARS + 1]; - size_t b, s, c, t; -} history; +static char *inc(char **x) { + return *x = (*x == history[MAXHIST] ? history[0] : *x + MAXCHARS + 1); +} static void readhistory(FILE *file) { - history.b = history.t = 0; - while (fgets(history.entries[history.t], sizeof*history.entries, file)) { - history.entries[history.t][strlen(history.entries[history.t]) - 1] = '\0'; - if (INC(t) == history.b) INC(b); + b = t = history[0]; + while (fgets(t, MAXCHARS + 1, file)) { + t[strlen(t) - 1] = '\0'; + if (inc(&t) == b) inc(&b); } - history.s = history.c = history.t; + s = c = t; } void inithistory(void) { FILE *file; - if (!interactive) return; - - if (!catpath(home, ".thushistory", history.path)) exit(EXIT_FAILURE); - if (!(file = fopen(history.path, "r"))) { + strcpy(path, home); + strcat(path, ".thushistory"); + if (!(file = fopen(path, "r"))) { if (errno == ENOENT) return; fatal("Unable to open history file for reading"); } @@ -43,37 +38,35 @@ void inithistory(void) { if (fclose(file) == EOF) fatal("Unable to close history file"); } +static char *dec(char **x) { + return *x = (*x == history[0] ? history[MAXHIST] : *x - (MAXCHARS + 1)); +} + int gethistory(int back, char *buffer) { - if (history.c == (back ? history.b : history.t)) return 0; + if (c == (back ? b : 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); + if (strcmp(c, buffer) != 0) strcpy(t, buffer); - strcpy(buffer, history.entries[back ? DEC(c) : INC(c)]); + strcpy(buffer, back ? dec(&c) : inc(&c)); return 1; } void addhistory(char *buffer) { if (buffer) { - strcpy(history.entries[history.t], buffer); - if (INC(t) == history.b) INC(b); - if (history.t == history.s) INC(s); + strcpy(t, buffer); + if (inc(&t) == b) inc(&b); + if (t == s) inc(&s); } - *history.entries[history.c = history.t] = '\0'; + *(c = t) = '\0'; } static void writehistory(FILE *file) { - 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; - } - if (fputc('\n', file) == EOF) { - note("Unable to terminate line of history file"); - break; - } + for (c = b; c != t; inc(&c)) { + if (fputs(c, file) == EOF) fatal("Unable to write to history file"); + if (fputc('\n', file) == EOF) + fatal("Unable to create new line in history file"); } } @@ -81,27 +74,17 @@ void deinithistory(void) { int fd; FILE *file; - if (!interactive) return; - - if ((fd = open(history.path, O_WRONLY | O_CREAT | O_APPEND, 0600)) == -1) { - note("Unable to open history file for writing"); - return; - } - if (!(file = fdopen(fd, "a"))) { - note("Unable to open history file descriptor as FILE pointer"); - return; - } - history.b = history.s; + if ((fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0600)) == -1) + fatal("Unable to open history file for writing"); + if (!(file = fdopen(fd, "a"))) + fatal("Unable to associate a new stream with the history file"); + b = s; writehistory(file); - if (!freopen(history.path, "r", file)) { - note("Unable to reopen history file for reading"); - return; - } + if (!freopen(path, "r", file)) + fatal("Unable to reopen history file for reading"); readhistory(file); - if (!freopen(history.path, "w", file)) { - note("Unable to reopen history file for writing"); - return; - } + if (!freopen(path, "w", file)) + fatal("Unable to reopen history file for writing"); writehistory(file); - if (fclose(file) == EOF) note("Unable to close history stream"); + if (fclose(file) == EOF) fatal("Unable to close history file"); } diff --git a/src/input.c b/src/input.c index 81ebaa7..4b751a3 100644 --- a/src/input.c +++ b/src/input.c @@ -1,11 +1,12 @@ #include -#include #include #include #include +#include #include #include #include +#include #include #include "context.h" @@ -14,18 +15,16 @@ #include "signals.h" #include "utils.h" -#define OFFSET(x) ((promptlen + x - start) % window.ws_col) - -static char *end, *cursor, *start; -static struct winsize window; +static char *end, *start, *cursor; static size_t promptlen; +static struct winsize window; int stringinput(struct context *c) { size_t l; if (!c->string[0]) { if (c->script && munmap(c->map, c->maplen) == -1) - note("Unable to unmap memory associated with `%s'", c->script); + fatal("Unable to unmap memory associated with `%s'", c->script); return 0; } @@ -33,10 +32,7 @@ int stringinput(struct context *c) { while (*end && *end != '\n') ++end; l = end - c->string; while (*end == '\n') ++end; - if (l > MAXCHARS) { - note("Line too long, exceeds %d character limit", MAXCHARS); - return 0; - } + if (l > MAXCHARS) fatal("Line too long, exceeds %d character limit", MAXCHARS); strncpy(c->buffer, c->string, l); c->buffer[l++] = ';'; @@ -50,24 +46,14 @@ int scriptinput(struct context *c) { int fd; struct stat sstat; - if ((fd = open(c->script, O_RDONLY)) == -1) { - note("Unable to open `%s'", c->script); - return 0; - } - if (stat(c->script, &sstat) == -1) { - note("Unable to stat `%s'", c->script); - return 0; - } + if ((fd = open(c->script, O_RDONLY)) == -1) + fatal("Unable to open `%s'", c->script); + if (stat(c->script, &sstat) == -1) fatal("Unable to stat `%s'", c->script); if ((c->maplen = sstat.st_size) == 0) return 0; if ((c->map = mmap(NULL, c->maplen, PROT_READ, MAP_PRIVATE, fd, 0)) - == MAP_FAILED) { - note("Unable to memory map `%s'", c->script); - return 0; - } - if (close(fd) == -1) { - note("Unable to close `%s'", c->script); - return 0; - } + == MAP_FAILED) + fatal("Unable to memory map `%s'", c->script); + if (close(fd) == -1) fatal("Unable to close `%s'", c->script); c->string = c->map; c->input = stringinput; @@ -79,16 +65,20 @@ int scriptinput(struct context *c) { return c->input(c); } +static size_t offset(char *x) { + return (promptlen + x - start) % window.ws_col; +} + static void moveright(void) { putchar(*cursor); - if (OFFSET(cursor++) == window.ws_col - 1) putchar('\n'); + if (offset(cursor++) == window.ws_col - 1) putchar('\n'); } static void prompt(void) { char *p, *oldstart, *oldcursor; - if (!(p = getenv("PROMPT")) && setenv("PROMPT", p = "> ", 1) == -1) - note("Unable to update $PROMPT$ environment variable"); + if (!(p = getenv("PROMPT"))) + fatal("Unable to access $PROMPT$ environment variable"); oldstart = start; oldcursor = cursor; @@ -103,7 +93,7 @@ static void prompt(void) { } static void moveleft(void) { - if (OFFSET(cursor--)) putchar('\b'); + if (offset(cursor--)) putchar('\b'); else printf("\033[A\033[%dC", window.ws_col - 1); } @@ -111,8 +101,8 @@ static void newline(void) { size_t i; for (i = (promptlen + end - cursor) / window.ws_col; i > 0; --i) putchar('\n'); - if (OFFSET(cursor) > OFFSET(end)) putchar('\n'); - if (OFFSET(end)) putchar('\n'); + if (offset(cursor) > offset(end)) putchar('\n'); + if (offset(end)) putchar('\n'); putchar('\r'); fflush(stdout); } @@ -137,11 +127,20 @@ int userinput(struct context *c) { DEL = '\177', }; + struct termios canonical, raw; int current; char *oldcursor, *oldend; clear(c); end = cursor = start = c->buffer; + + if (tcgetattr(STDIN_FILENO, &canonical) == -1) + fatal("Unable to save TTY mode"); + raw = canonical; + raw.c_lflag &= ~(ICANON | ECHO); + if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) + fatal("Unable to set raw TTY mode for input"); + while (start == end) { prompt(); while ((current = getchar()) != '\n') switch (current) { @@ -166,6 +165,11 @@ int userinput(struct context *c) { if (sigquit) { case EOT: newline(); + + if (tcsetattr(STDIN_FILENO, TCSANOW, &canonical) == -1) + fatal("Unable to restore TTY mode"); + + status = EXIT_SUCCESS; return 0; } if (sigint) { @@ -179,6 +183,9 @@ int userinput(struct context *c) { addhistory(NULL); } + + errno = 0; + break; case FF: oldcursor = cursor; @@ -259,6 +266,9 @@ int userinput(struct context *c) { newline(); } + if (tcsetattr(STDIN_FILENO, TCSANOW, &canonical) == -1) + fatal("Unable to restore TTY mode"); + while (*start == ' ') ++start; if (start == end) return quit(c); while (*(end - 1) == ' ') --end; diff --git a/src/main.c b/src/main.c index c87dfc7..acb6ca6 100644 --- a/src/main.c +++ b/src/main.c @@ -18,5 +18,5 @@ int main(int argc, char **argv) { while (run(&c)); deinit(); - return EXIT_SUCCESS; + return status; } diff --git a/src/options.c b/src/options.c index 48d3017..5b4aed1 100644 --- a/src/options.c +++ b/src/options.c @@ -2,14 +2,13 @@ #include #include -#include "builtin.h" #include "context.h" #include "input.h" #include "utils.h" +size_t argcount; char **argvector; int login, interactive; -size_t argcount; void options(int argc, char **argv, struct context *c) { char *p, *message = "[file | -c string] [arg ...] [-hl]\n" @@ -19,15 +18,14 @@ void options(int argc, char **argv, struct context *c) { " -l Run as a login shell"; int opt; - argvector = argv; - if (argvector[0][0] == '-') { + argcount = argc; + if ((argvector = argv)[0][0] == '-') { ++argvector[0]; login = 1; } if ((p = strrchr(argvector[0], '/'))) argvector[0] = p + 1; opt = 0; - argcount = argc; interactive = 1; c->input = userinput; while (opt != 'c' && (opt = getopt(argcount, argvector, ":c:hl")) != -1) diff --git a/src/options.h b/src/options.h index a14e372..c08d54d 100644 --- a/src/options.h +++ b/src/options.h @@ -1,5 +1,5 @@ +extern size_t argcount; extern char **argvector; extern int login, interactive; -extern size_t argcount; void options(int argc, char **argv, struct context *c); diff --git a/src/parse.c b/src/parse.c index 3ed02ca..354b5a6 100644 --- a/src/parse.c +++ b/src/parse.c @@ -8,7 +8,6 @@ #include "alias.h" #include "context.h" #include "options.h" -#include "run.h" #include "utils.h" static void sub(struct context *c, char **tokens, size_t numtokens) { @@ -30,8 +29,8 @@ static void subalias(struct context *c) { int parse(struct context *c) { char *end, *stlend, *p, *var, term; - int quote, globbing, v, offset, globflags; - size_t prevnumglobs; + int quote, globbing, v, offset; + size_t g; long l; static glob_t globs; @@ -45,7 +44,7 @@ int parse(struct context *c) { c->previous = c->current; for (end = c->b; *end; ++end); - prevnumglobs = globbing = quote = 0; + g = globbing = quote = 0; if (globs.gl_pathc) { globfree(&globs); globs.gl_pathc = 0; @@ -57,9 +56,10 @@ int parse(struct context *c) { if (quote || c->r->mode) break; if (*c->t == c->b) c->r->newfd = *c->b == '>'; - else if ((c->r->newfd = strtol(*c->t, &stlend, 10)) < 0 - || c->r->newfd > INT_MAX || stlend != c->b) - break; + else if ((l = strtol(*c->t, &stlend, 10)) >= 0 + && l <= INT_MAX && stlend == c->b) + c->r->newfd = l; + else break; c->r->mode = *c->b; if (*(c->b + 1) == '>') { ++c->r->mode; @@ -127,8 +127,7 @@ int parse(struct context *c) { break; case '\\': - if (!quote) break; - switch (*(c->b + 1)) { + if (quote) switch (*(c->b + 1)) { default: memmove(c->b, c->b + 1, end-- - c->b); --c->b; @@ -186,9 +185,7 @@ int parse(struct context *c) { } if (globbing) { - globflags = GLOB_MARK; - if (prevnumglobs) globflags |= GLOB_APPEND; - switch (glob(*c->t, globflags, NULL, &globs)) { + switch (glob(*c->t, GLOB_MARK | (g ? GLOB_APPEND : 0), NULL, &globs)) { case GLOB_NOMATCH: note("No matches found for `%s'", *c->t); return quit(c); @@ -197,8 +194,8 @@ int parse(struct context *c) { } globbing = 0; - sub(c, globs.gl_pathv + prevnumglobs, globs.gl_pathc - prevnumglobs); - prevnumglobs = globs.gl_pathc; + sub(c, globs.gl_pathv + g, globs.gl_pathc - g); + g = globs.gl_pathc; } if (*c->t != c->b) { @@ -214,21 +211,10 @@ int parse(struct context *c) { *c->t = NULL; if (c->t != c->tokens) { c->numtokens = c->t - c->tokens; - strcpy(c->current.name, c->tokens[0]); + strcpy(c->name, c->tokens[0]); } else c->t = NULL; if (c->r == c->redirects) c->r = NULL; - switch (term) { - case '&': - case '|': - c->current.term = term; - if (*(c->b + 1) == term) { - ++c->current.term; - *++c->b = '\0'; - } - break; - case ';': - c->current.term = SEMI; - } + c->current.type = term; ++c->b; return 1; diff --git a/src/run.c b/src/run.c index ed0da59..8215959 100644 --- a/src/run.c +++ b/src/run.c @@ -8,66 +8,41 @@ #include #include "bg.h" -#include "builtin.h" #include "context.h" #include "exec.h" #include "fg.h" +#include "mode.h" #include "parse.h" #include "signals.h" #include "utils.h" #include "which.h" -int verbose, status; +static void closepipe(struct context *c, int current) { + int *fds; -static int closepipe(struct command command) { - int result; - - result = close(command.pipe[0]) == 0; - result &= close(command.pipe[1]) == 0; - if (!result) note("Unable to close `%s' pipe", command.name); - - return result; -} - -static void redirectfiles(struct redirect *r) { - int access; - - for (; r->mode; ++r) { - if (r->oldname) { - switch (r->mode) { - case READ: - default: - access = O_RDONLY; - break; - case WRITE: - access = O_WRONLY | O_CREAT | O_TRUNC; - break; - case READWRITE: - access = O_RDWR | O_CREAT | O_APPEND; - break; - case APPEND: - access = O_WRONLY | O_CREAT | O_APPEND; - } - if ((r->oldfd = open(r->oldname, access, 0644)) == -1) - fatal("Unable to open `%s'", r->oldname); - } - if (dup2(r->oldfd, r->newfd) == -1) - fatal("Unable to redirect file descriptor %d to %d", r->newfd, r->oldfd); - if (r->oldname && close(r->oldfd) == -1) - fatal("Unable to close `%s'", r->oldname); - } + fds = current ? c->current.pipe : c->previous.pipe; + if (close(fds[0]) == -1 || close(fds[1]) == -1) + fatal("Unable to close pipe %s `%s' command", + current ? "after" : "before", c->name); } int run(struct context *c) { - int islist, ispipe, ispipestart, ispipeend; - pid_t cpid, jobid; + enum { + PREVIOUS, + CURRENT, + }; + + pid_t cpid; + struct job *j; + int access; static pid_t pipeid; - setsig(SIGCHLD, &bgaction); + sethandler(SIGCHLD, sigchldbghandler); if (!parse(c)) return 0; - setsig(SIGCHLD, &defaultaction); + sethandler(SIGCHLD, SIG_DFL); if (verbose && (c->t || c->r)) { + if (c->previous.type == '|') fputs(" | ", stdout); if (c->t) { for (c->t = c->tokens; *c->t; ++c->t) { if (c->t != c->tokens) putchar(' '); @@ -83,121 +58,109 @@ int run(struct context *c) { if (c->r->oldname) fputs(c->r->oldname, stdout); else printf("&%d", c->r->oldfd); } - switch (c->current.term) { - case PIPE: - fputs(" | ", stdout); - fflush(stdout); - break; - case BG: - putchar('&'); - default: + if ((linestart = c->current.type != '|')) { + if (c->current.type == '&') putchar('&'); putchar('\n'); } } - islist = c->previous.term > BG || c->current.term > BG; - - if (!c->t) { - if (islist) { - if (c->previous.term == PIPE) { - killpg(pipeid, SIGKILL); - if (verbose) putchar('\n'); - } - note("Expected command"); - return quit(c); + if (c->t ? !(c->builtin = getbuiltin(c->name)) && !(c->path = getpath(c->name)) + : c->previous.type == '|' || c->current.type == '|') { + if (c->t) note("Unable to find `%s' command", c->name); + else note("Expected command in pipeline"); + if (c->previous.type == '|') { + if (killpg(pipeid, SIGKILL) == -1) fatal("Unable to kill job %d", pipeid); + closepipe(c, PREVIOUS); } - if (!c->r) return 1; - - if ((cpid = fork()) == -1) { - note("Unable to fork child process"); - return quit(c); - } - if (!cpid) { - redirectfiles(c->redirects); - exit(EXIT_SUCCESS); - } - waitpid(cpid, NULL, 0); - errno = 0; - - return 1; + return quit(c); } - - if (c->current.term == BG && bgfull()) { + if (!c->t && !c->r) return 1; + if (c->previous.type != '|' && c->current.type != ';' && bgfull()) { note("Unable to place job in background; too many background jobs"); return quit(c); } - if (!(c->current.builtin = getbuiltin(c->current.name)) - && !(c->current.path = getpath(c->current.name))) { - note("Couldn't find `%s' command", c->current.name); - if (c->previous.term == PIPE) killpg(pipeid, SIGKILL); - return quit(c); + + if (c->current.type == '|' && pipe(c->current.pipe) == -1) + fatal("Unable to create pipe after `%c' command", c->name); + + if (c->builtin && !c->r && c->current.type == ';') { + status = execute(c); + return 1; } - ispipe = c->previous.term == PIPE || c->current.term == PIPE; - ispipestart = ispipe && c->previous.term != PIPE; - ispipeend = ispipe && c->current.term != PIPE; + if ((cpid = fork()) == -1) fatal("Unable to fork child process"); - if (ispipe) { - if (!ispipeend && pipe(c->current.pipe) == -1) { - note("Unable to create pipe"); - if (!ispipestart) closepipe(c->previous); - return quit(c); - } - if ((cpid = fork()) == -1) { - note("Unable to fork child process"); - return quit(c); - } - if (!cpid) { - if (!ispipestart) { - if (dup2(c->previous.pipe[0], 0) == -1) - fatal("Unable to duplicate read end of `%s' pipe", c->previous.name); - if (!closepipe(c->previous)) exit(EXIT_FAILURE); - } - if (!ispipeend) { - if (dup2(c->current.pipe[1], 1) == -1) - fatal("Unable to duplicate write end of `%s' pipe", c->current.name); - if (!closepipe(c->current)) exit(EXIT_FAILURE); - } - redirectfiles(c->redirects); - execute(c); + if (cpid) { + if (!c->t) { + waitpid(cpid, NULL, 0); + errno = 0; + return 1; } - if (ispipestart) pipeid = cpid; - else if (!closepipe(c->previous)) { - killpg(pipeid, SIGKILL); - return quit(c); + + if (c->previous.type == '|' || c->current.type == '|') { + if (c->previous.type == '|') closepipe(c, PREVIOUS); else pipeid = cpid; + fgjob.id = pipeid; + } else fgjob.id = cpid; + if (setpgid(cpid, fgjob.id) == -1) + fatal("Unable to set `%s' command to process group %d", c->name, fgjob.id); + if (tcgetattr(STDIN_FILENO, &fgjob.config) == -1) + fatal("Unable to set TTY mode of `%s' command", c->name); + fgjob.suspended = 0; + + switch (c->current.type) { + case ';': + if ((j = searchbg(fgjob.id))) removebg(j); + if (runfg() != EXIT_SUCCESS) return quit(c); + break; + case '&': + case '|': + if (c->previous.type != '|') pushbg(fgjob); } - jobid = pipeid; - } else if (c->current.builtin && !c->r) { - status = c->current.builtin(c->tokens, c->numtokens); - cpid = 0; - } else if ((jobid = cpid = fork()) == -1) { - note("Unable to fork child process"); - return quit(c); - } else if (!cpid) { - redirectfiles(c->redirects); - execute(c); + + return 1; } - if (cpid) { - if (setpgid(cpid, jobid) == -1) { - if (errno != ESRCH) { - note("Unable to set pgid of `%s' command to %d", c->current.name, jobid); - if (kill(cpid, SIGKILL) == -1) - note("Unable to kill process %d; may need to manually terminate", cpid); + if (sigprocmask(SIG_SETMASK, &childsigmask, NULL) == -1) + fatal("Unable to unblock TTY signals"); + + if (c->previous.type == '|') { + if (dup2(c->previous.pipe[0], 0) == -1) + fatal("Unable to duplicate pipe read end of `%s' command", c->name); + closepipe(c, PREVIOUS); + } + if (c->current.type == '|') { + if (dup2(c->current.pipe[1], 1) == -1) + fatal("Unable to duplicate pipe write end of `%s' command", c->name); + closepipe(c, CURRENT); + } + + if (c->r) for (c->r = c->redirects; c->r->mode; ++c->r) { + if (c->r->oldname) { + switch (c->r->mode) { + case READ: + access = O_RDONLY; + break; + case WRITE: + access = O_WRONLY | O_CREAT | O_TRUNC; + break; + case READWRITE: + access = O_RDWR | O_CREAT | O_APPEND; + break; + case APPEND: + access = O_WRONLY | O_CREAT | O_APPEND; + break; + case NONE: + fatal("Unreachable mode for file redirection"); } - return quit(c); - } - if (ispipestart || c->current.term == BG) { - pushbgid(jobid); - return 1; + if ((c->r->oldfd = open(c->r->oldname, access, 0644)) == -1) + fatal("Unable to open `%s'", c->r->oldname); } - if (c->current.term != PIPE && !runfg(jobid)) return quit(c); + if (dup2(c->r->oldfd, c->r->newfd) == -1) + fatal("Unable to redirect file descriptor %d to %d", + c->r->newfd, c->r->oldfd); + if (c->r->oldname && close(c->r->oldfd) == -1) + fatal("Unable to close `%s'", c->r->oldname); } - if (status != EXIT_SUCCESS) { - if (!islist) return quit(c); - if (c->current.term == AND) return clear(c); - } else if (c->current.term == OR) return clear(c); - - return 1; + exit(execute(c)); } diff --git a/src/run.h b/src/run.h index 810bc55..6dfecfc 100644 --- a/src/run.h +++ b/src/run.h @@ -1,3 +1 @@ -extern int verbose, status; - int run(struct context *c); diff --git a/src/signals.c b/src/signals.c index 3f4318d..072a44e 100644 --- a/src/signals.c +++ b/src/signals.c @@ -1,59 +1,50 @@ #include -#include #include -#include #include "utils.h" int sigquit, sigint, sigwinch; sigset_t shellsigmask, childsigmask; -struct sigaction defaultaction; -void setsig(int sig, struct sigaction *act) { - if (sigaction(sig, act, NULL) == -1) - fatal("Unable to install %s handler", strsignal(sig)); +void sethandler(int signal, void (*handler)(int signal)) { + struct sigaction action; + + action = (struct sigaction){.sa_handler = handler}; + if (sigaction(signal, &action, NULL) == -1) + fatal("Unable to install %s handler", strsignal(signal)); } -static void sigquithandler(int sig) { - (void)sig; +static void sigquithandler(int signal) { + (void)signal; sigquit = 1; } -static void siginthandler(int sig) { - (void)sig; +static void siginthandler(int signal) { + (void)signal; sigint = 1; } -static void sigwinchhandler(int sig) { - (void)sig; +static void sigwinchhandler(int signal) { + (void)signal; sigwinch = 1; } void initsignals(void) { - struct sigaction action; + int *signals; sigemptyset(&shellsigmask); - sigaddset(&shellsigmask, SIGTSTP); - sigaddset(&shellsigmask, SIGTTIN); - sigaddset(&shellsigmask, SIGTTOU); - if (sigprocmask(SIG_BLOCK, &shellsigmask, &childsigmask) == -1) exit(errno); - - defaultaction = (struct sigaction){.sa_handler = SIG_DFL}; - setsig(SIGTSTP, &defaultaction); - setsig(SIGTTOU, &defaultaction); - setsig(SIGTTIN, &defaultaction); - - action = (struct sigaction){.sa_handler = sigquithandler}; - setsig(SIGHUP, &action); - setsig(SIGQUIT, &action); - setsig(SIGTERM, &action); - - action = (struct sigaction){.sa_handler = siginthandler}; - setsig(SIGINT, &action); - - action = (struct sigaction){.sa_handler = sigwinchhandler}; - setsig(SIGWINCH, &action); + for (signals = (int []){SIGTSTP, SIGTTOU, SIGTTIN, 0}; *signals; ++signals) { + sigaddset(&shellsigmask, *signals); + sethandler(*signals, SIG_DFL); + } + if (sigprocmask(SIG_BLOCK, &shellsigmask, &childsigmask) == -1) + fatal("Unable to block TTY signals"); + + for (signals = (int []){SIGHUP, SIGQUIT, SIGTERM, 0}; *signals; ++signals) + sethandler(*signals, sigquithandler); + sethandler(SIGINT, siginthandler); + sethandler(SIGWINCH, sigwinchhandler); } diff --git a/src/signals.h b/src/signals.h index 14961b6..1098398 100644 --- a/src/signals.h +++ b/src/signals.h @@ -1,6 +1,5 @@ -extern int sigwinch, sigquit, sigint; +extern int sigquit, sigint, sigwinch; extern sigset_t shellsigmask, childsigmask; -extern struct sigaction defaultaction; -void setsig(int sig, struct sigaction *act); +void sethandler(int signal, void (*handler)(int signal)); void initsignals(void); diff --git a/src/utils.c b/src/utils.c index af3f85d..b851b1c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -16,45 +15,66 @@ #include "options.h" #include "signals.h" +int linestart = 1; char *home; -void note(char *fmt, ...) { +int usage(char *program, char *options) { + if (!linestart) { + fputc('\n', stderr); + linestart = 1; + } + + fprintf(stderr, "usage: %s", program); + if (options) fprintf(stderr, " %s", options); + fputc('\n', stderr); + + return EXIT_FAILURE; +} + +static void print(FILE *file, char *format, va_list args) { + if (!linestart) { + fputs("\r\n", file); + linestart = 1; + } + + fprintf(file, "%s: ", argvector[0]); + vfprintf(file, format, args); + if (errno) fprintf(file, ": %s", strerror(errno)); + fputs("\r\n", file); +} + +int note(char *format, ...) { va_list args; + int result; - fprintf(stderr, "%s: ", argvector[0]); - va_start(args, fmt); - vfprintf(stderr, fmt, args); + va_start(args, format); + print(stdout, format, args); va_end(args); - if (errno) { - fprintf(stderr, ": %s", strerror(errno)); - errno = 0; - } - putchar('\n'); + + result = errno ? errno : EXIT_FAILURE; + errno = 0; + return result; } -void fatal(char *fmt, ...) { +void fatal(char *format, ...) { va_list args; - fprintf(stderr, "%s: ", argvector[0]); - va_start(args, fmt); - vfprintf(stderr, fmt, args); + va_start(args, format); + print(stderr, format, args); va_end(args); - if (errno) fprintf(stderr, ": %s", strerror(errno)); - putchar('\n'); - exit(errno); + exit(errno ? errno : EXIT_FAILURE); } void init(void) { char *shlvlstr, buffer[PATH_MAX]; - size_t l; long shlvl; + size_t l; if (!(shlvlstr = getenv("SHLVL"))) shlvlstr = "0"; if ((shlvl = strtol(shlvlstr, NULL, 10)) < 0) shlvl = 0; sprintf(buffer, "%ld", ++shlvl); - if (setenv("SHLVL", buffer, 1) == -1) - note("Unable to update $SHLVL$ environment variable"); + if (setenv("SHLVL", buffer, 1) == -1) fatal("Unable to set $SHLVL$"); if (!(home = getenv("HOME"))) fatal("Unable to find home directory"); if (home[(l = strlen(home)) - 1] != '/') { @@ -62,7 +82,7 @@ void init(void) { buffer[l++] = '/'; buffer[l] = '\0'; if (setenv("HOME", buffer, 1) == -1 || !(home = getenv("HOME"))) - note("Unable to append trailing slash to $HOME$"); + fatal("Unable to append trailing slash to $HOME$"); } if (!getcwd(buffer, PATH_MAX)) fatal("Unable to find current directory"); @@ -70,40 +90,22 @@ void init(void) { buffer[l++] = '/'; buffer[l] = '\0'; if (setenv("PWD", buffer, 1) == -1) - note("Unable to append trailing slash to $PWD$"); + fatal("Unable to append trailing slash to $PWD$"); if (shlvl == 1 && setenv("PATH", "/usr/local/bin/:/usr/local/sbin/" ":/usr/bin/:/usr/sbin/:/bin/:/sbin/", 1) == -1) - note("Unable to initialize $PATH$"); + fatal("Unable to set $PATH$"); getcolumns(); initsignals(); initfg(); initbg(); - inithistory(); -} - -char *catpath(char *dir, char *filename, char *buffer) { - size_t l; - int slash; - - slash = dir[(l = strlen(dir)) - 1] == '/'; - if (l + 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; + if (interactive) inithistory(); } char *quoted(char *token) { - char *p, *end, quote; enum { NONE, DOUBLE, @@ -111,6 +113,7 @@ char *quoted(char *token) { ESCAPEDOUBLE, ANY, } degree; + char *p, *end, quote; static char buffer[MAXCHARS + 1]; if (!token[0]) return "\"\""; @@ -163,7 +166,6 @@ char *quoted(char *token) { } void deinit(void) { - deinithistory(); + if (interactive) deinithistory(); deinitbg(); - setconfig(&canonical); } diff --git a/src/utils.h b/src/utils.h index e7b8a95..dcbcd5d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,8 +1,9 @@ +extern int linestart; extern char *home; -void note(char *fmt, ...); -void fatal(char *fmt, ...); +int usage(char *program, char *options); +int note(char *format, ...); +void fatal(char *format, ...); void init(void); -char *catpath(char *dir, char *filename, char *buffer); char *quoted(char *token); void deinit(void); diff --git a/tools/install.c b/tools/install.c index 6ceb77f..b2c0f81 100644 --- a/tools/install.c +++ b/tools/install.c @@ -22,12 +22,12 @@ int main(int argc, char **argv) { } else strcpy(path, "/usr/local/"); strcat(path, "bin/"); - if ((cpid = fork()) == -1) err(EXIT_FAILURE, "Unable to fork"); + if ((cpid = fork()) == -1) err(errno, "Unable to fork"); if (!cpid) run("/bin/mkdir", LIST("mkdir", "-p", path), "create", path); await(cpid, "create", path); strcat(path, "thus"); - if ((cpid = fork()) == -1) err(EXIT_FAILURE, "Unable to fork"); + if ((cpid = fork()) == -1) err(errno, "Unable to fork"); if (!cpid) run("/bin/cp", LIST("cp", "-f", "bin/thus", path), "copy", "bin/thus"); await(cpid, "copy", "bin/thus"); diff --git a/tools/uninstall.c b/tools/uninstall.c index a8b190f..c1feeea 100644 --- a/tools/uninstall.c +++ b/tools/uninstall.c @@ -1,17 +1,18 @@ -#include - #include "cbs.c" -/* C preprocessor being finicky */ -#define STR(x) STRINGIFY(x) +/* Because of the C preprocessor's convoluted nature, we have to manually expand + * `PATH' with the `STR()' macro before stringify it. + * + * See "C Preprocessor Stringification" reference in `README.md' */ #define STRINGIFY(x) #x +#define STR(x) STRINGIFY(x) int main(int argc, char **argv) { pid_t cpid; - if (argc != 1) err(EXIT_FAILURE, "usage: %s\n", argv[0]); + if (argc != 1) errx(EXIT_FAILURE, "usage: %s\n", argv[0]); - if ((cpid = fork()) == -1) err(EXIT_FAILURE, "Unable to fork"); + if ((cpid = fork()) == -1) err(errno, "Unable to fork"); if (!cpid) run("/bin/rm", LIST("rm", STR(PATH), "uninstall"), "remove", STR(PATH)); await(cpid, "remove", STR(PATH)); -- 2.53.0