#define SRCDIR "src/"
#define SRC \
SRCDIR "builtins", \
- SRCDIR "history", \
SRCDIR "input", \
+ SRCDIR "history", \
SRCDIR "job", \
- SRCDIR "lex", \
- SRCDIR "options", \
+ SRCDIR "parse", \
SRCDIR "main", \
+ SRCDIR "options", \
SRCDIR "run", \
SRCDIR "stack", \
- SRCDIR "term", \
SRCDIR "utils"
#define BINDIR "bin/"
#define ASH BINDIR "ash"
-#include <err.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include "job.h"
#include "stack.h"
#include "term.h"
+#include "utils.h"
#define BUILTINSIG(name) int name(char **tokens)
BUILTINSIG((*func));
};
-BUILTINSIG(cd) {
+BUILTINSIG(cd) { // TODO: Affect $PWD$ env var
if (!tokens[1]) return 1;
if (chdir(tokens[1]) != -1) return 0;
- warn("Unable to change directory to `%s'", tokens[1]);
+ note("Unable to change directory to `%s'", tokens[1]);
return 1;
}
errno = 0;
if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno
|| jobid <= 0) {
- warn("Invalid process group id");
+ note("Invalid process group id");
return 1;
}
if (!(job = findjob((pid_t)jobid))) {
- warnx("Unable to find process group %d", (pid_t)jobid);
+ note("Unable to find process group %d", (pid_t)jobid);
return 1;
}
job = deletejob();
} else if (!(job = pull(&jobs))) {
- warnx("No processes to bring into the foreground");
+ note("No processes to bring into the foreground");
return 1;
}
if (!setfg(*job)) return 1;
errno = 0;
if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno
|| jobid <= 0) {
- warn("Invalid job id");
+ note("Invalid job id");
return 1;
}
if (!(job = findjob((pid_t)jobid))) {
- warnx("Unable to find job %d", (pid_t)jobid);
+ note("Unable to find job %d", (pid_t)jobid);
return 1;
}
if (job->type == BACKGROUND) {
- warnx("Job %d already in background", (pid_t)jobid);
+ note("Job %d already in background", (pid_t)jobid);
return 1;
}
} else {
for (jobs.c = MINUSONE(jobs, t); jobs.c != MINUSONE(jobs, b); DEC(jobs, c))
if (CURRENT->type == SUSPENDED) break;
if (jobs.c == MINUSONE(jobs, b)) {
- warnx("No suspended jobs to run in background");
+ note("No suspended jobs to run in background");
return 1;
}
}
job = deletejob();
if (!(push(&jobs, job))) {
- warnx("Unable to add job to background; too many jobs");
+ note("Unable to add job to background; too many jobs");
return 1;
}
if (killpg(job->id, SIGCONT) == -1) {
- warn("Unable to wake up suspended process group %d", job->id);
+ note("Unable to wake up suspended process group %d", job->id);
return 1;
}
job->type = BACKGROUND;
--- /dev/null
+#define HISTORYFILE ".ashhistory"
+#define INTERACTIVEFILE ".ashinteractive"
+#define LOGINFILE ".ashlogin"
+
+#define MAXCHARS 1000 // Maximum number of character per line
+#define MAXCMDS 100 // Maximum number of commands per line
+#define MAXHIST 100 // Maximum number of entries to store in history file
+#define MAXJOBS 100 // Maximum number of suspended/background jobs at any time
+#define MAXPATH 100 // Maximum number of character for a file path
+#define MAXRDS 10 // Maximum number of file redirects per command
-#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
+#include "config.h"
#include "input.h"
#include "stack.h"
#include "utils.h"
-#define HISTLEN 100
-#define HISTNAME ".ashhistory"
+static char historyarr[MAXHIST + 1][MAXCHARS + 1];
+INITSTACK(history, historyarr, 1);
-static char *histpath, histarr[HISTLEN + 1][BUFLEN + 1];
-struct stack INITSTACK(history, histarr, 1);
+void readhistory(void) {
+ FILE *file;
-void readhist(void) {
- char *homepath, buffer[BUFLEN + 1];
- FILE *histfile;
- int e;
-
- if (!(homepath = getenv("HOME")))
- errx(EXIT_FAILURE, "HOME environment variable does not exist");
- histpath = allocate(strlen(homepath) + 1 + strlen(HISTNAME) + 1);
- strcpy(histpath, homepath);
- strcat(histpath, "/");
- strcat(histpath, HISTNAME);
-
- if (!(histfile = fopen(histpath, "r"))) {
+ if (!(file = fopen(prependhome(HISTORYFILE), "r"))) {
if (errno == ENOENT) return;
- err(EXIT_FAILURE, "Unable to open history file for reading");
+ fatal("Unable to open history file for reading");
}
- while (fgets(buffer, history.size, histfile)) {
+ while (fgets(buffer, history.size, file)) {
*(buffer + strlen(buffer) - 1) = '\0';
push(&history, buffer);
}
-
- if (ferror(histfile) || !feof(histfile))
- err(EXIT_FAILURE, "Unable to read from history file");
- if (fclose(histfile) == EOF) err(EXIT_FAILURE, "Unable to close history file");
+ if (ferror(file) || !feof(file))
+ fatal("Unable to read from history file");
+ if (fclose(file) == EOF) fatal("Unable to close history file");
}
-void writehist(void) {
- FILE *histfile;
+void writehistory(void) {
+ FILE *file;
- if (!(histfile = fopen(histpath, "w"))) {
- warn("Unable to open history file for writing");
+ if (!(file = fopen(prependhome(HISTORYFILE), "w"))) {
+ note("Unable to open history file for writing");
return;
}
-
for (history.c = history.b; history.c != history.t; INC(history, c)) {
- if (fputs(history.c, histfile) == EOF) {
- warn("Unable to write to history file");
+ if (fputs((char *)history.c, file) == EOF) {
+ note("Unable to write to history file");
break;
}
- if (fputc('\n', histfile) == EOF) {
- warn("Unable to write newline to history file");
+ if (fputc('\n', file) == EOF) {
+ note("Unable to terminate line of history file");
break;
}
}
-
- if (fclose(histfile) == EOF) warn("Unable to close history stream");
+ if (fclose(file) == EOF) note("Unable to close history stream");
}
extern struct stack history;
-void readhist(void);
-void writehist(void);
+void readhistory(void);
+void writehistory(void);
+
-#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
#include <termios.h>
#include <unistd.h>
+#include "config.h"
#include "history.h"
#include "input.h"
#include "job.h"
#include "stack.h"
-#include "options.h"
+#include "utils.h"
-#define PROMPT "% "
+#define PROMPT "> " // TODO: Have prompt be an environment variable
enum character {
CTRLC = '\003',
DEL = '\177',
};
-char buffer[BUFLEN + 2]; // Terminating ";"
+char *string, buffer[MAXCHARS + 2], *script;
-char *input(void) {
+INPUT(stringinput) {
+ char *start;
+ size_t l;
+
+ if (!*string) return NULL;
+ start = string;
+ while (*string && *string != '\n') ++string;
+ l = string - start;
+ if (*string == '\n') ++string;
+ if (l > MAXCHARS) fatal("Line too long, exceeds %d character limit", MAXCHARS);
+ strncpy(buffer, start, l);
+ buffer[l] = ';';
+ buffer[l + 1] = '\0';
+
+ return buffer;
+}
+
+INPUT(scriptinput) {
+ int fd;
+ struct stat sstat;
+ char *result;
+ static char *map;
+ static size_t l;
+
+ if (!map) {
+ if ((fd = open(script, O_RDONLY)) == -1) fatal("Unable to open `%s'", script);
+ if (stat(script, &sstat) == -1) fatal("Unable to stat `%s'", script);
+ if ((l = sstat.st_size) == 0) return NULL;
+ if ((map = string = mmap(NULL, l, PROT_READ, MAP_PRIVATE, fd, 0))
+ == MAP_FAILED)
+ fatal("Unable to memory map `%s'", script);
+ if (close(fd) == -1) fatal("Unable to close `%s'", script);
+ }
+
+ if (!(result = stringinput())) {
+ if (munmap(map, l) == -1) fatal("Unable to unmap %s from memory", script);
+ map = NULL;
+ }
+
+ return result;
+}
+
+char *config(char *name) {
+ char *result;
+ static char *origscript, *origstr;
+
+ if (!origscript) {
+ origscript = script;
+ origstr = string;
+ script = prependhome(name);
+ }
+
+ if (!(result = scriptinput())) {
+ script = origscript;
+ string = origstr;
+ origscript = NULL;
+ }
+
+ return result;
+}
+
+static void waitbgsig(int sig) {
+ (void)sig;
+ waitbg();
+}
+
+INPUT(userinput) {
char *cursor, *end;
unsigned int c;
int i;
- signal(SIGCHLD, waitbg); // TODO: Use sigaction for portability
+ signal(SIGCHLD, waitbgsig); // TODO: Use sigaction for portability
end = cursor = buffer;
*history.t = *buffer = '\0';
while ((c = getchar()) != '\r') switch (c) {
default:
if (c >= ' ' && c <= '~') {
- if (end - buffer == BUFLEN) continue;
+ if (end - buffer == MAXCHARS) continue;
memmove(cursor + 1, cursor, end - cursor);
*cursor++ = c;
*++end = '\0';
return buffer;
case CTRLD:
puts("^D\r");
- signal(SIGCHLD, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL); // XXX
return NULL;
case CLEAR:
fputs("\033[H\033[J", stdout);
for (i = end - buffer + strlen(PROMPT); i > 0; --i) putchar(' ');
putchar('\r');
- if (strcmp(history.c, buffer) != 0) strcpy(history.t, buffer);
+ if (strcmp((char *)history.c, buffer) != 0)
+ strcpy((char *)history.t, buffer);
if (c == UP) DEC(history, c); else INC(history, c);
- strcpy(buffer, history.c);
+ strcpy(buffer, (char *)history.c);
end = cursor = buffer + strlen(buffer);
fputs(PROMPT, stdout);
*end++ = ';';
*end = '\0';
- signal(SIGCHLD, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL); // XXX
return buffer;
}
-#define BUFLEN 1000
+#define INPUT(name) char *name(void)
-extern char buffer[BUFLEN + 2];
+extern char *string, buffer[MAXCHARS + 2], *script;
-char *input(void);
+INPUT(stringinput);
+INPUT(scriptinput);
+char *config(char *name);
+INPUT(userinput);
-#include <err.h>
#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <termios.h>
+#include <unistd.h>
+#include "config.h"
#include "job.h"
#include "stack.h"
+#include "utils.h"
-#define MAXJOBS 100
-
-static struct job jobarray[MAXJOBS + 1];
-struct stack INITSTACK(jobs, jobarray, 0);
+static struct job jobarr[MAXJOBS + 1];
+INITSTACK(jobs, jobarr, 0);
+struct termios raw, canonical;
void *findjob(pid_t jobid) {
if (jobs.b == jobs.t) return NULL;
return DEC(jobs, t);
}
-void waitbg(int sig) {
+int setconfig(struct termios *mode) {
+ if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
+ note("Unable to set termios config");
+ return 0;
+ }
+ return 1;
+}
+
+int setfg(struct job job) {
+ if (!setconfig(&job.config)) return 0;
+ if (tcsetpgrp(STDIN_FILENO, job.id) == -1) {
+ note("Unable to bring job %d to foreground", job.id);
+ setconfig(&raw);
+ return 0;
+ }
+ if (killpg(job.id, SIGCONT) == -1) {
+ note("Unable to wake up job %d", job.id);
+ return 0;
+ }
+ return 1;
+}
+
+int waitfg(struct job job) {
+ int status, pgid, result;
+
+ do {
+ errno = 0;
+ waitpid(job.id, &status, WUNTRACED);
+ } while (errno == EINTR);
+ if (!errno && !WIFSTOPPED(status)) do {
+ errno = 0;
+ while (waitpid(-job.id, NULL, 0) != -1);
+ } while (errno == EINTR);
+ result = errno != ECHILD ? errno : 0;
+
+ // TODO: Use sigaction >:(
+ if ((pgid = getpgid(0)) == -1 || signal(SIGTTOU, SIG_IGN) == SIG_ERR
+ || tcsetpgrp(STDIN_FILENO, pgid) == -1
+ || signal(SIGTTOU, SIG_DFL) == SIG_ERR) {
+ note("Unable to reclaim foreground; terminating");
+ deinitialize();
+ exit(EXIT_FAILURE);
+ }
+ if (tcgetattr(STDIN_FILENO, &job.config) == -1)
+ note("Unable to save termios config of job %d", job.id);
+ setconfig(&raw);
+ if (result) return result;
+
+ if (WIFSIGNALED(status)) {
+ result = WTERMSIG(status);
+ puts("\r");
+ } else if (WIFSTOPPED(status)) {
+ result = WSTOPSIG(status);
+ job.type = SUSPENDED;
+ if (push(&jobs, &job)) return result;
+ note("Unable to add job %d to list; too many jobs\r\n"
+ "Press any key to continue", job.id);
+ getchar();
+ if (setfg(job)) return waitfg(job);
+ note("Manual intervention required for job %d", job.id);
+ } else if (WIFEXITED(status)) result = WEXITSTATUS(status);
+
+ return result;
+}
+
+void waitbg(void) {
int status;
pid_t id;
- (void)sig;
for (jobs.c = jobs.b; jobs.c != jobs.t; INC(jobs, c)) {
if (CURRENT->type != BACKGROUND) continue;
id = CURRENT->id;
+
+ // TODO: weird EINTR thing here too??
while ((id = waitpid(-id, &status, WNOHANG | WUNTRACED)) > 0)
if (WIFSTOPPED(status)) CURRENT->type = SUSPENDED;
+
if (id == -1 && errno != ECHILD)
- warn("Unable to wait on some child processes");
+ note("Unable to wait on some child processes");
}
}
+#define CURRENT ((struct job *)jobs.c)
+
enum jobtype {
BACKGROUND,
SUSPENDED,
enum jobtype type;
};
-#define CURRENT ((struct job *)jobs.c)
extern struct stack jobs;
+extern struct termios raw, canonical;
void *findjob(pid_t jobid);
void *deletejob(void);
-void waitbg(int sig);
+int setconfig(struct termios *mode);
+int setfg(struct job job);
+int waitfg(struct job job);
+void waitbg(void);
#include <stdlib.h>
-#include <termios.h>
-#include <stdio.h> // XXX
-#include "job.h"
+#include "config.h"
+#include "input.h"
#include "options.h"
+#include "parse.h"
#include "run.h"
-#include "term.h"
+#include "utils.h"
int main(int argc, char **argv) {
- struct cmd *cmd;
+ options(&argc, &argv);
- // TODO: Have `cd' builtin affect $PWD$ env var
+ initialize();
- options(&argc, &argv);
+ if (login) while (run(parse(config(LOGINFILE))));
+ if (interactive) while (run(parse(config(INTERACTIVEFILE))));
+
+ while (run(parse(input())));
- 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();
+ deinitialize();
return EXIT_SUCCESS;
}
+#include <err.h>
#include <stdlib.h>
-#include <stdio.h>
#include <unistd.h>
-#include <err.h>
-#include <string.h>
-int login;
-char *string;
+#include "config.h"
+#include "input.h"
+#include "options.h"
+
+int login, interactive;
+Input input;
void options(int *argcp, char ***argvp) {
int opt, l;
char *usage = "TODO: WRITE USAGE";
login = ***argvp == '-';
+ interactive = 1;
+ input = userinput;
- // -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';
+ interactive = 0;
+ input = stringinput;
+ string = optarg;
break;
case 'h':
errx(EXIT_SUCCESS, "%s", usage);
errx(EXIT_FAILURE, "Unknown command line option `-%c'\n%s", optopt, usage);
}
*argcp -= optind;
- *argvp+= optind;
+ *argvp += optind;
+
+ if (!string && **argvp) {
+ interactive = 0;
+ input = scriptinput;
+ script = **argvp;
+ }
}
-extern int login;
-extern char *string;
+typedef INPUT((*Input));
+
+extern int login, interactive;
+extern Input input;
void options(int *argcp, char ***argvp);
-#include <err.h>
-#include <fcntl.h>
#include <limits.h>
-#include <stddef.h>
#include <stdlib.h>
#include <string.h>
-#include <stdio.h> // XXX
+#include "config.h"
#include "input.h"
-#include "lex.h"
+#include "parse.h"
+#include "utils.h"
-#define MAXCMDS 100
-
-static char *tokens[BUFLEN + 1];
+static char *tokens[MAXCHARS + 1];
static struct cmd cmds[MAXCMDS + 1];
struct cmd empty = {0};
-struct cmd *lex(char *b) {
- char **t, *end, *p, *env, *name, *value;
- int e, offset;
+struct cmd *parse(char *b) {
+ char **t, *name, *value, *end, *p, *env;
struct cmd *c;
long l;
+ int e, offset;
if (!b) return NULL;
t = tokens;
if (*(b - 1)) {
if (c->args == --t) c->args = NULL;
if ((l = strtol(*t, &end, 10)) < 0 || l > INT_MAX || end != b) {
- warnx("Invalid file redirection");
+ note("Incorrect syntax for file redirection\r");
return ∅
}
c->r->newfd = l;
if (*end == '\\') ++end;
}
if (!b) {
- warnx("Open-ended quote");
+ note("Quote left open-ended\r");
return ∅
}
-
- // "..."...\0
- // ^ ^ ^
- // p b end
memmove(p, p + 1, end-- - p);
--b;
- // ..."...\0
- // ^ ^ ^
- // p b end
+
while (p != b) if (*p++ == '\\') {
switch (*p) {
- case 'n':
- *p = '\n';
- break;
case 't':
*p = '\t';
break;
+ case 'v':
+ *p = '\v';
+ break;
case 'r':
*p = '\r';
break;
- case 'v':
- *p = '\v';
+ case 'n':
+ *p = '\n';
break;
}
memmove(p - 1, p, end-- - p);
p = b++;
while (*b && *b != '$') ++b;
if (!*b) {
- warnx("Open-ended environment variable");
+ note("Environment variable lacks a terminating `$'\r");
return ∅
}
*b++ = '\0';
for (end = b; *end; ++end);
- // $...\0...\0
- // ^ ^ ^
- // p b end
if ((env = getenv(p + 1)) == NULL) {
- warnx("Unknown environment variable");
+ note("Environment variable does not exist\r");
return ∅
}
e = strlen(env);
case ';':
if (name && *c->args == name) c->args = NULL;
if (c->args) {
- if ((c->term = *b) == *(b + 1) && *b == '&' || *b == '|') {
+ if ((c->term = *b) == *(b + 1) && (*b == '&' || *b == '|')) {
++c->term;
*b++ = '\0';
}
c->r->mode = END;
for (c->r = c->rds; c->r->mode; ++c->r) if (*c->r->oldname == '&') {
if ((l = strtol(++c->r->oldname, &end, 10)) < 0 || l > INT_MAX || *end) {
- warnx("Invalid file redirection");
+ note("Incorrect syntax for file redirection\r");
return ∅
}
c->r->oldfd = l;
*b = '\0';
if (value) {
if (setenv(name, value, 1) == -1) {
- warn("Unable to set environment variable");
+ note("Unable to set environment variable\r");
return ∅
}
value = name = NULL;
case AND:
case PIPE:
case OR:
- warnx("Command left open-ended");
+ note("Expected another command\r");
return ∅
default:
break;
OR,
};
-#define MAXRDS 25
struct cmd {
char **args;
struct redirect *r, rds[MAXRDS + 1];
extern struct cmd empty;
-struct cmd *lex(char *b);
+struct cmd *parse(char *b);
-#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 "config.h"
#include "job.h"
-#include "lex.h"
+#include "parse.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;
- }
+ result = close(cmd->pipe[0]) == 0;
+ result &= close(cmd->pipe[1]) == 0;
+ if (!result) note("Unable to close `%s' pipe", *cmd->args);
return result;
}
-static int redirectfiles(struct redirect *r) {
- int oflag, fd;
+static void redirectfiles(struct redirect *r) {
+ int mode, fd;
for (; r->mode; ++r) {
if (r->oldname) {
switch (r->mode) {
case READ:
- oflag = O_RDONLY;
+ mode = O_RDONLY;
break;
case WRITE:
- oflag = O_WRONLY | O_CREAT | O_TRUNC;
+ mode = O_WRONLY | O_CREAT | O_TRUNC;
break;
case READWRITE:
- oflag = O_RDWR | O_CREAT | O_APPEND;
+ mode = O_RDWR | O_CREAT | O_APPEND;
break;
case APPEND:
- oflag = O_WRONLY | O_CREAT | O_APPEND;
+ mode = O_WRONLY | O_CREAT | O_APPEND;
+ default:
break;
- default:;
- }
- if ((fd = open(r->oldname, oflag, 0644)) == -1) {
- warn("Unable to open `%s'", r->oldname);
- return 0;
}
+ if ((fd = open(r->oldname, mode, 0644)) == -1)
+ fatal("Unable to open `%s'", r->oldname);
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;
- }
- }
+ if (dup2(r->oldfd, r->newfd) == -1)
+ fatal("Unable to redirect %d to %d", r->newfd, r->oldfd);
+ if (r->oldname && close(r->oldfd) == -1)
+ fatal("Unable to close `%s'", r->oldname);
}
- return 1;
}
-static void runcmd(struct cmd *cmd) {
+int run(struct cmd *cmd) {
struct cmd *prev;
int ispipe, ispipestart, ispipeend;
static int status;
pid_t cpid, jobid;
struct job job;
+ if (!cmd) return 0;
+
for (prev = ∅ cmd->args; prev = cmd++) {
ispipe = cmd->term == PIPE || prev->term == PIPE;
ispipestart = ispipe && prev->term != PIPE;
if (ispipe) {
if (!ispipeend && pipe(cmd->pipe) == -1) {
- warn("Unable to create pipe");
+ note("Unable to create pipe");
if (!ispipestart) closepipe(prev);
break;
}
if ((jobid = cpid = fork()) == -1) {
- warn("Unable to create child process");
+ note("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);
+ fatal("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);
+ fatal("Unable to duplicate write end of `%s' pipe", *cmd->args);
if (!closepipe(cmd)) exit(EXIT_FAILURE);
}
- if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
+ redirectfiles(cmd->rds);
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);
+ fatal("Couldn't find `%s' command", *cmd->args);
}
if (!ispipestart) {
closepipe(prev);
} else {
if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break;
if ((jobid = cpid = fork()) == -1) {
- warn("Unable to create child process");
+ note("Unable to create child process");
break;
} else if (cpid == 0) {
- if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
+ redirectfiles(cmd->rds);
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);
+ fatal("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);
+ note("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);
+ note("Unable to kill process %d; may need to manually terminate", 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");
+ note("Unable to add job to background; too many background jobs");
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;
+ waitbg();
- 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;
-
- readhist();
- while ((cmd = lex(input()))) runcmd(cmd);
- writehist();
-}
-
-#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);
+ return 1;
}
-char *prependhome(char *filename);
-
-void runstr(char *str);
-void runscript(char *filename);
-void runinteractive(void);
-void runhome(char *filename);
+int run(struct cmd *cmd);
#include <string.h>
+#include <stdint.h>
#include "stack.h"
int push(struct stack *s, void *d) {
- int result;
-
- result = 0;
if (PLUSONE(*s, t) == s->b) {
- if (!s->overwrite) return result;
+ if (!s->overwrite) return 0;
INC(*s, b);
- } else result = 1;
+ }
memmove(s->t, d, s->size);
INC(*s, t);
- return result;
+ return 1;
}
void *peek(struct stack *s) {
#define DEC(s, m) ((s).m = MINUSONE(s, m))
#define INITSTACK(s, d, o) \
- s = {sizeof*d, sizeof d, (char *)d, (char *)d, (char *)d, (char *)d, o}
+ struct stack s = {sizeof*d, sizeof d, (uint8_t *)d, (uint8_t *)d, \
+ (uint8_t *)d, (uint8_t *)d, o}
+
struct stack {
- size_t size, cap; // In bytes
- char *b, *c, *t, *data;
+ size_t size, cap;
+ uint8_t *b, *c, *t, *data;
int overwrite;
};
+++ /dev/null
-#include <termios.h>
-#include <unistd.h>
-#include <err.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <sys/errno.h>
-#include <stdio.h>
-
-#include "job.h"
-#include "history.h"
-#include "stack.h"
-
-struct termios raw, canonical;
-
-static int setconfig(struct termios *mode) {
- if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
- warn("Unable to set termios config");
- return 0;
- }
- return 1;
-}
-
-void initterm(void) {
- cfmakeraw(&raw);
- if (tcgetattr(STDIN_FILENO, &canonical) == -1)
- err(EXIT_FAILURE, "Unable to get default termios config");
- if (!setconfig(&raw)) exit(EXIT_FAILURE);
-}
-
-void deinitterm(void) {
- setconfig(&canonical);
-}
-
-int setfg(struct job job) {
- if (!setconfig(&job.config)) return 0;
- if (tcsetpgrp(STDIN_FILENO, job.id) == -1) {
- warn("Unable to bring job %d to foreground\r", job.id);
- setconfig(&raw);
- return 0;
- }
- if (killpg(job.id, SIGCONT) == -1) {
- warn("Unable to wake up job %d\r", job.id);
- return 0;
- }
- return 1;
-}
-
-int waitfg(struct job job) {
- int status, pgid, result;
-
- errno = 0;
- do waitpid(job.id, &status, WUNTRACED); while (errno == EINTR);
- if (!errno && !WIFSTOPPED(status))
- do while (waitpid(-job.id, NULL, 0) != -1); while (errno == EINTR);
- result = errno;
-
- // TODO: Use sigaction >:(
- if ((pgid = getpgid(0)) == -1 || signal(SIGTTOU, SIG_IGN) == SIG_ERR
- || tcsetpgrp(STDIN_FILENO, pgid) == -1
- || signal(SIGTTOU, SIG_DFL) == SIG_ERR) {
- warn("Unable to reclaim foreground; terminating\r");
- writehist();
- deinitterm();
- exit(EXIT_FAILURE);
- }
- if (tcgetattr(STDIN_FILENO, &job.config) == -1)
- warn("Unable to save termios config of job %d\r", job.id);
- setconfig(&raw);
- if (result) return 1;
-
- if (WIFSIGNALED(status)) {
- result = WTERMSIG(status);
- puts("\r");
- } else if (WIFSTOPPED(status)) {
- result = WSTOPSIG(status);
- job.type = SUSPENDED;
- if (push(&jobs, &job)) return result;
- warnx("Unable to add job %d to list; too many jobs\r\n"
- "Press any key to continue\r", job.id);
- getchar();
- if (setfg(job)) return waitfg(job);
- warnx("Manual intervention required for job %d\r", job.id);
- } else if (WIFEXITED(status)) result = WEXITSTATUS(status);
-
- return result;
-}
+++ /dev/null
-extern struct termios raw, canonical;
-
-void initterm(void);
-void deinitterm(void);
-int setfg(struct job job);
-int waitfg(struct job job);
#include <err.h>
+#include <stdarg.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/errno.h>
+#include <termios.h>
+#include <unistd.h>
-void *allocate(size_t s) {
- void *r;
+#include "config.h"
+#include "history.h"
+#include "input.h"
+#include "job.h"
+#include "options.h"
+#include "utils.h"
- if (!(r = malloc(s))) err(EXIT_FAILURE, "Memory allocation");
+void note(char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ (errno ? vwarn : vwarnx)(fmt, args);
+ va_end(args);
+ putchar('\r');
+}
+
+void fatal(char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ (errno ? vwarn : vwarnx)(fmt, args);
+ va_end(args);
+ putchar('\r');
+ exit(EXIT_FAILURE);
+}
+
+char *prependhome(char *name) {
+ static char *p, path[MAXPATH + 1];
+
+ if (!p) {
+ if (!(p = getenv("HOME")))
+ fatal("Unable to access $HOME$ environment variable");
+ strcpy(path, p);
+ strcat(path, "/");
+ p = path + strlen(path);
+ }
+ *p = '\0';
+ strcat(path, name);
- return memset(r, 0, s);
+ return path;
}
+void initialize(void) { // <-- TODO: Set $SHLVL$ in this function
+ cfmakeraw(&raw);
+ if (tcgetattr(STDIN_FILENO, &canonical) == -1)
+ fatal("Unable to get default termios config");
+ if (!setconfig(&raw)) exit(EXIT_FAILURE);
+ if (interactive) readhistory();
+}
+
+void deinitialize(void) {
+ if (interactive) writehistory();
+ setconfig(&canonical);
+}
-void *allocate(size_t s);
+void note(char *fmt, ...);
+void fatal(char *fmt, ...);
+char *prependhome(char *name);
+void initialize(void);
+void deinitialize(void);