]> Trent Huber's Code - thus.git/commitdiff
Test bugs, hone behavior, clean code
authorTrent Huber <trentmhuber@gmail.com>
Tue, 19 Aug 2025 07:18:17 +0000 (03:18 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Tue, 19 Aug 2025 07:18:17 +0000 (03:18 -0400)
32 files changed:
external/cbs
external/cbsext.c [moved from external/cbsfile.c with 100% similarity]
src/build.c
src/builtin/README.md [new file with mode: 0644]
src/builtin/alias.c
src/builtin/alias.h
src/builtin/bg.c
src/builtin/build.c
src/builtin/builtin.h
src/builtin/cd.c
src/builtin/fg.c
src/builtin/list.h
src/builtin/set.c [new file with mode: 0644]
src/builtin/source.c
src/builtin/unset.c [new file with mode: 0644]
src/builtin/which.c
src/builtin/which.h [new file with mode: 0644]
src/context.c [new file with mode: 0644]
src/context.h
src/input.c
src/input.h
src/job.c
src/job.h
src/main.c
src/options.c
src/options.h
src/parse.c
src/parse.h
src/run.c
src/run.h
src/utils.c
src/utils.h

index 5ab49da8d4ab0b8e6cb23c3cb1f927e4402db4fc..beedfb79168cadd181b26ee46e50a01a6a44de52 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 5ab49da8d4ab0b8e6cb23c3cb1f927e4402db4fc
+Subproject commit beedfb79168cadd181b26ee46e50a01a6a44de52
similarity index 100%
rename from external/cbsfile.c
rename to external/cbsext.c
index 2f0999e8c85f82ab1672aae60fbd53ecd4e232ab..c09161e248c85b1b0c2c2adaa52bda90432fa457 100644 (file)
@@ -1,5 +1,5 @@
 #include "../external/cbs/cbs.c"
-#include "../external/cbsfile.c"
+#include "../external/cbsext.c"
 
 #define BUILTINS LIST("-Ibuiltin/")
 
@@ -10,12 +10,13 @@ int main(void) {
 
    buildfiles((struct cbsfile []){{"../bin/ash", NONE, 'x'},
 
+                                  {"context", NONE},
                                   {"history", NONE},
                                   {"input", NONE},
                                   {"job", NONE},
                                   {"main", BUILTINS},
                                   {"options", NONE},
-                                  {"parse", NONE},
+                                  {"parse", BUILTINS},
                                   {"run", BUILTINS},
                                   {"utils", BUILTINS},
 
diff --git a/src/builtin/README.md b/src/builtin/README.md
new file mode 100644 (file)
index 0000000..ea1db20
--- /dev/null
@@ -0,0 +1,8 @@
+# Builtins
+
+## TODO
+ - Documentation on how to add builtins of your own
+ - Reworking aliasing stuff
+ - unset
+ - which needs to test for aliases
+ - pwd
index 49c9100a18d2cc5da18d79d33c05ca45a4ee526e..6ceb185010a43a43d449ca9afaa804771adecc75 100644 (file)
 static struct {
    struct {
        char lhs[MAXCHARS - 5], *rhs;
-       struct context context;
    } entries[MAXALIAS + 1];
    size_t size;
 } aliases;
 
-void applyaliases(struct command *command) {
-   struct command *c;
-   char **end;
-   size_t i, l, a;
-   struct context *context;
+char *getaliasrhs(char *token) {
+   size_t i;
 
-   c = command;
+   for (i = 0; i < aliases.size; ++i)
+       if (strcmp(token, aliases.entries[i].lhs) == 0) return aliases.entries[i].rhs;
 
-   end = c->args;
-   while ((c = c->next)) if (c->args) end = c->args;
-   if (end) while (*end) ++end;
+   return NULL;
+}
 
-   while ((c = command = command->next)) {
-       if (!command->args) continue;
-       for (i = 0; i < aliases.size; ++i)
-           if (strcmp(aliases.entries[i].lhs, *command->args) == 0) break;
-       if (i == aliases.size) continue;
-       context = &aliases.entries[i].context;
-
-       strcpy(context->buffer, aliases.entries[i].rhs);
-       l = strlen(context->buffer);
-       context->buffer[l + 1] = '\0';
-       context->buffer[l] = ';';
-
-       parse(context);
-
-       for (a = 0; context->tokens[a]; ++a);
-       memmove(command->args + a, command->args + 1,
-               (end - command->args + 1) * sizeof*command->args);
-       memcpy(command->args, context->tokens, a * sizeof*command->args);
-       while ((c = c->next)) c->args += a - 1;
-   }
+char **getalias(char *token) {
+   char *rhs;
+   size_t l;
+   static struct context context;
+
+   if (!(rhs = getaliasrhs(token))) return NULL;
+
+   strcpy(context.buffer, rhs);
+   l = strlen(rhs);
+   context.buffer[l + 1] = '\0';
+   context.buffer[l] = ';';
+   context.alias = 1;
+   context.b = context.buffer;
+
+   if (!parse(&context)) return NULL;
+
+   return context.tokens;
 }
 
-BUILTINSIG(alias) {
+BUILTIN(alias) {
    size_t i;
    char *lhs;
 
@@ -82,7 +75,7 @@ BUILTINSIG(alias) {
 
        break;
    default:
-       puts("Usage: alias [lhs rhs]\r");
+       fputs("Usage: alias [lhs rhs]\r\n", stderr);
        return EXIT_FAILURE;
    }
 
index b84318f364713e22ba103452c541633ee8191531..52a3725b00217b041aad0bf58e4823d5982f3ca1 100644 (file)
@@ -1 +1,2 @@
-void applyaliases(struct command *command);
+char *getaliasrhs(char *token);
+char **getalias(char *token);
index 09c483c24e8f94424b698bad90817973a1491286..4ab87b47e53392bca2a932dec736a13e96e8934c 100644 (file)
@@ -8,7 +8,7 @@
 #include "job.h"
 #include "utils.h"
 
-BUILTINSIG(bg) {
+BUILTIN(bg) {
    long l;
    pid_t id;
    struct job *job;
@@ -23,25 +23,19 @@ BUILTINSIG(bg) {
            note("Unable to find job %d", id);
            return EXIT_FAILURE;
        }
-       if (job->type == BACKGROUND) {
+       if (!job->suspended) {
            note("Job %d already in background", id);
            return EXIT_FAILURE;
        }
-   } else if (!(job = searchjobtype(SUSPENDED))) {
-       note("No suspended jobs to run in background");
-       return EXIT_FAILURE;
-   }
+   } else if (!(job = peeksuspendedjob())) return EXIT_FAILURE;
    deletejobid(job->id);
 
-   if (!pushjob(job)) {
-       note("Unable to add job to background; too many jobs");
-       return EXIT_FAILURE;
-   }
+   if (!pushjob(job)) return EXIT_FAILURE;
    if (killpg(job->id, SIGCONT) == -1) {
-       note("Unable to wake up suspended process group %d", job->id);
+       note("Unable to wake up suspended job %d", job->id);
        return EXIT_FAILURE;
    }
-   job->type = BACKGROUND;
+   job->suspended = 0;
 
    return EXIT_SUCCESS;
 }
index 9a5c843eb08fba3457f7a8d0c0d28cc065cf61f2..36f41e2026ac8f971fc325c31b820314a2e4b39f 100644 (file)
@@ -5,7 +5,7 @@
 #include <sys/errno.h>
 
 #include "../../external/cbs/cbs.c"
-#include "../../external/cbsfile.c"
+#include "../../external/cbsext.c"
 
 #define MAXBUILTINS 50
 
@@ -14,10 +14,12 @@ int main(void) {
    DIR *dir;
    size_t i, offset;
 
-   /* The three extra files correspond to:
-    * 1) output file (../libbuiltin.a)
-    * 2) list.c
-    * 3) builtin.c */
+   /* The three extra files are:
+    * 1) ../libbuiltin.a (target file)
+    * 2) list.c (generated by this code)
+    * 3) builtin.c (not a builtin, just the API)
+    * 
+    * The remaining files all correspond to shell builtins. */
    struct cbsfile files[3 + MAXBUILTINS + 1];
 
    struct dirent *entry;
@@ -49,7 +51,7 @@ int main(void) {
        name[strlen(name) - strlen(".c")] = '\0';
        if (strcmp(name, "list") == 0) continue;
        if (strcmp(name, "builtin") != 0)
-           dprintf(listfd, "extern BUILTINSIG(%s);\n", name);
+           dprintf(listfd, "extern BUILTIN(%s);\n", name);
        files[i++] = (struct cbsfile){name, LIST("-I../")};
    }
    if (errno) err(EXIT_FAILURE, "Unable to read from current directory");
index 07fc3872907df5be87ade1304115b16ce151f8db..bb197f867edee68aea05a92b576d71828af6775b 100644 (file)
@@ -1,3 +1,3 @@
-#define BUILTINSIG(name) int name(int argc, char **argv)
+#define BUILTIN(name) int name(int argc, char **argv)
 
 int isbuiltin(char **args);
index 0986e25464aef984b0015550dce3f77652c39eb9..ecd67cabfd04db1e901d82a1f9f4bb543848a7fe 100644 (file)
@@ -1,12 +1,18 @@
+#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
 #include "builtin.h"
 #include "utils.h"
 
-BUILTINSIG(cd) {
+BUILTIN(cd) {
    char *path;
 
+   if (argc > 2) {
+       fputs("Usage: cd [directory]\r\n", stderr);
+       return EXIT_FAILURE;
+   }
+
    path = argc == 1 ? home : argv[1];
 
    if (chdir(path) == -1) {
@@ -14,7 +20,7 @@ BUILTINSIG(cd) {
        return EXIT_FAILURE;
    }
 
-   if (setenv("PWD", path, 1) == -1) {
+   if (setenv("PWD", path, 1) == -1) { // TODO: Slash-terminate path
        note("Unable to change $PWD$ to `%s'", path);
        return EXIT_FAILURE;
    }
index cdff59b607dbb8d8790bee680eabae67e4caec7d..0ff7935578f289ddc014724fae2ddf3c2e5e21e4 100644 (file)
@@ -5,17 +5,22 @@
 #include <sys/errno.h>
 #include <termios.h>
 #include <unistd.h>
+#include <string.h> // XXX
 
 #include "builtin.h"
 #include "job.h"
 #include "utils.h"
 
-static int fgstatus;
+static struct {
+   pid_t id;
+   int status, done;
+} fgpg;
 struct termios canonical;
 static struct termios raw;
 struct sigaction sigchld, sigdfl;
 
 static int setconfig(struct termios *mode) {
+// printf("Setting config to %s\r\n", mode == &raw ? "raw" : "canonical");
    if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
        note("Unable to set termios config");
        return 0;
@@ -24,14 +29,22 @@ static int setconfig(struct termios *mode) {
 }
 
 static void sigchldhandler(int sig) {
+   int e;
    pid_t id;
    struct job *job;
 
    (void)sig;
-   while ((id = waitpid(-1, &fgstatus, WNOHANG | WUNTRACED)) > 0)
+   e = errno;
+   while ((id = waitpid(-1, &fgpg.status, WNOHANG | WUNTRACED)) > 0)
        if ((job = searchjobid(id))) {
-           if (WIFSTOPPED(fgstatus)) job->type = SUSPENDED; else deletejobid(id);
-       } else if (!WIFSTOPPED(fgstatus)) while (waitpid(-id, NULL, 0) != -1);
+           if (WIFSTOPPED(fgpg.status)) job->suspended = 1; else deletejobid(id);
+       } else {
+           fgpg.done = 1;
+           if (WIFSTOPPED(fgpg.status)) continue;
+           if (id != fgpg.id) waitpid(fgpg.id, &fgpg.status, 0);
+           while (waitpid(-fgpg.id, NULL, 0) != -1);
+       }
+   errno = e;
 }
 
 void setsigchld(struct sigaction *act) {
@@ -51,6 +64,7 @@ void initfg(void) {
 }
 
 int setfg(struct job job) {
+// puts("setfg\r");
    if (!setconfig(&job.config)) return 0;
    if (tcsetpgrp(STDIN_FILENO, job.id) == -1) {
        note("Unable to bring job %d to foreground", job.id);
@@ -69,10 +83,11 @@ void waitfg(struct job job) {
 
    /* SIGCHLD handler is really the function that reaps the foreground process,
     * the waitpid() below is just to block the current thread of execution until
-    * the foreground process has been reaped */
+    * the foreground process has been reaped. */
+   fgpg.id = job.id;
    setsigchld(&sigchld);
-   waitpid(job.id, NULL, 0);
-   errno = 0; // waitpid() will set errno
+   while (waitpid(fgpg.id, NULL, 0) && !fgpg.done);
+   fgpg.done = errno = 0;
    setsigchld(&sigdfl);
 
    if (sigaction(SIGTTOU, &(struct sigaction){{SIG_IGN}}, NULL) == -1
@@ -84,28 +99,31 @@ void waitfg(struct job job) {
    }
    if (tcgetattr(STDIN_FILENO, &job.config) == -1)
        note("Unable to save termios config of job %d", job.id);
+// puts("waitfg\r");
    setconfig(&raw);
 
-   if (WIFEXITED(fgstatus)) status = WEXITSTATUS(fgstatus);
-   else if (WIFSIGNALED(fgstatus)) status = WTERMSIG(fgstatus);
+// printf("fgstatus = %d\r\n", fgstatus);
+   if (WIFEXITED(fgpg.status)) status = WEXITSTATUS(fgpg.status);
+   else if (WIFSIGNALED(fgpg.status)) { puts("SIGNAL RECEIVED\r"); status = WTERMSIG(fgpg.status); }
    else {
-       status = WSTOPSIG(fgstatus);
-       job.type = SUSPENDED;
+       status = WSTOPSIG(fgpg.status);
+       job.suspended = 1;
        if (!pushjob(&job)) {
-           note("Unable to add job %d to list; too many jobs\r\n"
-                "Press any key to continue", job.id);
+           note("Press any key to continue");
            getchar();
            if (setfg(job)) return waitfg(job);
            note("Manual intervention required for job %d", job.id);
        }
    }
+// printf("status = %d\r\n", status);
 }
 
 void deinitfg(void) {
+// puts("deinitfg");
    setconfig(&canonical);
 }
 
-BUILTINSIG(fg) {
+BUILTIN(fg) {
    long l;
    pid_t id;
    struct job *job;
@@ -117,12 +135,12 @@ BUILTINSIG(fg) {
            return EXIT_FAILURE;
        }
        if (!(job = searchjobid(id))) {
-           note("Unable to find process group %d", id);
+           note("Unable to find job %d", id);
            return EXIT_FAILURE;
        }
        deletejobid(id);
    } else if (!(job = pulljob())) {
-       note("No processes to bring into the foreground");
+       note("No job to bring into the foreground");
        return EXIT_FAILURE;
    }
 
index 7c33a2f21376540d1cf8fda7400d866431aa58a4..911d534d29ea18d9e8c798773194ae455445a5c6 100644 (file)
@@ -1,6 +1,6 @@
 struct builtin {
    char *name;
-   BUILTINSIG((*func));
+   BUILTIN((*func));
 };
 
 extern struct builtin builtins[];
diff --git a/src/builtin/set.c b/src/builtin/set.c
new file mode 100644 (file)
index 0000000..5eca3c6
--- /dev/null
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "builtin.h"
+#include "utils.h"
+
+extern char **environ;
+
+BUILTIN(set) {
+   char **e;
+
+   if (argc != 3) {
+       fputs("Usage: set [name value]\r\n", stderr);
+       return EXIT_FAILURE;
+   }
+
+   if (setenv(argv[1], argv[2], 1) == -1) {
+       note("Unable to set %s to %s", argv[1], argv[2]);
+       return EXIT_FAILURE;
+   }
+   
+   return EXIT_SUCCESS;
+}
index 7190f84bc684558538f1a6f487c1c266a27b4b6f..c53c09c55aadc082cc5530912e7d9edfa1c3ffe3 100644 (file)
@@ -9,17 +9,29 @@
 #include "run.h"
 #include "utils.h"
 
-BUILTINSIG(source) {
+BUILTIN(source) {
    struct context context;
-
-   if (argc != 2) {
-       note("Usage: source file");
+   int c;
+   char **v;
+   
+   if (argc == 1) {
+       fputs("Usage: source file [args ...]\r\n", stderr);
        return EXIT_FAILURE;
    }
 
+   context = (struct context){0};
    context.script = argv[1];
    context.input = scriptinput;
-   while (run(parse(context.input(&context))));
+
+   c = argcount;
+   v = arglist;
+   argcount = argc - 1;
+   arglist = argv + 1;
+
+   while (run(&context));
+
+   argcount = c;
+   arglist = v;
 
    return EXIT_SUCCESS;
 }
@@ -27,5 +39,5 @@ BUILTINSIG(source) {
 void config(char *name) {
    char path[PATH_MAX];
 
-   source(2, (char *[]){name, catpath(home, name, path), NULL});
+   source(2, (char *[]){"source", catpath(home, name, path), NULL});
 }
diff --git a/src/builtin/unset.c b/src/builtin/unset.c
new file mode 100644 (file)
index 0000000..fbdccf4
--- /dev/null
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "builtin.h"
+#include "utils.h"
+
+BUILTIN(unset) {
+   if (argc != 2) {
+       fputs("Usage: unset [name]\r\n", stderr);
+       return EXIT_FAILURE;
+   }
+
+   if (!getenv(argv[1])) {
+       note("Environment variable does not exist");
+       return EXIT_FAILURE;
+   }
+   if (unsetenv(argv[1]) == -1) {
+       note("Unable to unset $%s$", argv[1]);
+       return EXIT_FAILURE;
+   }
+
+   return EXIT_SUCCESS;
+}
index 4d8c58ad3d978f95c8fcd9436c05e20ece89dd1a..5d05e93b51db799a7e6295f06d15783c669458b3 100644 (file)
@@ -5,52 +5,94 @@
 #include <sys/errno.h>
 #include <sys/stat.h>
 
+#include "alias.h"
 #include "builtin.h"
 #include "list.h"
 #include "utils.h"
 
-BUILTINSIG(which) {
-   int result;
-   size_t i, l;
-   struct builtin *builtin;
-   char *entry, *end, dir[PATH_MAX], path[PATH_MAX];
+enum {
+   BUILTIN,
+   PATH,
+   ALIAS,
+};
+
+static int exists(char *path) {
    struct stat pstat;
-   
-   if (argc == 1) return EXIT_FAILURE;
+   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);
+
+   return 0;
+}
+
+static char *getpathtype(char *file, int *type) {
+   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 = getaliasrhs(file))) {
+           *type = ALIAS;
+           return entry;
+       }
 
-   result = EXIT_SUCCESS;
-   for (i = 1; argv[i]; ++i) {
        for (builtin = builtins; builtin->func; ++builtin)
-           if (strcmp(argv[i], builtin->name) == 0) {
-               printf("%s is built-in\r\n", argv[i]);
-               break;
+           if (strcmp(file, builtin->name) == 0) {
+               *type = BUILTIN;
+               return file;
            }
-       if (builtin->func) continue;
 
        if (!(entry = getenv("PATH"))) {
            note("Unable to examine $PATH$");
-           return EXIT_FAILURE;
+           return NULL;
        }
        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, argv[i], path)) return EXIT_FAILURE;
-           if (stat(path, &pstat) != -1) {
-               if (pstat.st_mode & S_IXUSR) {
-                   printf("%s\r\n", path);
-                   break;
-               }
-           } else if (errno != ENOENT) {
-               note("Unable to check if `%s' exists", path);
-               return EXIT_FAILURE;
-           }
+           if (!catpath(dir, file, path)) return NULL;
+           if (exists(path)) return path;
        }
-       if (entry != end + 1) continue;
+   }
 
-       printf("%s not found\r\n", argv[i]);
-       result = EXIT_FAILURE;
+   if (!realpath(file, path)) {
+       if (errno != ENOENT) note("Unable to expand `%s'", file); else errno = 0;
+       return NULL;
    }
+   if (exists(path)) return path;
+
+   return NULL;
+}
+
+char *getpath(char *file) {
+   int type;
+
+   return getpathtype(file, &type);
+}
+
+BUILTIN(which) {
+   int type;
+   char *result;
+
+   if (argc != 2) {
+       fputs("Usage: which name\r\n", stderr);
+       return EXIT_FAILURE;
+   }
+
+   if (!(result = getpathtype(argv[1], &type))) {
+       printf("%s not found\r\n", argv[1]);
+       return EXIT_SUCCESS;
+   }
+   
+   fputs(result, stdout);
+   if (type == BUILTIN) fputs(" is built-in", stdout);
+   puts("\r");
 
-   return result;
+   return EXIT_SUCCESS;
 }
diff --git a/src/builtin/which.h b/src/builtin/which.h
new file mode 100644 (file)
index 0000000..f645a0f
--- /dev/null
@@ -0,0 +1 @@
+char *getpath(char *file);
diff --git a/src/context.c b/src/context.c
new file mode 100644 (file)
index 0000000..e11ec3c
--- /dev/null
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+
+#include "context.h"
+#include "input.h"
+
+int clear(struct context *c) {
+   c->b = NULL;
+   c->t = NULL;
+   c->r = NULL;
+   *c->current.name = '\0';
+   c->current.term = SEMI;
+
+   return 1;
+}
+
+int quit(struct context *c) {
+   clear(c);
+
+   return c->input == userinput;
+}
index 4fa355e184a930666bd56a459fd667c6cef3a045..d69b98b0ac1d78364b0919e64dd142cf431a5c59 100644 (file)
@@ -2,9 +2,7 @@
 #define MAXCOMMANDS (MAXCHARS + 1) / 2
 #define MAXREDIRECTS (MAXCHARS / 3)
 
-#define PIPELINE(name) struct context *name(struct context *context)
-
-enum access {
+enum {
    NONE,
    READ = '<',
    READWRITE,
@@ -13,14 +11,12 @@ enum access {
 };
 
 struct redirect {
-   enum access mode;
-   int oldfd, newfd;
+   int mode, oldfd, newfd;
    char *oldname;
-   struct redirect *next;
 };
 
-enum terminator {
-   SEMI = ';',
+enum {
+   SEMI,
    BG = '&',
    AND,
    PIPE = '|',
@@ -28,23 +24,18 @@ enum terminator {
 };
 
 struct command {
-   char **args;
-   struct redirect *r;
-   enum terminator term;
-   int pipe[2];
-   struct command *prev, *next;
+   char name[MAXCHARS + 1];
+   int term, pipe[2];
 };
 
 struct context {
-   struct {
-       char *string, *script;
-       struct {
-           char *map;
-           size_t len;
-       };
-       PIPELINE((*input));
-   };
-   char buffer[MAXCHARS + 1 + 1], *tokens[MAXCOMMANDS + 1];
-   struct redirect redirects[MAXREDIRECTS + 1];
-   struct command commands[1 + MAXCOMMANDS];
+   char *string, *script, *map, buffer[MAXCHARS + 1 + 1], *b,
+        *tokens[MAXCOMMANDS + 1], **t;
+   size_t maplen;
+   int (*input)(struct context *c), alias;
+   struct redirect redirects[MAXREDIRECTS + 1], *r;
+   struct command current, prev;
 };
+
+int clear(struct context *c);
+int quit(struct context *c);
index 0e5dc41a9c003f63774e82c8173ae2f75fb5b36f..b73b69bfae95198b930c6d65a6efeaa73ccb8ce2 100644 (file)
 #include "input.h"
 #include "utils.h"
 
-PIPELINE(stringinput) {
+int stringinput(struct context *c) {
    char *start;
    size_t l;
 
-   if (!*context->string) {
-       if (context->script && munmap(context->map, context->len) == -1)
-           note("Unable to unmap memory associated with `%s'", context->script);
-       return NULL;
+   if (!*c->string) {
+       if (c->script && munmap(c->map, c->maplen) == -1)
+           note("Unable to unmap memory associated with `%s'", c->script);
+       return 0;
    }
 
-   start = context->string;
-   while (*context->string && *context->string != '\n') ++context->string;
-   l = context->string - start;
-   if (*context->string == '\n') ++context->string;
+   start = c->string;
+   while (*c->string && *c->string != '\n') ++c->string;
+   l = c->string - start;
+   if (*c->string == '\n') ++c->string;
    if (l > MAXCHARS) {
        note("Line too long, exceeds %d character limit", MAXCHARS);
-       return NULL;
+       return 0;
    }
 
-   strncpy(context->buffer, start, l);
-   context->buffer[l] = ';';
-   context->buffer[l + 1] = '\0';
+   strncpy(c->buffer, start, l);
+   c->buffer[l] = ';';
+   c->buffer[l + 1] = '\0';
 
-   return context;
+   return 1;
 }
 
-PIPELINE(scriptinput) {
+int scriptinput(struct context *c) {
    int fd;
    struct stat sstat;
 
-   if ((fd = open(context->script, O_RDONLY)) == -1) {
-       note("Unable to open `%s'", context->script);
-       return NULL;
+   if ((fd = open(c->script, O_RDONLY)) == -1) {
+       note("Unable to open `%s'", c->script);
+       return 0;
    }
-   if (stat(context->script, &sstat) == -1) {
-       note("Unable to stat `%s'", context->script);
-       return NULL;
+   if (stat(c->script, &sstat) == -1) {
+       note("Unable to stat `%s'", c->script);
+       return 0;
    }
-   if ((context->len = sstat.st_size) == 0) return NULL;
-   if ((context->map = mmap(NULL, context->len, PROT_READ, MAP_PRIVATE, fd, 0))
+   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'", context->script);
-       return NULL;
+       note("Unable to memory map `%s'", c->script);
+       return 0;
    }
    if (close(fd) == -1) {
-       note("Unable to close `%s'", context->script);
-       return NULL;
+       note("Unable to close `%s'", c->script);
+       return 0;
    }
 
-   context->string = context->map;
-   context->input = stringinput;
+   c->string = c->map;
+   c->input = stringinput;
 
-   return context->input(context);
+   return c->input(c);
 }
 
 static void prompt(void) {
@@ -75,43 +75,42 @@ static void prompt(void) {
    printf("\r%s ", p);
 }
 
-PIPELINE(userinput) {
+int userinput(struct context *c) {
    char *start, *cursor, *end;
-   unsigned int c;
+   unsigned int current;
    int i;
    size_t oldlen, newlen;
 
-   end = cursor = start = context->buffer;
-   *context->buffer = '\0';
+   end = cursor = start = c->buffer;
+   *c->buffer = '\0';
    while (start == end) {
        prompt();
-       while ((c = getchar()) != '\r') switch (c) {
+       while ((current = getchar()) != '\r') switch (current) {
        default:
-           if (c >= ' ' && c <= '~') {
+           if (current >= ' ' && current <= '~') {
                if (end - start == MAXCHARS) continue;
                memmove(cursor + 1, cursor, end - cursor);
-               *cursor++ = c;
+               *cursor++ = current;
                *++end = '\0';
 
-               putchar(c);
+               putchar(current);
                fputs(cursor, stdout);
                for (i = end - cursor; i > 0; --i) putchar('\b');
            }
            break;
        case CTRLC:
            puts("^C\r");
-           *context->buffer = '\0';
-           return context;
+           return quit(c);
        case CTRLD:
            puts("^D\r");
-           return NULL;
+           return 0;
        case CLEAR:
            fputs("\033[H\033[J", stdout);
            prompt();
-           fputs(context->buffer, stdout);
+           fputs(c->buffer, stdout);
            continue;
        case ESCAPE:
-           switch ((c = getchar())) {
+           switch ((current = getchar())) {
            case FORWARD:
                while (cursor != end && *cursor != ' ') putchar(*cursor++);
                while (cursor != end && *cursor == ' ') putchar(*cursor++);
@@ -121,17 +120,17 @@ PIPELINE(userinput) {
                while (cursor != start && *(cursor - 1) != ' ') putchar((--cursor, '\b'));
                break;
            case ARROW:
-               switch ((c = getchar())) {
+               switch ((current = getchar())) {
                case UP:
                case DOWN:
-                   oldlen = strlen(context->buffer);
-                   if (!gethistory(c, context->buffer)) continue;
-                   newlen = strlen(context->buffer);
+                   oldlen = strlen(c->buffer);
+                   if (!gethistory(current, c->buffer)) continue;
+                   newlen = strlen(c->buffer);
                    end = cursor = start + newlen;
 
                    putchar('\r');
                    prompt();
-                   fputs(context->buffer, stdout);
+                   fputs(c->buffer, stdout);
                    for (i = oldlen - newlen; i > 0; --i) putchar(' ');
                    for (i = oldlen - newlen; i > 0; --i) putchar('\b');
 
@@ -145,7 +144,7 @@ PIPELINE(userinput) {
                }
                break;
            default:
-               ungetc(c, stdin);
+               ungetc(current, stdin);
            }
            break;
        case BACKSPACE:
@@ -166,15 +165,12 @@ PIPELINE(userinput) {
    }
 
    while (*start == ' ') ++start;
-   if (start == end) {
-       *context->buffer = '\0';
-       return context;
-   }
+   if (start == end) return quit(c);
 
-   sethistory(context->buffer);
+   sethistory(c->buffer);
 
    *end++ = ';';
    *end = '\0';
 
-   return context;
+   return 1;
 }
index 7ba2dcfe746e40f46566e59c5365b1e5d09ac0d1..3762b05afd050df6b6fcce3a4d67c7063414be5c 100644 (file)
@@ -14,6 +14,6 @@ enum {
    DEL = '\177',
 };
 
-PIPELINE(stringinput);
-PIPELINE(scriptinput);
-PIPELINE(userinput);
+int stringinput(struct context *c);
+int scriptinput(struct context *c);
+int userinput(struct context *c);
index 9c02e8b2ca0ab50da8b4ea750316e2f5a710a4bd..c412040800af851a18896ddd349801890a841d4c 100644 (file)
--- a/src/job.c
+++ b/src/job.c
@@ -25,7 +25,11 @@ void initjobs(void) {
 struct job *pushjob(struct job *job) {
    struct joblink *p;
 
-   if (!jobs.free) return NULL;
+   if (!jobs.free) {
+       note("Unable to %s, exceeds %d job limit",
+            job->suspended ? "suspend job" : "place job in background", MAXJOBS);
+       return NULL;
+   }
 
    (p = jobs.free)->job = *job;
    jobs.free = p->next;
@@ -52,18 +56,20 @@ struct job *peekjob(void) {
    return jobs.active ? &jobs.active->job : NULL;
 }
 
-struct job *searchjobid(pid_t id) {
+struct job *peeksuspendedjob(void) {
    struct joblink *p;
 
-   for (p = jobs.active; p; p = p->next) if (p->job.id == id) return &p->job;
+   for (p = jobs.active; p; p = p->next) if (p->job.suspended) return &p->job;
+
+   note("No suspended job to run in background");
 
    return NULL;
 }
 
-struct job *searchjobtype(enum jobtype type) {
+struct job *searchjobid(pid_t id) {
    struct joblink *p;
 
-   for (p = jobs.active; p; p = p->next) if (p->job.type == type) return &p->job;
+   for (p = jobs.active; p; p = p->next) if (p->job.id == id) return &p->job;
 
    return NULL;
 }
index f4777505d75b8e215626d015d021e7ad5765a7f4..28a4673bab4b1f1cc62ca4584aa996f02c6831fe 100644 (file)
--- a/src/job.h
+++ b/src/job.h
@@ -1,18 +1,13 @@
-enum jobtype {
-   BACKGROUND,
-   SUSPENDED,
-};
-
 struct job {
    pid_t id;
    struct termios config;
-   enum jobtype type;
+   int suspended;
 };
 
 void initjobs(void);
 struct job *pushjob(struct job *job);
 struct job *pulljob(void);
 struct job *peekjob(void);
+struct job *peeksuspendedjob(void);
 struct job *searchjobid(pid_t id);
-struct job *searchjobtype(enum jobtype);
 struct job *deletejobid(pid_t id);
index c6dc2021f0e53b40e06db08020fa4ac8cdc579b5..9b434d30499698ab286339d5305c61ac8cc5fea0 100644 (file)
@@ -1,25 +1,26 @@
 #include <stdlib.h>
 
 #include "context.h"
-#include "input.h"
 #include "options.h"
-#include "parse.h"
 #include "run.h"
 #include "source.h"
 #include "utils.h"
 
-int main(int c, char **v) {
-   argc = c;
-   argv = v;
+int main(int argc, char **argv) {
+   struct context context;
 
-   options();
+   argcount = argc;
+   arglist = argv;
+   context = (struct context){0};
+
+   options(&context);
 
    init();
 
    if (login) config(".ashlogin");
    if (interactive) config(".ashrc");
 
-   while (run(parse(context.input(&context))));
+   while (run(&context));
 
    deinit();
 
index 35f39a3125471cefb0eb56641e85b2f75ef759c3..73de4e48fa9482ddc262fcc28793da799e6afc1b 100644 (file)
@@ -6,9 +6,7 @@
 #include "input.h"
 #include "utils.h"
 
-int login, interactive, argc;
-char **argv;
-struct context context;
+int login, interactive;
 
 static void usage(char *program, int code) {
    printf("Usage: %s [file] [-c string] [-hl]\n"
@@ -19,42 +17,43 @@ static void usage(char *program, int code) {
    exit(code);
 }
 
-void options(void) {
+void options(struct context *context) {
    int opt, l;
 
-   login = **argv == '-';
+   login = **arglist == '-';
    interactive = 1;
-   context.input = userinput;
+   context->input = userinput;
 
-   while ((opt = getopt(argc, argv, ":c:hl")) != -1) {
+   while ((opt = getopt(argcount, arglist, ":c:hl")) != -1) {
        switch (opt) {
        case 'c':
            interactive = 0;
-           context.string = optarg;
-           context.input = stringinput;
+           context->string = optarg;
+           context->input = stringinput;
+           arglist[--optind] = ""; // Empty program name when running a string
            break;
        case 'h':
-           usage(*argv, EXIT_SUCCESS);
+           usage(*arglist, EXIT_SUCCESS);
        case 'l':
            login = 1;
            break;
        case ':':
            note("Expected argument following `-%c'\n", optopt);
-           usage(*argv, EXIT_FAILURE);
+           usage(*arglist, EXIT_FAILURE);
        case '?':
        default:
            note("Unknown command line option `-%c'\n", optopt);
-           usage(*argv, EXIT_FAILURE);
+           usage(*arglist, EXIT_FAILURE);
        }
        if (opt == 'c') break;
    }
-   if (!context.string && argv[optind]) {
+   if (!context->string && arglist[optind]) {
        interactive = 0;
-       context.script = argv[optind];
-       context.input = scriptinput;
+       context->script = arglist[optind];
+       context->input = scriptinput;
    }
    if (!interactive) {
-       argc -= optind;
-       argv += optind;
+       argcount -= optind;
+       arglist += optind;
    }
 }
index 022538f6e958cbe38ea31df1bff2550a81385524..825670aaaa994970c7605efd9dd934a51197af0d 100644 (file)
@@ -1,5 +1,3 @@
-extern int login, interactive, argc;
-extern char **argv;
-extern struct context context;
+extern int login, interactive;
 
-void options(void);
+void options(struct context *context);
index e36637e1ea3dc6b01e7eb786adfa39a46b790103..2ef49b4503cb782d81a83e153f64b39b2ea81ceb 100644 (file)
+#include <glob.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/errno.h>
 
+#include "alias.h"
 #include "context.h"
-#include "input.h"
 #include "options.h"
 #include "utils.h"
 
-static void initcommand(struct command *c) {
-   c->args = NULL;
-   c->r = NULL;
-   c->prev = c - 1;
-   c->next = NULL;
-}
-
-PIPELINE(parse) {
-   char *b, **t, *name, *value, *stlend, *p, *end, *env;
-   struct redirect *r, *q;
-   struct command *c;
+int parse(struct context *c) {
+   int globbing, e, offset;
+   char *stlend, *p, *end, *env, term, **sub;
    long l;
-   int e, offset;
-   
-   if (!context) return NULL;
-
-   b = context->buffer;
-   t = context->tokens;
-   r = context->redirects;
-   c = context->commands + 1;
-   context->commands->next = NULL;
-   *t = value = name = NULL;
-   r->mode = NONE;
-   for (initcommand(c); *b; ++b) switch (*b) {
-   default:
-       if (r->mode) break;
-       if (!c->args) c->args = t;
-       if (!*(b - 1)) {
-           if (!name) *t++ = b; else if (!value) value = b;
-       }
-       break;
+   size_t sublen;
+   static glob_t globs;
+
+   if (!c->b) {
+       if (!c->input(c)) return 0;
+       c->b = c->buffer;
+   }
+   if (globs.gl_pathc) {
+       globfree(&globs);
+       globs.gl_pathc = 0;
+   }
+   c->t = c->tokens;
+   c->r = c->redirects;
+   c->r->mode = NONE;
+   c->prev = c->current;
+   globbing = 0;
+
+   for (*c->t = c->b; *c->b; ++c->b) switch (*c->b) {
    case '<':
    case '>':
-       if (r->mode) {
-           note("File redirections should be separated by spaces");
-           return context;
+       if (c->r->mode) {
+           note("Invalid syntax for file redirection");
+           return quit(c);
        }
-       if (r - context->redirects == MAXREDIRECTS) {
+       if (c->r - c->redirects == MAXREDIRECTS) {
            note("Too many file redirects, exceeds %d redirect limit", MAXREDIRECTS);
-           return context;
+           return quit(c);
        }
-       if (!c->r) c->r = r;
-       if (*(b - 1)) {
-           if (c->args == --t) c->args = NULL;
-           if ((l = strtol(*t, &stlend, 10)) < 0 || l > INT_MAX || stlend != b) {
+       if (*c->t != c->b) {
+           if ((l = strtol(*c->t, &stlend, 10)) < 0 || l > INT_MAX || stlend != c->b) {
                note("Invalid value for a file redirection");
-               return context;
+               return quit(c);
            }
-           r->newfd = l;
-       } else r->newfd = *b == '>';
-       r->mode = *b;
-       if (*(b + 1) == '>') {
-           ++r->mode;
-           ++b;
+           c->r->newfd = l;
+       } else c->r->newfd = *c->b == '>';
+       c->r->mode = *c->b;
+       if (*(c->b + 1) == '>') {
+           ++c->r->mode;
+           ++c->b;
        }
-       r->oldname = b + 1;
-       if (*(b + 1) == '&') ++b;
+       c->r->oldname = c->b + 1;
+       if (*(c->b + 1) == '&') ++c->b;
        break;
-   case '"':
-       if (!*(b - 1)) {
-           if (!name) *t++ = b; else if (!value) value = b;
+   case '$':
+       p = c->b++;
+       while (*c->b && *c->b != '$') ++c->b;
+       if (!*c->b) {
+           note("Environment variable lacks a terminating `$'");
+           return quit(c);
        }
-       for (end = (p = b) + 1, b = NULL; *end; ++end) {
-           if (!b && *end == '"') b = end;
+       *c->b++ = '\0';
+       for (end = c->b; *end; ++end);
+
+       l = strtol(p + 1, &stlend, 10);
+       errno = 0;
+       if (stlend == c->b - 1) env = l >= 0 && l < argcount ? arglist[l] : c->b - 1;
+       else if (strcmp(p + 1, "^") == 0) {
+           if (!sprintf(env = (char [12]){0}, "%d", status)) {
+               note("Unable to get previous command status");
+               return quit(c);
+           }
+       } else if (!(env = getenv(p + 1))) {
+           note("Environment variable does not exist");
+           return quit(c);
+       }
+
+       e = strlen(env);
+       offset = e - (c->b - p);
+       memmove(c->b + offset, c->b, end - c->b + 1);
+       strncpy(p, env, e);
+       c->b += offset - 1;
+
+       break;
+   case '~':
+       for (end = c->b; *end; ++end);
+       offset = strlen(home);
+       memmove(c->b + offset, c->b + 1, end - c->b);
+       strncpy(c->b, home, offset);
+       c->b += offset - 1;
+       break;
+   case '[':
+       while (*c->b && *c->b != ']') ++c->b;
+       if (!*c->b) {
+           note("Range in glob left open-ended");
+           return quit(c);
+       }
+   case '*':
+   case '?':
+       globbing = 1;
+       break;
+   case '"':
+       for (end = (p = c->b) + 1, c->b = NULL; *end; ++end) if (!c->b) {
+           if (*end == '"') c->b = end;
            if (*end == '\\') ++end;
        }
-       if (!b) {
+       if (!c->b) {
            note("Quote left open-ended");
-           return context;
+           return quit(c);
        }
        memmove(p, p + 1, end-- - p);
-       --b;
+       --c->b;
 
-       while (p != b) if (*p++ == '\\') {
+       while (p != c->b) if (*p++ == '\\') {
            switch (*p) {
            case 't':
                *p = '\t';
@@ -96,116 +131,92 @@ PIPELINE(parse) {
                *p = '\n';
                break;
            }
-           memmove(p - 1, p, end-- - p);
-           --b;
-       }
-       *end = '\0';
-       memmove(p, p + 1, end - p);
-       --b;
-
-       break;
-   case '=':
-       name = *--t;
-       *b = '\0';
-       break;
-   case '$':
-       if (!*(b - 1)) {
-           if (!name) *t++ = b; else if (!value) value = b;
-       }
-       p = b++;
-       while (*b && *b != '$') ++b;
-       if (!*b) {
-           note("Environment variable lacks a terminating `$'");
-           return context;
-       }
-       *b++ = '\0';
-       for (end = b; *end; ++end);
-
-       l = strtol(p + 1, &stlend, 10);
-       if (stlend == b - 1) env = l >= 0 && l < argc ? argv[l] : b - 1;
-       else if (strcmp(p + 1, "^") == 0) {
-           if (!sprintf(env = (char [12]){0}, "%d", status)) {
-               note("Unable to get previous command status");
-               return context;
-           }
-       } else if ((env = getenv(p + 1)) == NULL) {
-           note("Environment variable does not exist");
-           return context;
+           memmove(p - 1, p, end-- - p + 1);
+           --c->b;
        }
-
-       e = strlen(env);
-       offset = e - (b - p);
-       memmove(b + offset, b, end - b + 1);
-       strncpy(p, env, e);
-       b += offset - 1;
+       memmove(p, p + 1, end-- - p);
+       --c->b;
 
-       break;
-   case '~':
-       if (!*(b - 1)) {
-           if (!name) *t++ = b; else if (!value) value = b;
-       }
-       for (end = b; *end; ++end);
-       offset = strlen(home);
-       memmove(b + offset, b + 1, end - b);
-       strncpy(b, home, offset);
-       b += offset - 1;
        break;
    case '#':
-       *(b + 1) = '\0';
+       *(c->b + 1) = '\0';
    case '&':
    case '|':
    case ';':
-       if (name && *c->args == name) c->args = NULL;
-       if (c->args || c->r) {
-           if ((c->term = *b) == *(b + 1) && (*b == '&' || *b == '|')) {
-               ++c->term;
-               *b++ = '\0';
-           }
-           *b = '\0';
-
-           if (r->mode) {
-               r++->next = NULL;
-               r->mode = NONE;
-           } else if (c->r) (r - 1)->next = NULL;
-           for (q = c->r; q; q = q->next) if (*q->oldname == '&') {
-               if ((l = strtol(++q->oldname, &stlend, 10)) < 0 || l > INT_MAX || *stlend) {
-                   note("Incorrect syntax for file redirection");
-                   return context;
+   case ' ':
+       term = *c->b;
+       *c->b = '\0';
+
+       if (c->r->mode) {
+           switch (*c->r->oldname) {
+           case '&':
+               if ((l = strtol(++c->r->oldname, &stlend, 10)) < 0 || l > INT_MAX || *stlend) {
+               case '\0':
+                   note("Invalid syntax for file redirection");
+                   return quit(c);
                }
-               q->oldfd = l;
-               q->oldname = NULL;
+               c->r->oldfd = l;
+               c->r->oldname = NULL;
            }
+           (++c->r)->mode = NONE;
+           globbing = 0;
 
-           initcommand(c = c->next = c + 1);
-           *t++ = NULL;
-       }
-   case ' ':
-       *b = '\0';
-       if (value) {
-           if (setenv(name, value, 1) == -1) {
-               note("Unable to set environment variable");
-               return context;
+           *c->t = c->b;
+       } else if (!c->alias && c->t == c->tokens && (sub = getalias(*c->tokens)) || globbing) {
+           if (globbing) {
+               switch (glob(*c->t, GLOB_APPEND | GLOB_MARK, NULL, &globs)) {
+               case GLOB_NOMATCH:
+                   note("No matches found for %s", *c->t);
+                   return quit(c);
+               case GLOB_NOSPACE:
+                   fatal("Memory allocation");
+               }
+               sublen = globs.gl_matchc;
+               sub = globs.gl_pathv + globs.gl_pathc - sublen;
+               globbing = 0;
+           } else for (sublen = 0; sub[sublen]; ++sublen);
+
+           memcpy(c->t, sub, sublen * sizeof*c->t);
+           c->t += sublen;
+           *c->t = c->b;
+       } else if (*c->t != c->b) ++c->t;
+
+       if (term != ' ') {
+           if (c->t != c->tokens) {
+               *c->t = NULL;
+               strcpy(c->current.name, *c->tokens);
+           } 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;
            }
-           value = name = NULL;
-       }
-       if (r->mode) {
-           r = r->next = r + 1;
-           r->mode = NONE;
+           ++c->b;
+
+           return 1;
        }
+       *c->t = c->b + 1;
    }
 
-   (--c)->next = NULL;
-   switch (c->term) {
+   switch (c->current.term) {
    case AND:
    case PIPE:
    case OR:
        note("Expected another command");
-       return context;
-   default:
-       break;
+       return quit(c);
    }
 
-   context->commands->next = context->commands + 1;
+   if (c->t == c->tokens) c->t = NULL;
+   if (c->r == c->redirects) c->r = NULL;
+   c->b = NULL;
 
-   return context;
+   return 1;
 }
index 377fc376dd3378f0569bbc5f3183bd25d9166da8..496c99d4415d7828d1e7edb83b25390b71535565 100644 (file)
@@ -1 +1 @@
-PIPELINE(parse);
+int parse(struct context *c);
index 69c8dbae3e3a568739970d4ffe3c863a42ac23d7..6fa314c60b163fbd1522bb0a68766b89e436474b 100644 (file)
--- a/src/run.c
+++ b/src/run.c
@@ -2,6 +2,7 @@
 #include <limits.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/errno.h>
 #include <sys/wait.h>
 #include <termios.h>
 
 #include "builtin.h"
 #include "context.h"
-#include "alias.h"
-#include "input.h"
 #include "job.h"
 #include "fg.h"
+#include "parse.h"
 #include "utils.h"
+#include "which.h"
 
-static int closepipe(struct command *command) {
+extern char **environ;
+
+static int closepipe(struct command c) {
    int result;
 
-   result = close(command->pipe[0]) == 0;
-   result &= close(command->pipe[1]) == 0;
-   if (!result) note("Unable to close `%s' pipe", *command->args);
+   result = close(c.pipe[0]) == 0;
+   result &= close(c.pipe[1]) == 0;
+   if (!result) note("Unable to close `%s' pipe", c.name);
 
    return result;
 }
 
 static void redirectfiles(struct redirect *r) {
-   int mode, fd;
+   int access, fd;
 
-   for (; r; r = r->next) {
+   for (; r->mode; ++r) {
        if (r->oldname) {
            switch (r->mode) {
            case READ:
-               mode = O_RDONLY;
+               access = O_RDONLY;
                break;
            case WRITE:
-               mode = O_WRONLY | O_CREAT | O_TRUNC;
+               access = O_WRONLY | O_CREAT | O_TRUNC;
                break;
            case READWRITE:
-               mode = O_RDWR | O_CREAT | O_APPEND;
+               access = O_RDWR | O_CREAT | O_APPEND;
                break;
            case APPEND:
-               mode = O_WRONLY | O_CREAT | O_APPEND;
+               access = O_WRONLY | O_CREAT | O_APPEND;
            default:
                break;
            }
-           if ((fd = open(r->oldname, mode, 0644)) == -1)
+           if ((fd = open(r->oldname, access, 0644)) == -1)
                fatal("Unable to open `%s'", r->oldname);
            r->oldfd = fd;
        }
@@ -56,111 +59,118 @@ static void redirectfiles(struct redirect *r) {
    }
 }
 
-static void exec(struct command *c) {
+static void exec(char *path, struct context *c) {
    char cwd[PATH_MAX];
 
-   redirectfiles(c->r);
-
-   if (isbuiltin(c->args)) exit(status);
-   execvp(*c->args, c->args);
-   if (!getcwd(cwd, PATH_MAX)) fatal("Unable to check current working directory");
-   execvP(*c->args, cwd, c->args);
+   redirectfiles(c->redirects);
 
-   fatal("Couldn't find `%s' command", *c->args);
+   if (isbuiltin(c->tokens)) exit(status);
+   execve(path, c->tokens, environ);
+   fatal("Couldn't find `%s' command", c->current.name);
 }
 
-PIPELINE(run) {
-   struct command *c;
-   int ispipe, ispipestart, ispipeend;
+int run(struct context *c) {
+   int islist, ispipe, ispipestart, ispipeend;
+   char *path;
    pid_t cpid, jobid;
-   struct job job;
-
-   if (!context) return NULL;
-
-   applyaliases(c = context->commands);
+   struct job *p, job;
 
+   setsigchld(&sigchld);
+   if (!parse(c)) return 0;
    setsigchld(&sigdfl);
 
-   while ((c = c->next)) if (c->args) {
-       ispipe = c->term == PIPE || c->prev->term == PIPE;
-       ispipestart = ispipe && c->prev->term != PIPE;
-       ispipeend = ispipe && c->term != PIPE;
+   islist = c->prev.term > BG || c->current.term > BG;
+   if (c->t) {
+       if (!(path = getpath(c->current.name))) {
+           note("Couldn't find `%s' command", c->current.name);
+           if (c->prev.term == PIPE) closepipe(c->prev);
+           return quit(c);
+       }
+
+       ispipe = c->prev.term == PIPE || c->current.term == PIPE;
+       ispipestart = ispipe && c->prev.term != PIPE;
+       ispipeend = ispipe && c->current.term != PIPE;
 
        if (ispipe) {
-           if (!ispipeend && pipe(c->pipe) == -1) {
+           if (!ispipeend && pipe(c->current.pipe) == -1) {
                note("Unable to create pipe");
                if (!ispipestart) closepipe(c->prev);
-               break;
+               return quit(c);
            }
            if ((jobid = cpid = fork()) == -1) {
                note("Unable to fork child process");
-               break;
+               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->args);
+                   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 (!ispipeend) {
-                   if (dup2(c->pipe[1], 1) == -1)
-                       fatal("Unable to duplicate write end of `%s' pipe", *c->args);
-                   if (!closepipe(c)) exit(EXIT_FAILURE);
+                   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(c);
+               exec(path, c);
            }
            if (!ispipestart) {
                closepipe(c->prev);
-               jobid = (ispipeend ? pulljob : peekjob)()->id;
+               if (!(p = (ispipeend ? pulljob : peekjob)())) {
+                   note("Unable to %s pipeline job from background",
+                        ispipeend ? "remove" : "get");
+                   return quit(c);
+               }
+               jobid = p->id;
            }
-       } else if (!c->r && isbuiltin(c->args)) cpid = 0;
+       } else if (!c->r && isbuiltin(c->tokens)) cpid = 0;
        else if ((jobid = cpid = fork()) == -1) {
            note("Unable to fork child process");
-           break;
-       } else if (cpid == 0) exec(c);
+           return quit(c);
+       } else if (cpid == 0) exec(path, c);
 
        if (cpid) {
            if (setpgid(cpid, jobid) == -1) {
                if (errno != ESRCH) {
-                   note("Unable to set pgid of `%s' command to %d", *c->args, jobid);
+                   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);
                }
-               break;
+               return quit(c);
            }
-           job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND};
-           if (ispipestart || c->term == BG) {
+           job = (struct job){.id = jobid, .config = canonical};
+           if (ispipestart || c->current.term == BG) {
                if (!pushjob(&job)) {
-                   note("Unable to add job to background; too many background jobs");
-                   if (ispipestart) closepipe(c);
-                   break;
+                   if (ispipestart) closepipe(c->current);
+                   return quit(c);
                }
-           } else if (c->term != PIPE) {
-               if (!setfg(job)) break;
+           } else if (c->current.term != PIPE) {
+               if (!setfg(job)) return quit(c);
                waitfg(job);
            }
        }
 
-       if (c->term == AND && status != EXIT_SUCCESS) break;
-       if (c->term == OR && status == EXIT_SUCCESS) break;
+       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);
    } else {
-       if (c->term == AND || c->term == PIPE || c->term == OR) {
+       if (islist) {
+           if (c->prev.term == PIPE) closepipe(c->prev);
            note("Expected command");
-           break;
+           return quit(c);
        }
-       if (!c->r) break;
+       if (c->r) return 1;
 
        if ((cpid = fork()) == -1) {
            note("Unable to fork child process");
-           break;
+           return quit(c);
        } else if (cpid == 0) {
-           redirectfiles(c->r);
+           redirectfiles(c->redirects);
            exit(EXIT_SUCCESS);
        }
        waitpid(cpid, NULL, 0);
-       errno = 0; // waitpid() might set errno
+       errno = 0;
    }
 
-   setsigchld(&sigchld);
-
-   return context;
+   return 1;
 }
index e7eaa0802d87f36fc5df4bcfc495f904ee33b5db..6dfecfc32234c70b7b3d8b148ece868e9e8163a0 100644 (file)
--- a/src/run.h
+++ b/src/run.h
@@ -1,3 +1 @@
-extern int status;
-
-PIPELINE(run);
+int run(struct context *c);
index b95bcf9886c84ba160b7b13b3ffa1e423e9fad87..649b1f4ca871d3131cef4c3fb2f137bb206b103c 100644 (file)
@@ -8,13 +8,14 @@
 #include <termios.h>
 #include <unistd.h>
 
+#include "context.h"
 #include "history.h"
 #include "job.h"
 #include "fg.h"
 #include "options.h"
 
-char *home;
-int status;
+int argcount, status;
+char **arglist, *home;
 
 void note(char *fmt, ...) {
    va_list args;
@@ -35,11 +36,18 @@ void fatal(char *fmt, ...) {
 }
 
 void init(void) {
-   char *shlvlstr, buffer[19 + 1];
+   char buffer[PATH_MAX], *shlvlstr;
+   size_t l;
    long shlvl;
 
    if (!(home = getenv("HOME")))
-       fatal("Unable to locate user's home directory, $HOME$ not set");
+       fatal("Unable to query $HOME$");
+   strcpy(buffer, home);
+   l = strlen(buffer);
+   buffer[l + 1] = '\0';
+   buffer[l] = '/';
+   if (setenv("HOME", buffer, 1) == -1 || !(home = getenv("HOME")))
+       fatal("Unable to append trailing slash to $HOME$");
 
    if (!(shlvlstr = getenv("SHLVL"))) shlvlstr = "0";
    if ((shlvl = strtol(shlvlstr, NULL, 10)) < 0) shlvl = 0;
index c2884c06963d1b458950078a984007da9e0cfc85..4302a00538eb84199ce5aae5f7f7673cae7c5b39 100644 (file)
@@ -1,5 +1,5 @@
-extern char *home;
-extern int status;
+extern int argcount, status;
+extern char **arglist, *home;
 
 void note(char *fmt, ...);
 void fatal(char *fmt, ...);