]> Trent Huber's Code - thus.git/commitdiff
Major refactors
authorTrent Huber <trentmhuber@gmail.com>
Mon, 15 Jun 2026 00:15:24 +0000 (20:15 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Mon, 15 Jun 2026 00:15:24 +0000 (20:15 -0400)
- 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

45 files changed:
INSTALL.md
LICENSE
README.md
external/cbs
src/build.c
src/builtins/.gitignore
src/builtins/README.md
src/builtins/alias.c
src/builtins/bg.c
src/builtins/bg.h
src/builtins/build.c
src/builtins/builtin.c [deleted file]
src/builtins/builtin.h [deleted file]
src/builtins/builtins.h [new file with mode: 0644]
src/builtins/cd.c
src/builtins/exec.c
src/builtins/exec.h
src/builtins/exeunt.c
src/builtins/fg.c
src/builtins/fg.h
src/builtins/mode.c
src/builtins/mode.h [new file with mode: 0644]
src/builtins/pwd.c
src/builtins/set.c
src/builtins/source.c
src/builtins/unalias.c
src/builtins/unset.c
src/builtins/which.c
src/builtins/which.h
src/context.c
src/context.h
src/history.c
src/input.c
src/main.c
src/options.c
src/options.h
src/parse.c
src/run.c
src/run.h
src/signals.c
src/signals.h
src/utils.c
src/utils.h
tools/install.c
tools/uninstall.c

index c220c9a9befd5be94d1440811086a3e6be3f80a4..56a3d09294860ffdd5443997f2f8ba14133272d1 100644 (file)
@@ -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 4236baab2157738305989ad46b5a17a3951e8156..5c51ff406a16f7b23035427d99f99897a256eadf 100644 (file)
--- 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
index 4e6af185788312c496bf84cd3c3b484cd053084f..61841c853a1f7e1c6fb71938a0112b1cc81230b1 100644 (file)
--- 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 (`<file`, `2>&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)
index 8ca54de4dab957a5cb5889c9b84ec41a00840e10..a2755058e34465134d49bf8b75ec2ea939158418 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 8ca54de4dab957a5cb5889c9b84ec41a00840e10
+Subproject commit a2755058e34465134d49bf8b75ec2ea939158418
index 1b0c587e60cf0c572c2969679c4cc96c0ed8117f..2d5b03b79c0a724c1c817488949e7336649e8272 100644 (file)
@@ -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;
index 3309b20c6b7cc6028ccbb3a7f709eef0e1761f4e..03bc79e88334f90064952319858752eeb3d21ba8 100644 (file)
@@ -1 +1 @@
-list.c
+builtins.c
index 575227adb58edbc8603116bbdf62f82c57b5b309..5c6fb99a49f9f99c68dc71d2c4ec83ac24c8934e 100644 (file)
@@ -10,7 +10,6 @@ built-in).
 
 #include <stdlib.h>
 
-#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.
index c5229a6d1e800df17a084f5a598ef9ce5df34b0d..40d25538a4fbfe3fb8885af7b3cd67524becbf9d 100644 (file)
@@ -2,25 +2,21 @@
 #include <stdlib.h>
 #include <string.h>
 
-#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:
index 1191c24546c7d92c8cde8474e3bab4e287b209ca..6afb6c73b335b97b7de10d2dee24a0f302a9bb3a 100644 (file)
 #include <termios.h>
 
 #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);
 
index 3d34bbe0481f76c747e7c578e7705dbc5bad51fe..948d678167f89c624112b4d96bcf01bc47b7b816 100644 (file)
@@ -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);
index 30ad34440aba556ba717d1e78ca498c90a727ab2..4d2e118e5e74c20cca2541ca89b7a35b9a2dd1b7 100644 (file)
@@ -1,27 +1,22 @@
-#include <err.h>
-#include <fcntl.h>
 #include <dirent.h>
-#include <string.h>
-#include <sys/errno.h>
 
 #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 <stddef.h>\n\n#include \"builtin.h\"\n\n");
+   dprintf(fd, "#include <stddef.h>\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 (file)
index 870c22f..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#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 (file)
index 0aeaf3a..0000000
+++ /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 (file)
index 0000000..7a6ca06
--- /dev/null
@@ -0,0 +1,4 @@
+extern struct builtin {
+   char *name;
+   int (*func)(char **args, size_t numargs);
+} *builtins;
index acd9c4f0ea71d28d6dd27b05180abc19dc31d5b6..a8d27e241b8b992c7feac4f9ef13b6476bc35bc4 100644 (file)
@@ -3,7 +3,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#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;
 }
index dd072071b986c1ce843dd7024a4dcb4b859427a5..f05b9f432be4618f034ae987bde95cff94b2f7ea 100644 (file)
@@ -1,25 +1,19 @@
-#include <signal.h>
 #include <stdlib.h>
 #include <string.h>
-#include <termios.h>
 #include <unistd.h>
 
-#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));
 }
index 6a30f5357eaffc8576600305d2eef0bfaa06757f..c9a14594153feb9734202546bbfd21d368bd39d9 100644 (file)
@@ -1 +1 @@
-void execute(struct context *c);
+int execute(struct context *c);
index afaba439894ef9747f5eeafbf11baa465849afa7..e3f2177a620473850b516ba3595a51a92208c06a 100644 (file)
@@ -1,6 +1,5 @@
 #include <stdlib.h>
 
-#include "builtin.h"
 #include "utils.h"
 
 int exeunt(char **args, size_t numargs) {
index 5b94fc865c10828f447ad35873b1c5e281c0861e..a21ec8ff8cf35c029a991208b90484a2b20da503 100644 (file)
@@ -2,94 +2,74 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <sys/errno.h>
 #include <sys/wait.h>
 #include <termios.h>
 #include <unistd.h>
 
 #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();
 }
index 1308bccdb25fc49c9180c0d18d2fea0bc4a08d21..a95efd9b990d557b5981d6b1ae3dfb586c43c940 100644 (file)
@@ -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);
index a875c279fa0b69f45125fcf9288153c717c8766f..b9253cc56de1fc7c0694b8e5c181c8db657607e5 100644 (file)
@@ -2,9 +2,9 @@
 #include <stdlib.h>
 #include <string.h>
 
-#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 (file)
index 0000000..60d6807
--- /dev/null
@@ -0,0 +1 @@
+extern int verbose;
index 21953827ab9928515e61496de4cef8bc9c60a198..1bd6ab2ca4e379425795c52fc1251255aa14bc97 100644 (file)
@@ -4,27 +4,22 @@
 #include <string.h>
 #include <unistd.h>
 
-#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);
 
index fb8586607eb76414f422e1b5b3696f1b6cd7f6c4..29e6b2010a1c4973fb5e5a88d20c46905ae55f32 100644 (file)
@@ -1,19 +1,17 @@
 #include <stdlib.h>
 
-#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;
 }
index 7e2890f2d4d42c42f6f96da73f3f7bb101b25149..187df3b698a7d5424b5e8e8a1f6bf9d7fb428547 100644 (file)
@@ -1,9 +1,8 @@
 #include <fcntl.h>
 #include <limits.h>
-#include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
-#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);
 }
index 2e18ad07da97b9184c6aab146bba6f4a4c717da6..22437be968a6bc7da3738a7581b557df5bb17f56 100644 (file)
@@ -1,9 +1,10 @@
 #include <stdlib.h>
 
 #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;
 }
index f3b0061158a96cda62245f285d018a469426e937..7e366266c02ab3717950534dc5321644233b4bd5 100644 (file)
@@ -1,15 +1,11 @@
 #include <stdlib.h>
 
-#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;
 }
index 3016d87677fe5989867eb64cb95d80cfd79c17d6..fda9ff56b0b247418486830e642edd979c0d11da 100644 (file)
@@ -6,48 +6,53 @@
 #include <sys/stat.h>
 
 #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) {
index f645a0fb2a303374ee2ac1fab05bcaf833a4b3cb..485766d4dfe2c17e09b423dd81d0894e20f6963e 100644 (file)
@@ -1 +1,2 @@
+int (*getbuiltin(char *name))(char **args, size_t numargs);
 char *getpath(char *file);
index 276458b3579808bcbe891397b649c97337cba0f8..93d910f5e0416ed31947a49e058eb1b069e236da 100644 (file)
@@ -1,14 +1,16 @@
-#include <stddef.h>
+#include <stdlib.h>
 
 #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;
 }
index 1e4257495c53c55f6075d976bada7dd576bf1adf..a364ee3147e40baa3681a0c85dec5d8136b39e36 100644 (file)
@@ -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);
index 717cfbf200a755c0238e5d612b79fb9be86eb614..fe966c08d79666293fb0b395cfd58c4bdfdd31eb 100644 (file)
@@ -1,40 +1,35 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <sys/errno.h>
 
 #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");
 }
index 81ebaa71865f4f1fcfe90ec56460629d337b6464..4b751a3853a2c06171f2e32385a92f689a949f52 100644 (file)
@@ -1,11 +1,12 @@
 #include <fcntl.h>
-#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/errno.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#include <termios.h>
 #include <unistd.h>
 
 #include "context.h"
 #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;
index c87dfc7efe98a108a3678eda45128f5828022bc9..acb6ca69e0fc0127b4b2f6a01c1be82929fa0770 100644 (file)
@@ -18,5 +18,5 @@ int main(int argc, char **argv) {
    while (run(&c));
    deinit();
 
-   return EXIT_SUCCESS;
+   return status;
 }
index 48d3017de257e148d24d5cd8924302345838405e..5b4aed1f6dd8ef03747d43786fefd74d9b2c6ecd 100644 (file)
@@ -2,14 +2,13 @@
 #include <string.h>
 #include <unistd.h>
 
-#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)
index a14e3726e50e984f66b81c1e0892f9c0d70f2e7d..c08d54de69075424fe0c397cd08f8cf6056ec4a8 100644 (file)
@@ -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);
index 3ed02ca28ffbe61bf54067cba48f8100f19a7cb0..354b5a6c8944082968e8c3a826accd0d9bd35326 100644 (file)
@@ -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;
index ed0da59551d64e41b60cadca8f56696d24b7b4f7..8215959509f4236e6c839c72a167f161538b6646 100644 (file)
--- a/src/run.c
+++ b/src/run.c
@@ -8,66 +8,41 @@
 #include <unistd.h>
 
 #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));
 }
index 810bc55f646cd041e43062c1934afd23cb69d874..6dfecfc32234c70b7b3d8b148ece868e9e8163a0 100644 (file)
--- a/src/run.h
+++ b/src/run.h
@@ -1,3 +1 @@
-extern int verbose, status;
-
 int run(struct context *c);
index 3f4318d06b2a3efd7b70d5c4dcaf49974902d622..072a44e832d6f5663c56f078e3c806878ec8cd8d 100644 (file)
@@ -1,59 +1,50 @@
 #include <signal.h>
-#include <stdlib.h>
 #include <string.h>
-#include <sys/errno.h>
 
 #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);
 }
index 14961b6f59328dca26809c0bcec0b8264441a831..10983985813893e0eaa5d164a5f5bbaf7dde7487 100644 (file)
@@ -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);
index af3f85dcf2ceb42978cc172708c03c3b134a61c2..b851b1c15d01ff77fcf58d6142e72b39f70d19e7 100644 (file)
@@ -1,5 +1,4 @@
 #include <limits.h>
-#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #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);
 }
index e7b8a950f429fd41b3242959bea37562c52678b2..dcbcd5da785c10be941efd15d1819ef7d356f0e3 100644 (file)
@@ -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);
index 6ceb77fb4c82ee1aa3a6d0d1c8ad83b48a1c2206..b2c0f81f31dcc56e4d02a9bfb70669217c178a4d 100644 (file)
@@ -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");
index a8b190f689d0e7a20488128840dc1cbbd62b11e7..c1feeea820ae525396c6ee85712b9531fbb7dfe7 100644 (file)
@@ -1,17 +1,18 @@
-#include <stdlib.h>
-
 #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));