]> Trent Huber's Code - thus.git/commitdiff
Introduce exec builtin, many bug fixes and updates
authorTrent Huber <trentmhuber@gmail.com>
Tue, 11 Nov 2025 22:12:04 +0000 (17:12 -0500)
committerTrent Huber <trentmhuber@gmail.com>
Tue, 11 Nov 2025 22:12:04 +0000 (17:12 -0500)
34 files changed:
INSTALL.md
src/builtins/README.md
src/builtins/alias.c
src/builtins/bg.c
src/builtins/build.c
src/builtins/builtin.c
src/builtins/builtin.h
src/builtins/cd.c
src/builtins/exec.c [new file with mode: 0644]
src/builtins/exec.h [new file with mode: 0644]
src/builtins/exeunt.c
src/builtins/fg.c
src/builtins/fg.h
src/builtins/list.h
src/builtins/mode.c
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/context.c
src/context.h
src/input.c
src/main.c
src/options.c
src/options.h
src/parse.c
src/run.c
src/run.h
src/utils.c
src/utils.h
tools/install.c
tools/uninstall.c

index 977b572b00e621e18d64fbd6bb20e03299893d38..c220c9a9befd5be94d1440811086a3e6be3f80a4 100644 (file)
@@ -37,19 +37,20 @@ interactive sessions, in this case, `.shrc`.
 # ~.shrc
 
 test -z "$ESCAPE" && echo $- | grep -q i && exec thus $THUSLOGIN
-```
-
-`THUSLOGIN` will be set to the `-l` flag only in the case when sh is running as
-a login shell. Running thus with the `-l` flag starts *it* as a login shell.
+unset ESCAPE
 
-Notice if sh is called from within thus, then it will run *as* thus which makes
-it impossible to open sh as an interactive subshell. This is why the `ESCAPE`
-environment variable is used as a guard in the command list. Defining `ESCAPE`
-will make all subsequent calls to sh open sh itself as an interactive shell, not
-thus.
+alias sh=~/.sh.sh
+```
 
-Since aliases take precedent over executables in the path, you could even define
-an alias to sh that runs a script that runs sh after defining `ESCAPE`.
+There are two things to note. First, `THUSLOGIN` will be set to the `-l` flag
+only in the case when sh is running as a login shell. Running thus with the `-l`
+flag starts *it* as a login shell. Second, if `ESCAPE` isn't set in the
+environment, then sh will just switch to running thus. This behavior is indeed
+what we want when we're opening an interactive shell for the first time, but
+when we're running sh as a subshell, it ends up running thus instead. This is
+why the `ESCAPE` environment variable exists. Every time we want to run sh from
+inside thus, we first need to set `ESCAPE`. This functionality can be automated
+in a script, say `~.sh.sh`.
 
 ```sh
 # ~.sh.sh
@@ -57,14 +58,18 @@ an alias to sh that runs a script that runs sh after defining `ESCAPE`.
 #! /usr/bin/env thus
 
 set ESCAPE escape
-env sh
-unset ESCAPE
+exec sh
 ```
 
-And then the alias goes in the interactive config file for thus.
+We can add an alias to this script in the interactive configuration file used by
+thus, that is `~.thusrc`. Putting it inside `~.thusrc` ensures that the alias
+only exists when running thus as an interactive session, which is exactly what
+we want. Notice that this alias was also added to the sh interactive
+configuration file earlier. This is to ensure you can still run sh as a subshell
+inside itself.
 
 ```sh
 # ~.thusrc
 
-alias sh "~.sh.sh"
+alias sh ~.sh.sh
 ```
index ab2364fe5d26a0c49cdcc23bf36baf15002477d5..0d77dbd7679da85cc050b0f67a5b09ef2486f398 100644 (file)
@@ -1,6 +1,9 @@
 # Adding a built-in shell command
 
-To add a built-in command to the shell, first make a source file in the `src/builtins/` directory with the same name as the built-in. The source file should contain at least the following code (where "foo" is the name of the built-in).
+To add a built-in command to the shell, first make a source file in the
+`src/builtins/` directory with the same name as the built-in. The source file
+should contain at least the following code (where "foo" is the name of the
+built-in).
 
 ```c
 /* foo.c */
@@ -10,13 +13,25 @@ To add a built-in command to the shell, first make a source file in the `src/bui
 #include "builtin.h"
 #include "utils.h"
 
-BUILTIN(foo) {
+int foo(char **args, size_t numargs) {
+
+    /* ... */
+
     return EXIT_SUCCESS;
 }
 ```
 
-The `BUILTIN()` macro is defined in [`builtin.h`](builtin.h) and provides an interface similar to that of `main()`, passing the arguments from the user as an array of C strings (`argv`) along with a count (`argc`). This allows you to write code for built-ins exactly as you would write them in a regular C program.
+The parameters `args` and `numargs` function essentially the same as the `argv`
+and `argc` parameters found in the prototypical C `main()` function. Arguments
+here are also passed as a NULL-terminated array of C strings.
 
-Errors should be reported to the user using the `note()` function defined in [`utils.c`](../utils.c).
+For a consistent user interface, usage messages can be shown with the `usage()`
+function, defined in [`builtin.c`](builtin.c), and errors can be explained with
+the `note()` function defined in [`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 the source is done being written, simply rebuild the shell and it will automatically incorporate the new built-in.
+Once finished, simply rebuild the shell and it will automatically incorporate
+the new built-in.
index 3a1ea4c9fa08c59ddf2b73148a9a96afd30012b6..62f3786a03c7c8dce945846d73f3ac9a0ee9c6de 100644 (file)
@@ -7,7 +7,7 @@
 #include "parse.h"
 #include "utils.h"
 
-#define MAXALIAS 25
+#define MAXALIAS 50
 
 static struct {
    struct entry {
@@ -35,22 +35,20 @@ char *getaliasvalue(char *name) {
 char **getalias(char *name) {
    char *value;
    size_t l;
-   static struct context context;
+   static struct context c;
 
    if (!(value = getaliasvalue(name))) return NULL;
 
-   while (*value == ' ') ++value;
-   strcpy(context.buffer, value);
+   strcpy(c.buffer, value);
    l = strlen(value);
-   context.buffer[l + 1] = '\0';
-   context.buffer[l] = ';';
-   context.alias = 1;
-   context.b = context.buffer;
+   c.buffer[l + 1] = '\0';
+   c.buffer[l] = ';';
+   c.b = c.buffer;
+   c.alias = 1;
 
-   if (!parse(&context)) return NULL;
-   if (!context.t) context.tokens[0] = NULL;
+   if (!parse(&c)) return NULL;
 
-   return context.tokens;
+   return c.tokens;
 }
 
 int removealias(char *name) {
@@ -59,38 +57,46 @@ int removealias(char *name) {
 
    if ((i = getindex(name)) == aliases.size) return 0;
    entry = &aliases.entries[i];
-   memmove(entry, entry + 1, (--aliases.size - i) * sizeof(*entry));
+   memmove(entry, entry + 1, (--aliases.size - i) * sizeof*entry);
    for (; i < aliases.size; ++i, ++entry)
-       entry->value = (void *)entry->value - sizeof(*entry);
+       entry->value = (void *)entry->value - sizeof*entry;
 
    return 1;
 }
 
-BUILTIN(alias) {
+int alias(char **args, size_t numargs) {
    size_t i;
+   char *end;
    struct entry *entry;
 
-   switch (argc) {
+   switch (numargs) {
    case 1:
        for (i = 0; i < aliases.size; ++i)
-           printf("%s = \"%s\"\n", aliases.entries[i].name, aliases.entries[i].value);
+           printf("%s -> %s\n", quoted(aliases.entries[i].name),
+                  aliases.entries[i].value);
        break;
    case 3:
        if (aliases.size == MAXALIAS) {
-           note("Unable to add alias `%s', maximum reached (%d)", argv[1], MAXALIAS);
+           note("Unable to add alias, maximum reached (%d)", MAXALIAS);
            return EXIT_FAILURE;
        }
+       for (i = 1; i <= 2; ++i) {
+           end = args[i] + strlen(args[i]);
+           while (*args[i] == ' ') ++args[i];
+           if (end != args[i]) while (*(end - 1) == ' ') --end;
+           *end = '\0';
+       }
 
-       entry = &aliases.entries[i = getindex(argv[1])];
+       entry = &aliases.entries[i = getindex(args[1])];
        if (i == aliases.size) {
-           strcpy(entry->name, argv[1]);
+           strcpy(entry->name, args[1]);
            ++aliases.size;
        }
-       strcpy(entry->value = entry->name + strlen(entry->name) + 1, argv[2]);
+       strcpy(entry->value = entry->name + strlen(entry->name) + 1, args[2]);
 
        break;
    default:
-       return usage(argv[0], "[name value]");
+       return usage(args[0], "[name value]");
    }
 
    return EXIT_SUCCESS;
index eec6fbd7e7391893e72fdf028578d1af690a5982..c9207edd8c0fa822ea6cc9844ae3b39124ac176a 100644 (file)
@@ -112,12 +112,12 @@ void deinitbg(void) {
    for (p = bgjobs.active; p; p = p->next) killpg(p->job.id, SIGKILL);
 }
 
-BUILTIN(bg) {
+int bg(char **args, size_t numargs) {
    struct bglink *p;
    struct bgjob job;
    long l;
 
-   switch (argc) {
+   switch (numargs) {
    case 1:
        for (p = bgjobs.active; p; p = p->next) if (p->job.suspended) {
            job = p->job;
@@ -130,7 +130,7 @@ BUILTIN(bg) {
        break;
    case 2:
        errno = 0;
-       if ((l = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || l <= 0) {
+       if ((l = strtol(args[1], NULL, 10)) == LONG_MAX && errno || l <= 0) {
            note("Invalid job id %ld", l);
            return EXIT_FAILURE;
        }
@@ -144,7 +144,7 @@ BUILTIN(bg) {
        }
        break;
    default:
-       return usage(argv[0], "[pgid]");
+       return usage(args[0], "[pgid]");
    }
 
    if (killpg(job.id, SIGCONT) == -1) {
index a032421cf54f1e69d8ef427e8f336f284d044301..40596af8d593b621a478d3e3138f65a34e861ec7 100644 (file)
@@ -37,7 +37,7 @@ int main(void) {
            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, "BUILTIN(%s);\n", *src);
+           dprintf(listfd, "int %s(char **args, size_t numargs);\n", *src);
        ++src;
    }
    if (errno) err(EXIT_FAILURE, "Unable to read from current directory");
index ccb7832eddbf7416a084d278c84400bd8e2e0311..cf2d9148f9645bc6be7ab52d0f370a536c463268 100644 (file)
@@ -6,18 +6,13 @@
 #include "list.h"
 #include "utils.h"
 
-int isbuiltin(char **args) {
+int (*getbuiltin(char *name))(char **args, size_t numargs) {
    struct builtin *builtin;
-   size_t n;
 
    for (builtin = builtins; builtin->func; ++builtin)
-       if (strcmp(args[0], builtin->name) == 0) {
-           for (n = 1; args[n]; ++n);
-           status = builtin->func(n, args);
-           return 1;
-       }
+       if (strcmp(name, builtin->name) == 0) return builtin->func;
 
-   return 0;
+   return NULL;
 }
 
 int usage(char *program, char *options) {
index 004f02f2dfeb42101e61da7f39b46950cec5549d..768ba7e1b6f74aeef253ba725d3e9540d851e4b4 100644 (file)
@@ -1,4 +1,2 @@
-#define BUILTIN(name) int name(int argc, char **argv)
-
-int isbuiltin(char **args);
+int (*getbuiltin(char *name))(char **args, size_t numargs);
 int usage(char *program, char *options);
index 0d4d856b9e8dcf2bdd0438bc38332f54976059e3..12451af612d9e6b1c7bcfb7cb1f8db26ecf3a1b7 100644 (file)
@@ -6,17 +6,17 @@
 #include "builtin.h"
 #include "utils.h"
 
-BUILTIN(cd) {
+int cd(char **args, size_t numargs) {
    char *path, buffer[PATH_MAX + 1];
    size_t l;
 
-   switch (argc) {
+   switch (numargs) {
    case 1:
        path = home;
        break;
    case 2:
-       if (!(path = realpath(argv[1], buffer))) {
-           note(argv[1]);
+       if (!(path = realpath(args[1], buffer))) {
+           note(args[1]);
            return EXIT_FAILURE;
        }
        l = strlen(buffer);
@@ -24,7 +24,7 @@ BUILTIN(cd) {
        buffer[l] = '/';
        break;
    default:
-       return usage(argv[0], "[directory]");
+       return usage(args[0], "[directory]");
    }
 
    if (chdir(path) == -1) {
diff --git a/src/builtins/exec.c b/src/builtins/exec.c
new file mode 100644 (file)
index 0000000..e786c96
--- /dev/null
@@ -0,0 +1,44 @@
+#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");
+
+   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);
+}
+
+int exec(char **args, size_t numargs) {
+   struct context c;
+
+   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 */
+   return EXIT_SUCCESS;
+}
diff --git a/src/builtins/exec.h b/src/builtins/exec.h
new file mode 100644 (file)
index 0000000..6a30f53
--- /dev/null
@@ -0,0 +1 @@
+void execute(struct context *c);
index ebb04633994cef117c9c7fd98e353933b24a957e..877ea348bf7c9f3ad19105588cb4bb2dd77e25e4 100644 (file)
@@ -3,8 +3,8 @@
 #include "builtin.h"
 #include "utils.h"
 
-BUILTIN(exeunt) {
-   if (argc != 1) return usage(argv[0], NULL);
+int exeunt(char **args, size_t numargs) {
+   if (numargs != 1) return usage(args[0], NULL);
 
    deinit();
    exit(EXIT_SUCCESS);
index dd65f23d77a238585f02fa60ad3d301508d6db79..9af112553da1ace0fbefe5abb7d769516ef72cc3 100644 (file)
@@ -12,6 +12,7 @@
 #include "builtin.h"
 #include "context.h"
 #include "options.h"
+#include "run.h"
 #include "signals.h"
 #include "utils.h"
 
@@ -42,7 +43,7 @@ static void sigchldfghandler(int sig) {
    errno = e;
 }
 
-static int setconfig(struct termios *mode) {
+int setconfig(struct termios *mode) {
    if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
        note("Unable to configure TTY");
        return 0;
@@ -86,7 +87,7 @@ int runfg(pid_t 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. */
+    * foreground process has been reaped */
    fgjob.id = id;
    setsig(SIGCHLD, &fgaction);
    while (!fgjob.done) {
@@ -113,7 +114,7 @@ int runfg(pid_t id) {
        status = WSTOPSIG(fgjob.status);
        job.suspended = 1;
        if (!pushbg(job)) {
-           note("Unable to suspend current process, too many background jobs\n"
+           note("Unable to suspend current job; too many background jobs\n"
                 "(Press any key to continue)");
            getchar();
            return runfg(id);
@@ -127,12 +128,12 @@ void deinitfg(void) {
    setconfig(&canonical);
 }
 
-BUILTIN(fg) {
+int fg(char **args, size_t numargs) {
    struct bgjob job;
    long l;
    pid_t id;
 
-   switch (argc) {
+   switch (numargs) {
    case 1:
        if (!peekbg(&job)) {
            note("No job to bring into the foreground");
@@ -142,8 +143,8 @@ BUILTIN(fg) {
        break;
    case 2:
        errno = 0;
-       if ((l = strtol(argv[1], NULL, 10)) == LONG_MAX && errno || l <= 0) {
-           note("Invalid process group id %ld", l);
+       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;
@@ -153,7 +154,7 @@ BUILTIN(fg) {
        }
        break;
    default:
-       return usage(argv[0], "[pgid]");
+       return usage(args[0], "[pgid]");
    }
 
    if (!runfg(id)) return EXIT_FAILURE;
index f1122f96ffcafd95876420ace0f50343e3410c9e..f1dab387c3e994c2b5d2134bf3e22aa88798016b 100644 (file)
@@ -1,5 +1,6 @@
 extern struct termios canonical;
 
+int setconfig(struct termios *mode);
 void initfg(void);
 int runfg(pid_t id);
 void deinitfg(void);
index 911d534d29ea18d9e8c798773194ae455445a5c6..0c4a403253b3e3c8645f5a523a3a47cb9b630c1b 100644 (file)
@@ -1,6 +1,6 @@
 struct builtin {
    char *name;
-   BUILTIN((*func));
+   int (*func)(char **args, size_t numargs);
 };
 
 extern struct builtin builtins[];
index 0d9a29df938549a39c8a10684e615da0bbac8933..fcbf67c6a9a498da32d6e419330502a9dce4a5cb 100644 (file)
@@ -6,17 +6,17 @@
 #include "context.h"
 #include "run.h"
 
-BUILTIN(mode) {
-   switch (argc) {
+int mode(char **args, size_t numargs) {
+   switch (numargs) {
    case 1:
        puts(verbose ? "verbose" : "quiet");
        break;
    case 2:
-       if (strcmp(argv[1], "verbose") == 0) verbose = 1;
-       else if (strcmp(argv[1], "quiet") == 0) verbose = 0;
+       if (strcmp(args[1], "verbose") == 0) verbose = 1;
+       else if (strcmp(args[1], "quiet") == 0) verbose = 0;
        else
        default:
-           return usage(argv[0], "[verbose | quiet]");
+           return usage(args[0], "[verbose | quiet]");
    }
 
    return EXIT_SUCCESS;
index bbde98d928e764bd1d712108d5dea49d527f54c3..c6a6a28b70e4b524f6fa616b73366776e764faac 100644 (file)
@@ -7,11 +7,11 @@
 #include "builtin.h"
 #include "utils.h"
 
-BUILTIN(pwd) {
+int pwd(char **args, size_t numargs) {
    char *cwd, buffer[PATH_MAX];
    size_t l;
 
-   if (argc != 1) return usage(argv[0], NULL);
+   if (numargs != 1) return usage(args[0], NULL);
 
    if (!(cwd = getenv("PWD"))) {
        if (!(cwd = getcwd(buffer, PATH_MAX))) {
index f8df49eddfebce95f15322ed2a98e36467cfbced..56f780d6d2d6d3f5956972dfeb8e565a16b603e3 100644 (file)
@@ -3,17 +3,17 @@
 #include "builtin.h"
 #include "utils.h"
 
-BUILTIN(set) {
-   switch (argc) {
+int set(char **args, size_t numargs) {
+   switch (numargs) {
    case 3:
-       if (setenv(argv[1], argv[2], 1) == -1) {
-           note("Unable to set %s to %s", argv[1], argv[2]);
+       if (setenv(args[1], args[2], 1) == -1) {
+           note("Unable to set %s to %s", args[1], args[2]);
            return EXIT_FAILURE;
        }
    case 2:
        break;
    default:
-       return usage(argv[0], "name [value]");
+       return usage(args[0], "name [value]");
    }
 
    return EXIT_SUCCESS;
index 676c41424630819c9577c3750651a9613a9ccf51..7e2890f2d4d42c42f6f96da73f3f7bb101b25149 100644 (file)
@@ -6,29 +6,31 @@
 #include "builtin.h"
 #include "context.h"
 #include "input.h"
+#include "options.h"
 #include "run.h"
 #include "utils.h"
 
-BUILTIN(source) {
-   struct context context;
-   int c;
-   char **v;
+int source(char **args, size_t numargs) {
+   struct context c;
+   char **vector;
+   size_t count;
    
-   if (argc == 1) return usage(argv[0], "file [args ...]");
+   if (numargs < 2) return usage(args[0], "file [args ...]");
 
-   context = (struct context){0};
-   context.script = argv[1];
-   context.input = scriptinput;
+   c = (struct context){.script = args[1], .input = scriptinput};
 
-   c = argcount;
-   v = arglist;
-   argcount = argc - 1;
-   arglist = argv + 1;
+   /* See comment in `src/options.c' */
+   args[1] = argvector[0];
 
-   while (run(&context));
+   vector = argvector;
+   count = argcount;
+   argvector = args + 1;
+   argcount = numargs - 1;
 
-   argcount = c;
-   arglist = v;
+   while (run(&c));
+
+   argvector = vector;
+   argcount = count;
 
    return EXIT_SUCCESS;
 }
@@ -48,5 +50,5 @@ void config(char *name) {
        return;
    }
 
-   source(2, (char *[]){"source", path, NULL});
+   source((char *[]){"source", path, NULL}, 2);
 }
index d906b8ec5bc62374079fa6c208039706b45b3e4b..55581ba617db83f1124a03326a3aa751d1903cb1 100644 (file)
@@ -3,8 +3,8 @@
 #include "alias.h"
 #include "builtin.h"
 
-BUILTIN(unalias) {
-   if (argc != 2) return usage(argv[0], "name");
+int unalias(char **args, size_t numargs) {
+   if (numargs != 2) return usage(args[0], "name");
 
-   return removealias(argv[1]) ? EXIT_SUCCESS : EXIT_FAILURE;
+   return removealias(args[1]) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
index d7a5679e628c648edc3a2e6d726794942b8c2478..af62aebceec6228079f607d41e523c534ac6b302 100644 (file)
@@ -3,11 +3,11 @@
 #include "builtin.h"
 #include "utils.h"
 
-BUILTIN(unset) {
-   if (argc != 2) return usage(argv[0], "name");
+int unset(char **args, size_t numargs) {
+   if (numargs != 2) return usage(args[0], "name");
 
-   if (unsetenv(argv[1]) != -1) return EXIT_SUCCESS;
+   if (unsetenv(args[1]) != -1) return EXIT_SUCCESS;
 
-   note("Unable to unset $%s$", argv[1]);
+   note("Unable to unset $%s$", args[1]);
    return EXIT_FAILURE;
 }
index 841ca6052b2de172e0f473fd1b24f24f2a7b4b57..234a20c979c7527869ba4cf424592370fee61fc2 100644 (file)
@@ -7,17 +7,8 @@
 
 #include "alias.h"
 #include "builtin.h"
-#include "list.h"
 #include "utils.h"
 
-enum {
-   ALIAS,
-   BUILTIN,
-   PATH,
-};
-
-static int type;
-
 static int exists(char *path) {
    struct stat pstat;
    mode_t mask;
@@ -33,23 +24,10 @@ static int exists(char *path) {
 
 char *getpath(char *file) {
    char *slash, *entry, *end, dir[PATH_MAX];
-   struct builtin *builtin;
    size_t l;
    static char path[PATH_MAX];
 
-   type = PATH;
    if (!(slash = strchr(file, '/'))) {
-       if ((entry = getaliasvalue(file))) {
-           type = ALIAS;
-           return entry;
-       }
-
-       for (builtin = builtins; builtin->func; ++builtin)
-           if (strcmp(file, builtin->name) == 0) {
-               type = BUILTIN;
-               return file;
-           }
-
        if (!(entry = getenv("PATH"))) {
            note("Unable to examine $PATH$");
            return NULL;
@@ -73,19 +51,18 @@ char *getpath(char *file) {
    return NULL;
 }
 
-BUILTIN(which) {
-   char *result;
+int which(char **args, size_t numargs) {
+   char *p;
 
-   if (argc != 2) return usage(argv[0], "name");
+   if (numargs != 2) return usage(args[0], "name");
 
-   if (!(result = getpath(argv[1]))) {
-       printf("%s not found\n", argv[1]);
-       return EXIT_SUCCESS;
+   if ((p = getaliasvalue(args[1]))) puts(p);
+   else if (getbuiltin(p = args[1])) printf("%s is a built-in\n", p);
+   else if ((p = getpath(args[1]))) puts(quoted(p));
+   else {
+       printf("%s not found\n", quoted(args[1]));
+       return EXIT_FAILURE;
    }
-   
-   fputs(result, stdout);
-   if (type == BUILTIN) fputs(" is built-in", stdout);
-   putchar('\n');
 
    return EXIT_SUCCESS;
 }
index 372bf50a1f895e0114a4b70de4358295e9318ffa..276458b3579808bcbe891397b649c97337cba0f8 100644 (file)
@@ -8,7 +8,7 @@ int clear(struct context *c) {
    c->t = NULL;
    c->r = NULL;
    c->current.name[0] = c->buffer[0] = '\0';
-   c->prev.term = c->current.term = SEMI;
+   c->previous.term = c->current.term = SEMI;
 
    return 1;
 }
index d69b98b0ac1d78364b0919e64dd142cf431a5c59..1e4257495c53c55f6075d976bada7dd576bf1adf 100644 (file)
@@ -2,39 +2,37 @@
 #define MAXCOMMANDS (MAXCHARS + 1) / 2
 #define MAXREDIRECTS (MAXCHARS / 3)
 
-enum {
-   NONE,
-   READ = '<',
-   READWRITE,
-   WRITE = '>',
-   APPEND,
-};
-
 struct redirect {
-   int mode, oldfd, newfd;
+   enum {
+       NONE,
+       READ = '<',
+       READWRITE,
+       WRITE = '>',
+       APPEND,
+   } mode;
+   int oldfd, newfd;
    char *oldname;
 };
 
-enum {
-   SEMI,
-   BG = '&',
-   AND,
-   PIPE = '|',
-   OR,
-};
-
 struct command {
-   char name[MAXCHARS + 1];
-   int term, pipe[2];
+   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;
-   size_t maplen;
+   size_t maplen, numtokens;
    int (*input)(struct context *c), alias;
    struct redirect redirects[MAXREDIRECTS + 1], *r;
-   struct command current, prev;
+   struct command current, previous;
 };
 
 int clear(struct context *c);
index 0e5e906def141db01dfe0f33df86b9473a42687c..e8399780026bc0823ec45c8becf30ed046df5142 100644 (file)
 
 #include "context.h"
 #include "history.h"
+#include "options.h"
 #include "signals.h"
 #include "utils.h"
 
 #define OFFSET(x) ((promptlen + (x) - start) % window.ws_col)
 
-enum {
-   CTRLD = '\004',
-   CLEAR = '\014',
-   ESCAPE = '\033',
-
-   /* See `ESCAPE' case in `userinput()' */
-   ALT = '2' + 1,
-
-   UP = 'A',
-   DOWN,
-   RIGHT,
-   LEFT,
-   FORWARD = 'f',
-   BACKWARD = 'b',
-   DEL = '\177',
-};
-
 static struct winsize window;
 static char *start, *cursor, *end;
 static size_t promptlen;
@@ -94,8 +78,8 @@ int scriptinput(struct context *c) {
    c->input = stringinput;
 
    /* We want errors from this point forward to be reported by the script, not the
-    * shell. */
-   arglist[0] = c->script;
+    * shell */
+   argvector[0] = c->script;
 
    return c->input(c);
 }
@@ -138,6 +122,23 @@ static void newline(void) {
 }
 
 int userinput(struct context *c) {
+   enum {
+       CTRLD = '\004',
+       CLEAR = '\014',
+       ESCAPE = '\033',
+
+       /* See `ESCAPE' case */
+       ALT = '2' + 1,
+
+       UP = 'A',
+       DOWN,
+       RIGHT,
+       LEFT,
+       FORWARD = 'f',
+       BACKWARD = 'b',
+       DEL = '\177',
+   };
+
    int current;
    char *oldcursor, *oldend;
 
index d7a8ffc80b2413700661f84457a0402b3cdfb8a5..09d058ca76a0536df2caf8f0ebf5f5fe7f21c087 100644 (file)
@@ -7,20 +7,18 @@
 #include "utils.h"
 
 int main(int argc, char **argv) {
-   struct context context;
+   struct context c;
 
-   argcount = argc;
-   arglist = argv;
-   context = (struct context){0};
+   c = (struct context){0};
 
-   options(&context);
+   options(argc, argv, &c);
 
    init();
 
    if (login) config(".thuslogin");
    if (interactive) config(".thusrc");
 
-   while (run(&context));
+   while (run(&c));
 
    deinit();
 
index 44b8035728eab64bcba9f817bcd4f478c07600e8..ea305e2e641af8fa72481258a66e87462d60338a 100644 (file)
@@ -7,9 +7,11 @@
 #include "input.h"
 #include "utils.h"
 
+char **argvector;
 int login, interactive;
+size_t argcount;
 
-void options(struct context *context) {
+void options(int argc, char **argv, struct context *c) {
    char *p, *message = "[file | -c string] [arg ...] [-hl]\n"
                        "    <file> [arg ...]      Run script\n"
                        "    -c <string> [arg ...] Run string\n"
@@ -17,48 +19,51 @@ void options(struct context *context) {
                        "    -l                    Run as a login shell";
    int opt;
 
-   if (arglist[0][0] == '-') {
-       ++arglist[0];
+   argvector = argv;
+
+   if (argvector[0][0] == '-') {
+       ++argvector[0];
        login = 1;
    }
-   if ((p = strrchr(arglist[0], '/'))) arglist[0] = p + 1;
+   if ((p = strrchr(argvector[0], '/'))) argvector[0] = p + 1;
    interactive = 1;
-   context->input = userinput;
+   c->input = userinput;
 
    opt = 0;
-   while (opt != 'c' && (opt = getopt(argcount, arglist, ":c:hl")) != -1)
+   argcount = argc;
+   while (opt != 'c' && (opt = getopt(argcount, argvector, ":c:hl")) != -1)
        switch (opt) {
        case 'c':
            interactive = 0;
-           context->string = optarg;
-           context->input = stringinput;
-           arglist[--optind] = arglist[0];
+           c->string = optarg;
+           c->input = stringinput;
+           argvector[--optind] = argvector[0];
            break;
        case 'h':
-           usage(arglist[0], message);
+           usage(argvector[0], message);
            exit(EXIT_SUCCESS);
        case 'l':
            login = 1;
            break;
        case ':':
            note("Expected argument following `-%c'\n", optopt);
-           exit(usage(arglist[0], message));
+           exit(usage(argvector[0], message));
        case '?':
        default:
            note("Unknown command line option `-%c'\n", optopt);
-           exit(usage(arglist[0], message));
+           exit(usage(argvector[0], message));
        }
-   if (!context->string && arglist[optind]) {
+   if (!c->string && argvector[optind]) {
        interactive = 0;
-       context->script = arglist[optind];
-       context->input = scriptinput;
+       c->script = argvector[optind];
+       c->input = scriptinput;
 
        /* Until we're able to load the script, errors need to be reported by the
-        * shell, not the script. */
-       arglist[optind] = arglist[0];
+        * shell, not the script */
+       argvector[optind] = argvector[0];
    }
    if (!interactive) {
        argcount -= optind;
-       arglist += optind;
+       argvector += optind;
    }
 }
index 825670aaaa994970c7605efd9dd934a51197af0d..a14e3726e50e984f66b81c1e0892f9c0d70f2e7d 100644 (file)
@@ -1,3 +1,5 @@
+extern char **argvector;
 extern int login, interactive;
+extern size_t argcount;
 
-void options(struct context *context);
+void options(int argc, char **argv, struct context *c);
index 695246c8de3d1df6ccf9c10f97f9eed59aadb70c..0ee28c428b54a56749cfe144f2abb3085f5f3455 100644 (file)
@@ -7,12 +7,31 @@
 
 #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) {
+   memcpy(c->t, tokens, numtokens * sizeof*c->t);
+   c->t += numtokens - 1;
+}
+
+static int subalias(struct context *c) {
+   char **tokens;
+   size_t numtokens;
+
+   if (c->alias || c->t != c->tokens || !(tokens = getalias(c->tokens[0]))) return 0;
+
+   for (numtokens = 0; tokens[numtokens]; ++numtokens);
+   sub(c, tokens, numtokens);
+
+   return 1;
+}
+
 int parse(struct context *c) {
-   int globbing, quote, v, offset, globflags;
-   size_t prevsublen, sublen;
-   char *stlend, *p, *end, *var, term, **sub;
+   char *end, *stlend, *p, *var, term;
+   int quote, globbing, v, offset, globflags;
+   size_t prevnumglobs;
    long l;
    static glob_t globs;
 
@@ -23,11 +42,10 @@ int parse(struct context *c) {
    c->t = c->tokens;
    c->r = c->redirects;
    c->r->mode = NONE;
-   c->prev = c->current;
+   c->previous = c->current;
 
    for (end = c->b; *end; ++end);
-   prevsublen = globbing = quote = 0;
-   sub = NULL;
+   prevnumglobs = globbing = quote = 0;
    if (globs.gl_pathc) {
        globfree(&globs);
        globs.gl_pathc = 0;
@@ -62,7 +80,8 @@ int parse(struct context *c) {
 
        l = strtol(p + 1, &stlend, 10);
        errno = 0;
-       if (stlend == c->b - 1) var = l >= 0 && l < argcount ? arglist[l] : c->b - 1;
+       if (stlend == c->b - 1)
+           var = l >= 0 && l < argcount ? argvector[l] : c->b - 1;
        else if (strcmp(p + 1, "^") == 0) {
            if (!sprintf(var = (char [12]){0}, "%d", status)) {
                note("Unable to get previous command status");
@@ -99,12 +118,14 @@ int parse(struct context *c) {
        }
 
        *c->b = '\0';
+       subalias(c);
        ++c->t;
        *c->t = c->b + 1;
 
        break;
    case '"':
        *c->b = '\0';
+       if (quote) subalias(c);
        if (quote || *c->t != c->b) ++c->t;
        *c->t = c->b + 1;
 
@@ -115,6 +136,7 @@ int parse(struct context *c) {
        if (!quote) break;
        switch (*(c->b + 1)) {
        case '$':
+       case '~':
        case '"':
        case '\\':
            break;
@@ -169,34 +191,28 @@ int parse(struct context *c) {
        }
 
        if (*c->t != c->b) {
-           if (!c->alias && c->t == c->tokens && (sub = getalias(c->tokens[0])))
-               for (sublen = 0; sub[sublen]; ++sublen);
-           else if (globbing) {
+           if (!subalias(c) && globbing) {
                globflags = GLOB_MARK;
-               if (prevsublen) globflags |= GLOB_APPEND;
+               if (prevnumglobs) globflags |= GLOB_APPEND;
                switch (glob(*c->t, globflags, NULL, &globs)) {
                case GLOB_NOMATCH:
-                   note("No matches found for %s", *c->t);
+                   note("No matches found for `%s'", *c->t);
                    return quit(c);
                case GLOB_NOSPACE:
                    fatal("Memory allocation");
                }
-               sublen = globs.gl_pathc - prevsublen;
-               sub = globs.gl_pathv + prevsublen;
-               prevsublen = globs.gl_pathc;
+
                globbing = 0;
-           }
-           if (sub) {
-               memcpy(c->t, sub, sublen * sizeof*c->t);
-               c->t += sublen - 1;
-               sub = NULL;
+               sub(c, globs.gl_pathv + prevnumglobs, globs.gl_pathc - prevnumglobs);
+               prevnumglobs = globs.gl_pathc;
            }
            ++c->t;
        }
 
        if (term != ' ') {
+           *c->t = NULL;
            if (c->t != c->tokens) {
-               *c->t = NULL;
+               c->numtokens = c->t - c->tokens;
                strcpy(c->current.name, c->tokens[0]);
            } else c->t = NULL;
            if (c->r == c->redirects) c->r = NULL;
@@ -225,9 +241,5 @@ int parse(struct context *c) {
        return quit(c);
    }
 
-   if (c->t == c->tokens) c->t = NULL;
-   if (c->r == c->redirects) c->r = NULL;
-   c->b = NULL;
-
-   return 1;
+   return clear(c);
 }
index 5ab813001a164e51b55634ffcd132746e2abe41f..0255cdadf452808a719370187f8e58f16801bfea 100644 (file)
--- a/src/run.c
+++ b/src/run.c
 #include "bg.h"
 #include "builtin.h"
 #include "context.h"
+#include "exec.h"
 #include "fg.h"
 #include "parse.h"
 #include "signals.h"
 #include "utils.h"
 #include "which.h"
 
-extern char **environ;
+int verbose, status;
 
-int verbose;
-
-static int closepipe(struct command c) {
+static int closepipe(struct command command) {
    int result;
 
-   result = close(c.pipe[0]) == 0;
-   result &= close(c.pipe[1]) == 0;
-   if (!result) note("Unable to close `%s' pipe", c.name);
+   result = close(command.pipe[0]) == 0;
+   result &= close(command.pipe[1]) == 0;
+   if (!result) note("Unable to close `%s' pipe", command.name);
 
    return result;
 }
@@ -55,25 +54,13 @@ static void redirectfiles(struct redirect *r) {
            r->oldfd = fd;
        }
        if (dup2(r->oldfd, r->newfd) == -1)
-           fatal("Unable to redirect %d to %d", r->newfd, r->oldfd);
+           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);
    }
 }
 
-static void exec(char *path, struct context *c) {
-   redirectfiles(c->redirects);
-
-   if (sigprocmask(SIG_SETMASK, &childsigmask, NULL) == -1)
-       note("Unable to unblock TTY signals");
-
-   if (isbuiltin(c->tokens)) exit(status);
-   execve(path, c->tokens, environ);
-   fatal("Couldn't find `%s' command", c->current.name);
-}
-
 int run(struct context *c) {
-   char *p;
    int islist, ispipe, ispipestart, ispipeend;
    pid_t cpid, jobid;
    static pid_t pipeid;
@@ -85,10 +72,8 @@ int run(struct context *c) {
    if (verbose && (c->t || c->r)) {
        if (c->t) {
            for (c->t = c->tokens; *c->t; ++c->t) {
-               for (p = *c->t; *p && *p != ' '; ++p);
-               p = p == *c->t || *p == ' ' ? "'" : "";
                if (c->t != c->tokens) putchar(' ');
-               printf("%s%s%s", p, *c->t, p);
+               fputs(quoted(*c->t), stdout);
            }
            if (c->r) putchar(' ');
        }
@@ -98,30 +83,39 @@ int run(struct context *c) {
            if (c->r->oldname) printf("%s", c->r->oldname);
            else printf("&%d", c->r->oldfd);
        }
-       if (c->current.term == BG) putchar('&');
-       fputs(c->current.term == PIPE ? " | " : "\n", stdout);
+       switch (c->current.term) {
+       case PIPE:
+           fputs(" | ", stdout);
+           fflush(stdout);
+           break;
+       case BG:
+           putchar('&');
+       default:
+           putchar('\n');
+       }   
    }
 
-   islist = c->prev.term > BG || c->current.term > BG;
+   islist = c->previous.term > BG || c->current.term > BG;
    if (c->t) {
        if (c->current.term == BG && bgfull()) {
-           note("Unable to place job in background, too many background jobs");
+           note("Unable to place job in background; too many background jobs");
            return quit(c);
        }
-       if (!(p = getpath(c->current.name))) {
+       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->prev.term == PIPE) killpg(pipeid, SIGKILL);
+           if (c->previous.term == PIPE) killpg(pipeid, SIGKILL);
            return quit(c);
        }
 
-       ispipe = c->prev.term == PIPE || c->current.term == PIPE;
-       ispipestart = ispipe && c->prev.term != PIPE;
+       ispipe = c->previous.term == PIPE || c->current.term == PIPE;
+       ispipestart = ispipe && c->previous.term != PIPE;
        ispipeend = ispipe && c->current.term != PIPE;
 
        if (ispipe) {
            if (!ispipeend && pipe(c->current.pipe) == -1) {
                note("Unable to create pipe");
-               if (!ispipestart) closepipe(c->prev);
+               if (!ispipestart) closepipe(c->previous);
                return quit(c);
            }
            if ((cpid = fork()) == -1) {
@@ -129,28 +123,34 @@ int run(struct context *c) {
                return quit(c);
            } else if (cpid == 0) {
                if (!ispipestart) {
-                   if (dup2(c->prev.pipe[0], 0) == -1)
-                       fatal("Unable to duplicate read end of `%s' pipe", c->prev.name);
-                   if (!closepipe(c->prev)) exit(EXIT_FAILURE);
+                   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);
                }
-               exec(p, c);
+               redirectfiles(c->redirects);
+               execute(c);
            }
            if (ispipestart) pipeid = cpid;
-           else if (!closepipe(c->prev)) {
+           else if (!closepipe(c->previous)) {
                killpg(pipeid, SIGKILL);
                return quit(c);
            }
            jobid = pipeid;
-       } else if (!c->r && isbuiltin(c->tokens)) cpid = 0;
-       else if ((jobid = cpid = fork()) == -1) {
+       } else if (!c->r && (c->current.builtin = getbuiltin(c->current.name))) {
+           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 == 0) exec(p, c);
+       } else if (cpid == 0) {
+           redirectfiles(c->redirects);
+           execute(c);
+       }
 
        if (cpid) {
            if (setpgid(cpid, jobid) == -1) {
@@ -174,7 +174,7 @@ int run(struct context *c) {
        } else if (c->current.term == OR) return clear(c);
    } else {
        if (islist) {
-           if (c->prev.term == PIPE) {
+           if (c->previous.term == PIPE) {
                killpg(pipeid, SIGKILL);
                if (verbose) putchar('\n');
            }
index 9c5cbb8160f5bbe6b58abac81cb4bcfcdf7f832a..810bc55f646cd041e43062c1934afd23cb69d874 100644 (file)
--- a/src/run.h
+++ b/src/run.h
@@ -1,3 +1,3 @@
-extern int verbose;
+extern int verbose, status;
 
 int run(struct context *c);
index 3d01af21104a92c8cdf10b9ff44cb308dc5522a3..cc3dc8e5a91966fc299fe84a793880c311d361d1 100644 (file)
 #include "fg.h"
 #include "history.h"
 #include "input.h"
+#include "options.h"
 #include "signals.h"
 
-int argcount, status;
-char **arglist, *home;
+char *home;
 
 void note(char *fmt, ...) {
    va_list args;
 
-   fprintf(stderr, "%s: ", arglist[0]);
+   fprintf(stderr, "%s: ", argvector[0]);
 
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
@@ -38,7 +38,7 @@ void note(char *fmt, ...) {
 void fatal(char *fmt, ...) {
    va_list args;
 
-   fprintf(stderr, "%s: ", arglist[0]);
+   fprintf(stderr, "%s: ", argvector[0]);
 
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
@@ -110,6 +110,64 @@ char *catpath(char *dir, char *filename, char *buffer) {
    return buffer;
 }
 
+char *quoted(char *token) {
+   char *p, *q, quote;
+   enum {
+       NONE,
+       DOUBLE,
+       SINGLE,
+       ESCAPEDOUBLE,
+   } degree;
+   static char buffer[MAXCHARS + 1];
+
+   if (!*token) return "\"\"";
+
+   degree = NONE;
+   for (p = token; *p; ++p) switch(*p) {
+   case '[':
+       for (q = p; *q; ++q) if (*q == ']') break;
+       if (!*q) continue;
+   case '>':
+   case '<':
+   case '*':
+   case '?':
+   case '#':
+   case '&':
+   case '|':
+   case ';':
+   case ' ':
+       if (degree < DOUBLE) degree = DOUBLE;
+       break;
+   case '$':
+   case '~':
+   case '"':
+       if (degree < SINGLE) degree = SINGLE;
+       break;
+   case '\'':
+       degree |= DOUBLE;
+   }
+   if (degree == NONE) return token;
+
+   quote = degree == SINGLE ? '\'' : '"';
+   p = buffer;
+   *p++ = quote;
+   strcpy(p, token);
+   for (q = p; *q; ++q);
+   if (degree & DOUBLE) for (; *p; ++p) switch (*p)
+   case '$':
+   case '~':
+   case '"':
+       if (degree == ESCAPEDOUBLE) {
+       case '\\':
+           memmove(p + 1, p, ++q - p);
+           *p++ = '\\';
+       }
+   *p++ = quote;
+   *p++ = '\0';
+
+   return buffer;
+}
+
 void deinit(void) {
    deinithistory();
    deinitbg();
index 4302a00538eb84199ce5aae5f7f7673cae7c5b39..e7b8a950f429fd41b3242959bea37562c52678b2 100644 (file)
@@ -1,8 +1,8 @@
-extern int argcount, status;
-extern char **arglist, *home;
+extern char *home;
 
 void note(char *fmt, ...);
 void fatal(char *fmt, ...);
 void init(void);
 char *catpath(char *dir, char *filename, char *buffer);
+char *quoted(char *token);
 void deinit(void);
index e90ce07fc4015abc321c02a9d4c9337a6ffad3ef..883514c27b710684367ed8f1e6f7fa607ef4f619 100644 (file)
@@ -3,14 +3,14 @@
 #include "cbs.c"
 
 int main(int argc, char **argv) {
-   char define[7 + 1 + PATH_MAX + 1], *path;
+   char define[7 + PATH_MAX], *path;
    size_t l;
    int slash;
    pid_t cpid;
 
    if (argc > 2) errx(EXIT_FAILURE, "usage: %s [prefix]", argv[0]);
 
-   path = stpcpy(define, "-DPATH=\"");
+   path = stpcpy(define, "-DPATH=");
    if (argc == 2) {
        l = strlen(argv[1]);
        slash = argv[1][l - 1] == '/';
@@ -33,9 +33,6 @@ int main(int argc, char **argv) {
        run("/bin/cp", LIST("cp", "-f", "bin/thus", path), "copy", "bin/thus");
    await(cpid, "copy", "bin/thus");
 
-   l = strlen(define);
-   define[l] = '\"';
-   define[l + 1] = '\0';
    cflags = LIST(define, "-Iexternal/cbs");
    compile("tools/uninstall");
    load('x', "uninstall", LIST("tools/uninstall"));
index 15ba8e4f0f73d6820057258a0b3ce9e1d557db77..cca930fe6f996abc331246803459c711316a1d2d 100644 (file)
@@ -2,6 +2,10 @@
 
 #include "cbs.c"
 
+/* C preprocessor being finicky */
+#define STR(x) STRINGIFY(x)
+#define STRINGIFY(x) #x
+
 int main(int argc, char **argv) {
    pid_t cpid;
 
@@ -9,8 +13,8 @@ int main(int argc, char **argv) {
 
    if ((cpid = fork()) == -1) err(EXIT_FAILURE, "Unable to fork");
    else if (cpid == 0)
-       run("/bin/rm", LIST("rm", PATH, "uninstall"), "remove", PATH);
-   await(cpid, "remove", PATH);
+       run("/bin/rm", LIST("rm", STR(PATH), "uninstall"), "remove", STR(PATH));
+   await(cpid, "remove", STR(PATH));
 
    return EXIT_SUCCESS;
 }