SRCDIR "input", \
SRCDIR "job", \
SRCDIR "lex", \
+ SRCDIR "options", \
SRCDIR "main", \
+ SRCDIR "run", \
SRCDIR "stack", \
SRCDIR "term", \
SRCDIR "utils"
#include "input.h"
#include "job.h"
#include "stack.h"
+#include "options.h"
#define PROMPT "% "
char buffer[BUFLEN + 2]; // Terminating ";"
-char *strinput(char **strp) {
- char *p;
- size_t l;
-
- if (!**strp) return NULL;
- p = *strp;
- while (**strp && **strp != '\n') ++*strp;
- l = (*strp)++ - p;
- if (l > BUFLEN)
- errx(EXIT_FAILURE, "Line is too long; exceeds %d characters", BUFLEN);
- strncpy(buffer, p, l);
- *(buffer + l++) = ';';
- *(buffer + l) = '\0';
-
- return buffer;
-}
-
char *input(void) {
char *cursor, *end;
int c, i;
extern char buffer[BUFLEN + 2];
-char *strinput(char **strp);
char *input(void);
-#include <err.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h> // DEBUG
#include <stdlib.h>
-#include <string.h>
-#include <sys/errno.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
#include <termios.h>
-#include <unistd.h>
+#include <stdio.h> // XXX
-#include "builtins.h"
-#include "stack.h"
-#include "history.h"
-#include "input.h"
-#include "lex.h"
#include "job.h"
+#include "options.h"
+#include "run.h"
#include "term.h"
-#include "utils.h"
-
-static int closepipe(struct cmd *cmd) {
- int result;
-
- if (!cmd->args) return 1;
-
- result = 1;
- if (close(cmd->pipe[0]) == -1) {
- warn("Unable to close read end of `%s' pipe", *cmd->args);
- result = 0;
- }
- if (close(cmd->pipe[1]) == -1) {
- warn("Unable to close write end of `%s' pipe", *cmd->args);
- result = 0;
- }
- return result;
-}
-
-static int redirectfiles(struct redirect *r) {
- int oflag, fd;
-
- for (; r->mode; ++r) {
- if (r->oldname) {
- switch (r->mode) {
- case READ:
- oflag = O_RDONLY;
- break;
- case WRITE:
- oflag = O_WRONLY | O_CREAT | O_TRUNC;
- break;
- case READWRITE:
- oflag = O_RDWR | O_CREAT | O_APPEND;
- break;
- case APPEND:
- oflag = O_WRONLY | O_CREAT | O_APPEND;
- break;
- default:;
- }
- if ((fd = open(r->oldname, oflag, 0644)) == -1) {
- warn("Unable to open `%s'", r->oldname);
- return 0;
- }
- r->oldfd = fd;
- }
- if (dup2(r->oldfd, r->newfd) == -1) {
- warn("Unable to redirect %d to %d", r->newfd, r->oldfd);
- return 0;
- }
- if (r->oldname) {
- if (close(r->oldfd) == -1) {
- warn("Unable to close file descriptor %d", r->oldfd);
- return 0;
- }
- }
- }
- return 1;
-}
-
-void run(struct cmd *cmd) {
- struct cmd *prev;
- int ispipe, ispipestart, ispipeend;
- static int status;
- pid_t cpid, jobid;
- struct job job;
-
- for (prev = ∅ cmd->args; prev = cmd++) {
- ispipe = cmd->term == PIPE || prev->term == PIPE;
- ispipestart = ispipe && prev->term != PIPE;
- ispipeend = ispipe && cmd->term != PIPE;
-
- if (ispipe) {
- if (!ispipeend && pipe(cmd->pipe) == -1) {
- warn("Unable to create pipe");
- if (!ispipestart) closepipe(prev);
- break;
- }
- if ((jobid = cpid = fork()) == -1) {
- warn("Unable to create child process");
- break;
- } else if (cpid == 0) {
- if (!ispipestart) {
- if (dup2(prev->pipe[0], 0) == -1)
- err(EXIT_FAILURE, "Unable to duplicate read end of `%s' pipe",
- *prev->args);
- if (!closepipe(prev)) exit(EXIT_FAILURE);
- }
- if (!ispipeend) {
- if (dup2(cmd->pipe[1], 1) == -1)
- err(EXIT_FAILURE, "Unable to duplicate write end of `%s' pipe",
- *cmd->args);
- if (!closepipe(cmd)) exit(EXIT_FAILURE);
- }
-
- if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
- if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
- if (execvp(*cmd->args, cmd->args) == -1)
- err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
- }
- if (!ispipestart) {
- closepipe(prev);
- jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
- }
- } else {
- if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break;
- if ((jobid = cpid = fork()) == -1) {
- warn("Unable to create child process");
- break;
- } else if (cpid == 0) {
- if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
- if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
- if (execvp(*cmd->args, cmd->args) == -1)
- err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
- }
- }
- if (setpgid(cpid, jobid) == -1) {
- if (errno != ESRCH) {
- warn("Unable to set pgid of `%s' command to %d", *cmd->args, jobid);
- if (kill(cpid, SIGKILL) == -1)
- warn("Unable to kill process %d; manual termination may be required",
- cpid);
- }
- break;
- }
-
- job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND};
- if (ispipestart || cmd->term == BG) {
- if (!push(&jobs, &job)) {
- warn("Unable to add command to background; "
- "too many processes in the background");
- if (ispipestart) closepipe(cmd);
- break;
- }
- } else if (cmd->term != PIPE) {
- if (!setfg(job)) break;
- status = waitfg(job);
- if (cmd->term == AND && status != 0) break;
- if (cmd->term == OR && status == 0) break;
- }
- }
- waitbg(0);
-}
-
-void usage(void) {
- puts("TODO: PRINT USAGE MESSAGE");
-}
-
-char *getoptions(int argc, char **argv, int *l) {
- int opt, help, fd;
- struct cmd *cmd;
- char *r;
- struct stat fdstat;
-
- help = 0;
- r = NULL;
- while ((opt = getopt(argc, argv, ":c:h")) != -1) switch (opt) {
- case 'c':
- *l = strlen(optarg) + 2;
- if (!(r = malloc(*l))) err(EXIT_FAILURE, "Memory allocation");
- strcpy(r, optarg);
- *(r + *l - 1) = ';';
- *(r + *l) = '\0';
- break;
- case 'h':
- help = 1;
- break;
- case ':':
- errx(EXIT_FAILURE, "Expected argument following `-%c'", optopt);
- case '?':
- default:
- errx(EXIT_FAILURE, "Unknown command line option `-%c'", optopt);
- }
- argc -= optind;
- argv += optind;
-
- if (help) {
- usage();
- return EXIT_SUCCESS;
- }
-
- if (r) *argv = NULL;
- else if (*argv) {
- if ((fd = open(*argv, O_RDONLY)) == -1)
- err(EXIT_FAILURE, "Unable to open `%s'", *argv);
- if (stat(*argv, &fdstat) == -1)
- err(EXIT_FAILURE, "Unable to stat `%s'", *argv);
- if ((r = mmap(NULL, *l = fdstat.st_size, PROT_READ, MAP_PRIVATE, fd, 0))
- == MAP_FAILED)
- err(EXIT_FAILURE, "Unable to memory map `%s'", *argv);
- if (close(fd) == -1) err(EXIT_FAILURE, "Unable to close `%s'", *argv);
- }
-
- return r;
-}
int main(int argc, char **argv) {
struct cmd *cmd;
- char *o, *p;
- int l;
- p = o = getoptions(argc, argv, &l);
+ // TODO: Have `cd' builtin affect $PWD$ env var
+
+ options(&argc, &argv);
- initterm();
- if (p) {
- while ((cmd = lex(strinput(&p)))) run(cmd);
- if (*argv) munmap(o, l); else free(o);
- } else {
- readhist();
- while ((cmd = lex(input()))) run(cmd);
- writehist();
+ initterm(); // <-- TODO: Set $SHLVL$ in this function
+ if (login) runhome(".ashlogin");
+ if (string) {
+ runstr(string);
+ free(string);
+ } else if (*argv) runscript(*argv);
+ else {
+ runhome(".ashinteractive");
+ runinteractive();
}
deinitterm();
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <err.h>
+#include <string.h>
+
+int login;
+char *string;
+
+void options(int *argcp, char ***argvp) {
+ int opt, l;
+ char *usage = "TODO: WRITE USAGE";
+
+ login = ***argvp == '-';
+
+ // -h -> help message
+ // -l -> login shell
+ // -c "***" -> run string
+ // file.ash -> run file
+ while ((opt = getopt(*argcp, *argvp, ":c:hl")) != -1) switch (opt) {
+ case 'c':
+ l = strlen(optarg) + 2;
+ if (!(string = malloc(l))) err(EXIT_FAILURE, "Memory allocation");
+ strcpy(string, optarg);
+ *(string + l - 1) = ';';
+ *(string + l) = '\0';
+ break;
+ case 'h':
+ errx(EXIT_SUCCESS, "%s", usage);
+ case 'l':
+ login = 1;
+ break;
+ case ':':
+ errx(EXIT_FAILURE, "Expected argument following `-%c'\n%s", optopt, usage);
+ case '?':
+ default:
+ errx(EXIT_FAILURE, "Unknown command line option `-%c'\n%s", optopt, usage);
+ }
+ *argcp -= optind;
+ *argvp+= optind;
+}
--- /dev/null
+extern int login;
+extern char *string;
+
+void options(int *argcp, char ***argvp);
--- /dev/null
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h> // DEBUG
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "builtins.h"
+#include "history.h"
+#include "input.h"
+#include "job.h"
+#include "lex.h"
+#include "stack.h"
+#include "term.h"
+#include "utils.h"
+
+static int closepipe(struct cmd *cmd) {
+ int result;
+
+ if (!cmd->args) return 1;
+
+ result = 1;
+ if (close(cmd->pipe[0]) == -1) {
+ warn("Unable to close read end of `%s' pipe", *cmd->args);
+ result = 0;
+ }
+ if (close(cmd->pipe[1]) == -1) {
+ warn("Unable to close write end of `%s' pipe", *cmd->args);
+ result = 0;
+ }
+ return result;
+}
+
+static int redirectfiles(struct redirect *r) {
+ int oflag, fd;
+
+ for (; r->mode; ++r) {
+ if (r->oldname) {
+ switch (r->mode) {
+ case READ:
+ oflag = O_RDONLY;
+ break;
+ case WRITE:
+ oflag = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case READWRITE:
+ oflag = O_RDWR | O_CREAT | O_APPEND;
+ break;
+ case APPEND:
+ oflag = O_WRONLY | O_CREAT | O_APPEND;
+ break;
+ default:;
+ }
+ if ((fd = open(r->oldname, oflag, 0644)) == -1) {
+ warn("Unable to open `%s'", r->oldname);
+ return 0;
+ }
+ r->oldfd = fd;
+ }
+ if (dup2(r->oldfd, r->newfd) == -1) {
+ warn("Unable to redirect %d to %d", r->newfd, r->oldfd);
+ return 0;
+ }
+ if (r->oldname) {
+ if (close(r->oldfd) == -1) {
+ warn("Unable to close file descriptor %d", r->oldfd);
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+static void runcmd(struct cmd *cmd) {
+ struct cmd *prev;
+ int ispipe, ispipestart, ispipeend;
+ static int status;
+ pid_t cpid, jobid;
+ struct job job;
+
+ for (prev = ∅ cmd->args; prev = cmd++) {
+ ispipe = cmd->term == PIPE || prev->term == PIPE;
+ ispipestart = ispipe && prev->term != PIPE;
+ ispipeend = ispipe && cmd->term != PIPE;
+
+ if (ispipe) {
+ if (!ispipeend && pipe(cmd->pipe) == -1) {
+ warn("Unable to create pipe");
+ if (!ispipestart) closepipe(prev);
+ break;
+ }
+ if ((jobid = cpid = fork()) == -1) {
+ warn("Unable to create child process");
+ break;
+ } else if (cpid == 0) {
+ if (!ispipestart) {
+ if (dup2(prev->pipe[0], 0) == -1)
+ err(EXIT_FAILURE, "Unable to duplicate read end of `%s' pipe",
+ *prev->args);
+ if (!closepipe(prev)) exit(EXIT_FAILURE);
+ }
+ if (!ispipeend) {
+ if (dup2(cmd->pipe[1], 1) == -1)
+ err(EXIT_FAILURE, "Unable to duplicate write end of `%s' pipe",
+ *cmd->args);
+ if (!closepipe(cmd)) exit(EXIT_FAILURE);
+ }
+
+ if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
+ if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
+ if (execvp(*cmd->args, cmd->args) == -1)
+ err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
+ }
+ if (!ispipestart) {
+ closepipe(prev);
+ jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
+ }
+ } else {
+ if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break;
+ if ((jobid = cpid = fork()) == -1) {
+ warn("Unable to create child process");
+ break;
+ } else if (cpid == 0) {
+ if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
+ if (isbuiltin(cmd->args, &status)) exit(EXIT_SUCCESS);
+ if (execvp(*cmd->args, cmd->args) == -1)
+ err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
+ }
+ }
+ if (setpgid(cpid, jobid) == -1) {
+ if (errno != ESRCH) {
+ warn("Unable to set pgid of `%s' command to %d", *cmd->args, jobid);
+ if (kill(cpid, SIGKILL) == -1)
+ warn("Unable to kill process %d; manual termination may be required",
+ cpid);
+ }
+ break;
+ }
+
+ job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND};
+ if (ispipestart || cmd->term == BG) {
+ if (!push(&jobs, &job)) {
+ warn("Unable to add command to background; "
+ "too many processes in the background");
+ if (ispipestart) closepipe(cmd);
+ break;
+ }
+ } else if (cmd->term != PIPE) {
+ if (!setfg(job)) break;
+ status = waitfg(job);
+ if (cmd->term == AND && status != 0) break;
+ if (cmd->term == OR && status == 0) break;
+ }
+ }
+ waitbg(0);
+}
+
+void runstr(char *start) {
+ char *p;
+ size_t l;
+
+ for (p = start; *p; start = ++p) {
+ while (*p && *p != '\n') ++p;
+ l = p - start;
+ if (l > BUFLEN)
+ errx(EXIT_FAILURE, "Line is too long; exceeds %d characters", BUFLEN);
+ strncpy(buffer, start, l);
+ *(buffer + l) = ';';
+ *(buffer + l + 1) = '\0';
+ runcmd(lex(buffer));
+ }
+}
+
+void runscript(char *filename) {
+ int fd;
+ struct stat sstat;
+ char *str;
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ err(EXIT_FAILURE, "Unable to open `%s'", filename);
+ if (stat(filename, &sstat) == -1)
+ err(EXIT_FAILURE, "Unable to stat `%s'", filename);
+ if (sstat.st_size == 0) return;
+ if ((str = mmap(NULL, sstat.st_size, PROT_READ, MAP_PRIVATE, fd, 0))
+ == MAP_FAILED)
+ err(EXIT_FAILURE, "Unable to memory map `%s'", filename);
+ if (close(fd) == -1) err(EXIT_FAILURE, "Unable to close `%s'", filename);
+ runstr(str);
+ if (munmap(str, sstat.st_size) == -1)
+ err(EXIT_FAILURE, "Unable to unmap `%s'", filename);
+}
+
+void runinteractive(void) {
+ struct cmd *cmd;
+
+ while ((cmd = lex(input()))) runcmd(cmd);
+}
+
+#define MAXPATH 1000
+char *prependhome(char *filename) {
+ static char filepath[MAXPATH + 1];
+
+ strcpy(filepath, getenv("HOME"));
+ strcat(filepath, "/");
+ strcat(filepath, filename);
+
+ return filepath;
+}
+
+void runhome(char *filename) {
+ char *filepath;
+ int fd;
+
+ filepath = prependhome(filename);
+ if (access(filepath, R_OK) == -1) {
+ if (errno != ENOENT)
+ err(EXIT_FAILURE, "Unable to access `%s'", filepath);
+ if ((fd = open(filepath, O_RDONLY | O_CREAT, 0644)) == -1)
+ err(EXIT_FAILURE, "Unable to open `%s'", filepath);
+ if (close(fd) == -1)
+ err(EXIT_FAILURE, "Unable to close `%s'", filepath);
+ } else runscript(filepath);
+}
--- /dev/null
+char *prependhome(char *filename);
+
+void runstr(char *str);
+void runscript(char *filename);
+void runinteractive(void);
+void runhome(char *filename);