#define SRCDIR "src/"
#define SRC \
SRCDIR "builtins", \
- SRCDIR "cmd", \
SRCDIR "history", \
SRCDIR "input", \
+ SRCDIR "job", \
+ SRCDIR "lex", \
SRCDIR "main", \
- SRCDIR "pg", \
+ SRCDIR "stack", \
SRCDIR "term", \
SRCDIR "utils"
-
#define BINDIR "bin/"
#define ASH BINDIR "ash"
build(NULL);
src = (char *[]){SRC, NULL};
- cflags = (char *[]){"-ferror-limit=1", NULL};
+ // cflags = (char *[]){"-ferror-limit=1", NULL};
while (*src) compile(*src++, NULL);
load('x', ASH, SRC, NULL);
#include <err.h>
-#include <unistd.h>
-#include <termios.h>
-#include <sys/errno.h>
-#include <stdlib.h>
#include <limits.h>
#include <signal.h>
-#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <termios.h>
+#include <unistd.h>
-#include "pg.h"
+#include "job.h"
+#include "stack.h"
#include "term.h"
-#include "wait.h"
#define BUILTINSIG(name) int name(char **tokens)
+struct builtin {
+ char *name;
+ BUILTINSIG((*func));
+};
+
BUILTINSIG(cd) {
if (!tokens[1]) return 1;
if (chdir(tokens[1]) != -1) return 0;
}
BUILTINSIG(fg) {
- long id;
- struct pg pg;
+ long jobid;
+ struct job *job;
if (tokens[1]) {
errno = 0;
- if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) {
+ if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno
+ || jobid <= 0) {
warn("Invalid process group id");
return 1;
}
- if (find(&spgs, (pid_t)id)) pg = *spgs.c;
- else if (find(&bgpgs, (pid_t)id)) pg = *bgpgs.c;
- else {
- warn("Unable to find process group %ld", id);
+ if (!(job = findjob((pid_t)jobid))) {
+ warnx("Unable to find process group %d", (pid_t)jobid);
return 1;
}
- } else if (!(pg = peek(recent)).id) {
+ job = deletejob();
+ } else if (!(job = pull(&jobs))) {
warnx("No processes to bring into the foreground");
return 1;
}
- if (tcsetattr(STDIN_FILENO, TCSANOW, &pg.config) == -1) {
- warn("Unable to reload termios state for process group %d", pg.id);
- return 1;
- }
- if (tcsetpgrp(STDIN_FILENO, pg.id) == -1) {
- warn("Unable to bring process group %d to foreground", pg.id);
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
- warn("Unable to set termios state back to raw mode");
- return 1;
- }
- if (killpg(pg.id, SIGCONT) == -1) {
- if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE);
- warn("Unable to wake up suspended process group %d", pg.id);
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
- warn("Unable to set termios state back to raw mode");
- return 1;
- }
- if (tokens[1]) cut(recent); else pull(recent);
- waitfg(pg.id);
+ setfg(*job);
+ waitfg(*job);
+
return 0;
}
BUILTINSIG(bg) {
- long id;
- struct pg pg;
+ long jobid;
+ struct job *job;
if (tokens[1]) {
errno = 0;
- if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) {
- warn("Invalid process group id");
+ if ((jobid = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno
+ || jobid <= 0) {
+ warn("Invalid job id");
+ return 1;
+ }
+ if (!(job = findjob((pid_t)jobid))) {
+ warnx("Unable to find job %d", (pid_t)jobid);
return 1;
}
- if (!find(&spgs, (pid_t)id)) {
- warn("Unable to find process group %ld", id);
+ if (job->type == BACKGROUND) {
+ warnx("Job %d already in background", (pid_t)jobid);
return 1;
}
- pg = *spgs.c;
- } else if (!(pg = peek(&spgs)).id) {
- warnx("No suspended processes to run in the background");
+ } else
+ for (jobs.c = MINUSONE(jobs, t); CURRENT->type == BACKGROUND; DEC(jobs, c))
+ if (jobs.c == jobs.b) {
+ warnx("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");
return 1;
}
- if (killpg(pg.id, SIGCONT) == -1) {
- warn("Unable to wake up suspended process group %d", pg.id);
+ if (killpg(job->id, SIGCONT) == -1) {
+ warn("Unable to wake up suspended process group %d", job->id);
return 1;
}
- if (tokens[1]) cut(&spgs); else pull(&spgs);
- push(&bgpgs, pg);
- recent = &bgpgs;
+ job->type = BACKGROUND;
+
return 0;
}
-struct builtin {
- char *name;
- int (*func)(char **tokens);
-};
-
#define BUILTIN(name) {#name, name}
-
BUILTINSIG(which);
struct builtin builtins[] = {
BUILTIN(cd),
+#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <err.h>
#include <sys/errno.h>
+#include "history.h"
#include "input.h"
+#include "stack.h"
#include "utils.h"
-#include "history.h"
#define HISTNAME ".ashhistory"
-// TODO: This could be its own struct and use macros like the stopped process table
-char history[HISTLEN][BUFLEN], (*hb)[BUFLEN], (*hc)[BUFLEN], (*ht)[BUFLEN];
-static char *histpath;
+static char *histpath, histarr[HISTLEN + 1][BUFLEN + 1];
+struct stack INITSTACK(history, histarr, 1);
void readhist(void) {
- char *homepath;
+ char *homepath, buffer[BUFLEN + 1];
FILE *histfile;
int e;
strcat(histpath, "/");
strcat(histpath, HISTNAME);
- ht = hc = hb = history;
if (!(histfile = fopen(histpath, "r"))) {
if (errno == ENOENT) return;
err(EXIT_FAILURE, "Unable to open history file for reading");
}
- while (fgets(*ht, BUFLEN, histfile)) {
- *(*ht + strlen(*ht) - 1) = '\0'; // Get rid of newline
- ht = history + (ht - history + 1) % HISTLEN;
- if (ht == hb) hb = NULL;
+ while (fgets(buffer, history.size, histfile)) {
+ *(buffer + strlen(buffer) - 1) = '\0';
+ push(&history, buffer);
}
- e = ferror(histfile) || !feof(histfile);
+ 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 (e) err(EXIT_FAILURE, "Unable to read from history file");
-
- if (!hb) hb = history + (ht - history + 1) % HISTLEN;
- hc = ht;
}
void writehist(void) {
return;
}
- while (hb != ht) {
- if (fputs(*hb, histfile) == EOF) {
+ 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");
break;
}
warn("Unable to write newline to history file");
break;
}
- hb = history + (hb - history + 1) % HISTLEN;
}
if (fclose(histfile) == EOF) warn("Unable to close history stream");
-#define HISTLEN 101
-#define BUFLEN 1001
+#define HISTLEN 100
+#define BUFLEN 1000
-extern char history[HISTLEN][BUFLEN], (*hb)[BUFLEN], (*hc)[BUFLEN], (*ht)[BUFLEN];
+extern struct stack history;
void readhist(void);
void writehist(void);
#include <unistd.h>
#include "history.h"
-#include "pg.h"
+#include "job.h"
+#include "stack.h"
#define PROMPT "% "
-enum {
+enum character {
CTRLC = '\003',
CTRLD,
+ CLEAR = '\014',
ESCAPE = '\033',
+ FORWARD = 'f',
+ BACKWARD = 'b',
+ ARROW = '[',
UP = 'A',
DOWN,
RIGHT,
BACKSPACE = '\177',
};
-int getinput(char *buffer) {
+static char buffer[BUFLEN + 1];
+
+char *input(void) {
char *cursor, *end;
int c, i;
- signal(SIGCHLD, waitbg);
+ signal(SIGCHLD, waitbg); // TODO: Use sigaction for portability
+reset:
end = cursor = buffer;
- *buffer = '\0';
+ *history.t = *buffer = '\0';
+ history.c = history.t;
while (buffer == end) {
fputs(PROMPT, stdout);
while ((c = getchar()) != '\n') {
if (c >= ' ' && c <= '~') {
- if (end - buffer == BUFLEN - 1) continue;
+ if (end - buffer == BUFLEN) continue;
memmove(cursor + 1, cursor, end - cursor);
*cursor++ = c;
*++end = '\0';
} else switch (c) {
case CTRLC:
puts("^C");
- ungetc('\n', stdin);
- continue;
+ goto reset;
case CTRLD:
puts("^D");
signal(SIGCHLD, SIG_DFL);
- return 0;
+ return NULL;
+ case CLEAR:
+ fputs("\033[H\033[J", stdout);
+ fputs(PROMPT, stdout);
+ fputs(buffer, stdout);
+ continue;
case ESCAPE:
- if ((c = getchar()) != '[') {
- ungetc(c, stdin);
- break;
- }
switch ((c = getchar())) {
- case UP:
- case DOWN:
- if (hc == (c == UP ? hb : ht)) continue;
- if (strcmp(*hc, buffer) != 0) strcpy(*ht, buffer);
-
- putchar('\r');
- for (i = end - buffer + strlen(PROMPT); i > 0; --i) putchar(' ');
- putchar('\r');
- fputs(PROMPT, stdout);
-
- hc = history + ((hc - history + (c == UP ? -1 : 1)) % HISTLEN + HISTLEN)
- % HISTLEN;
- strcpy(buffer, *hc);
- end = cursor = buffer + strlen(buffer);
-
- fputs(buffer, stdout);
+ case FORWARD:
+ while (cursor != end && *cursor != ' ') putchar(*cursor++);
+ while (cursor != end && *cursor == ' ') putchar(*cursor++);
break;
- case LEFT:
- if (cursor > buffer) {
- putchar('\b');
- --cursor;
- }
+ case BACKWARD:
+ while (cursor != buffer && *(cursor - 1) == ' ') putchar((--cursor, '\b'));
+ while (cursor != buffer && *(cursor - 1) != ' ') putchar((--cursor, '\b'));
break;
- case RIGHT:
- if (cursor < end) putchar(*cursor++);
+ case ARROW:
+ switch ((c = getchar())) {
+ case UP:
+ case DOWN:
+ if (history.c == (c == UP ? history.b : history.t)) continue;
+
+ putchar('\r');
+ for (i = end - buffer + strlen(PROMPT); i > 0; --i) putchar(' ');
+ putchar('\r');
+
+ if (strcmp(history.c, buffer) != 0) strcpy(history.t, buffer);
+ if (c == UP) DEC(history, c); else INC(history, c);
+ strcpy(buffer, history.c);
+ end = cursor = buffer + strlen(buffer);
+
+ fputs(PROMPT, stdout);
+ fputs(buffer, stdout);
+ break;
+ case LEFT:
+ if (cursor > buffer) putchar((--cursor, '\b'));
+ break;
+ case RIGHT:
+ if (cursor < end) putchar(*cursor++);
+ break;
+ }
break;
+ default:
+ ungetc(c, stdin);
}
break;
case BACKSPACE:
}
}
}
-
fpurge(stdout);
- strcpy(*ht, buffer);
- ht = history + (ht - history + 1) % HISTLEN;
- hc = ht;
- if (ht == hb) hb = history + (hb - history + 1) % HISTLEN;
+ push(&history, buffer);
signal(SIGCHLD, SIG_DFL);
- return 1;
+ return buffer;
}
-int getinput(char *buffer);
+char *input(void);
--- /dev/null
+#include <err.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#include "job.h"
+#include "stack.h"
+
+#define MAXJOBS 100
+
+static struct job jobarray[MAXJOBS + 1];
+struct stack INITSTACK(jobs, jobarray, 0);
+
+void sigkill(pid_t jobid) {
+ if (killpg(jobid, SIGKILL) == -1)
+ warn("Unable to kill process group %d; manual termination may be required",
+ jobid);
+}
+
+void *findjob(pid_t jobid) {
+ if (jobs.b == jobs.t) return NULL;
+ for (jobs.c = jobs.b; CURRENT->id != jobid; INC(jobs, c))
+ if (jobs.c == jobs.t) return NULL;
+ return jobs.c;
+}
+
+void *deletejob(void) {
+ memcpy(jobs.t, jobs.c, jobs.size);
+ memmove(jobs.c, PLUSONE(jobs, c), jobs.t - jobs.c);
+ return MINUSONE(jobs, t);
+}
+
+void waitbg(int sig) {
+ 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;
+ 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");
+ }
+}
--- /dev/null
+enum jobtype {
+ BACKGROUND,
+ SUSPENDED,
+};
+
+struct job {
+ pid_t id;
+ struct termios config;
+ enum jobtype type;
+};
+
+#define CURRENT ((struct job *)jobs.c)
+extern struct stack jobs;
+
+void sigkill(pid_t jobid);
+void *findjob(pid_t jobid);
+void *deletejob(void);
+void waitbg(int sig);
#include <stddef.h>
#include "history.h"
-#include "cmd.h"
+#include "lex.h"
-int delimiter(char c) {
+static char *tokens[1 + BUFLEN + 1];
+static struct cmd cmds[1 + (BUFLEN + 1) / 2 + 1];
+
+static int delimiter(char c) {
return c == ' ' || c == '&' || c == '|' || c == ';' || c == '`' || c == '\0';
}
-enum terminator strp2term(char **strp) {
- enum terminator result;
+static enum terminator strp2term(char **strp) {
char *p;
+ enum terminator term;
switch (*(p = (*strp)++)) {
case '&':
- result = **strp == '&' ? (++*strp, AND) : BG;
+ term = **strp == '&' ? (++*strp, AND) : BG;
break;
case '|':
- result = **strp == '|' ? (++*strp, OR) : PIPE;
+ term = **strp == '|' ? (++*strp, OR) : PIPE;
break;
default:
- result = SEMI;
+ term = SEMI;
}
*p = '\0';
- return result;
+ return term;
}
-struct cmd *getcmds(char *b) {
- size_t i, j;
+struct cmd *lex(char *b) {
char **t;
struct cmd *c;
- enum terminator term;
- static char *tokens[BUFLEN + 1];
- static struct cmd result[BUFLEN / 2 + 1];
- *tokens = NULL;
+ if (!b) return NULL;
t = tokens + 1;
- c = result;
+ c = cmds + 1;
while (*b == ' ') ++b;
c->args = t;
while (*b) switch (*b) {
case ';':
case '&':
case '|':
- if (!*(t - 1)) {
- strp2term(&b);
- break;
- }
- *t++ = NULL;
- c++->type = strp2term(&b);
- c->args = t;
+ if (*(t - 1)) {
+ *t++ = NULL;
+ c++->type = strp2term(&b);
+ c->args = t;
+ } else strp2term(&b);
break;
case '`':
*t++ = ++b;
}
*c = (struct cmd){0};
- return result;
+ return cmds;
}
int pipe[2];
};
-struct cmd *getcmds(char *b);
+struct cmd *lex(char *b);
#include <err.h>
-#include <signal.h> // TODO: Use sigaction for portability? but this is also used with killpg, so don't remove it
+#include <signal.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/errno.h>
#include <termios.h>
#include <unistd.h>
-#include <sys/errno.h>
+#include <stdio.h> // DEBUG
#include "builtins.h"
-#include "cmd.h"
+#include "stack.h"
#include "history.h"
#include "input.h"
-#include "pg.h"
+#include "lex.h"
+#include "job.h"
#include "term.h"
#include "utils.h"
-int closepipe(struct cmd *cmd) {
+static int closepipe(struct cmd *cmd) {
int result;
if (!cmd->args) return 1;
}
int main(void) {
- char buffer[BUFLEN];
struct cmd *cmd, *prev;
- int status;
- pid_t id;
+ int ispipe, ispipestart, ispipeend, status;
+ pid_t cpid, jobid;
+ struct job job;
initterm();
readhist();
- while (getinput(buffer)) {
- prev = cmd = getcmds(buffer);
- while (prev->args) ++prev;
- while (cmd->args) {
- if (cmd->type != PIPE && prev->type != PIPE) {
- if (isbuiltin(cmd->args, &status)) break;
- if ((id = fork()) == 0 && execvp(*cmd->args, cmd->args) == -1)
- err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
- if (setpgid(id, id) == -1) {
- warn("Unable to set pgid of `%s' command to %d", *cmd->args, id);
- if (kill(id, SIGKILL) == -1)
- warn("Unable to kill process %d; manual termination may be required",
- id);
- break;
- }
- } else {
- if (cmd->type == PIPE && pipe(cmd->pipe) == -1) {
+ while ((cmd = lex(input()))) {
+ while (prev = cmd++, cmd->args) {
+ ispipe = cmd->type == PIPE || prev->type == PIPE;
+ ispipestart = ispipe && prev->type != PIPE;
+ ispipeend = ispipe && cmd->type != PIPE;
+
+ if (ispipe) {
+ if (!ispipeend && pipe(cmd->pipe) == -1) {
warn("Unable to create pipe");
- if (prev->type == PIPE) closepipe(prev);
+ if (!ispipestart) closepipe(prev);
break;
}
- if ((id = fork()) == 0) {
- 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 (cmd->type == PIPE) {
+ 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 (execvp(*cmd->args, cmd->args) == -1)
err(EXIT_FAILURE, "Couldn't find `%s' command", *cmd->args);
}
- if (prev->type == PIPE) closepipe(prev);
- else push(&bgpgs, (struct pg){.id = id, .config = canonical,});
- if (setpgid(id, peek(&bgpgs).id) == -1) {
- if (errno != ESRCH) {
- warn("Unable to set pgid of `%s' command to %d", *cmd->args, peek(&bgpgs).id);
- if (kill(id, SIGKILL) == -1)
- warn("Unable to kill process %d; manual termination may be required",
- id);
- }
+ if (!ispipestart) {
+ closepipe(prev);
+ jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
+ }
+ } else {
+ if (isbuiltin(cmd->args, &status)) break;
+ if ((jobid = cpid = fork()) == -1) {
+ warn("Unable to create child process");
break;
+ } else if (cpid == 0 && 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);
}
- if (cmd->type != PIPE) id = peek(&bgpgs).id;
+ break;
}
- if (cmd->type == BG) {
- push(&bgpgs, (struct pg){.id = id, .config = canonical,});
- recent = &bgpgs;
+
+ job = (struct job){.id = jobid, .config = canonical, .type = BACKGROUND};
+ if (ispipestart || cmd->type == 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->type != PIPE) {
- if (prev->type == PIPE) pull(&bgpgs);
- if (!setfg(id)) break;
- status = waitfg(id);
+ if (!setfg(job)) break;
+ status = waitfg(job);
+ if (cmd->type == AND && status != 0) break;
+ if (cmd->type == OR && status == 0) break;
}
- if (cmd->type == AND && status != 0) break;
- if (cmd->type == OR && status == 0) break;
- prev = cmd++;
}
waitbg(0);
}
+++ /dev/null
-#include <string.h>
-#include <termios.h>
-#include <signal.h>
-#include <err.h>
-#include <sys/wait.h>
-#include <sys/errno.h>
-
-#include "pg.h"
-
-#define INITPGS(pgs) {MAXPG, (pgs).data, (pgs).data, (pgs).data}
-struct pgs spgs = INITPGS(spgs), bgpgs = INITPGS(bgpgs), *recent = &spgs;
-
-void sigkill(pid_t pgid) {
- if (killpg(pgid, SIGKILL) == -1)
- warn("Unable to kill process group %d; manual termination may be required",
- pgid);
-}
-
-void push(struct pgs *pgs, struct pg pg) {
- if (PLUSONE(*pgs, t) == pgs->b) {
- sigkill(pgs->b->id);
- INC(*pgs, b);
- }
- *pgs->t = pg;
- INC(*pgs, t);
-}
-
-struct pg peek(struct pgs *pgs) {
- if (pgs->t == pgs->b) return (struct pg){0};
- return *MINUSONE(*pgs, t);
-}
-
-struct pg pull(struct pgs *pgs) {
- struct pg result;
-
- if ((result = peek(pgs)).id) DEC(*pgs, t);
- return result;
-}
-
-struct pg *find(struct pgs *pgs, pid_t pgid) {
- if (pgid == 0 || pgs->t == pgs->b) return NULL;
- for (pgs->c = MINUSONE(*pgs, t); pgs->c->id != pgid; DEC(*pgs, c))
- if (pgs->c == pgs->b) return NULL;
- return pgs->c;
-}
-
-void cut(struct pgs *pgs) {
- if (pgs->c >= pgs->b) {
- memmove(pgs->b + 1, pgs->b, sizeof(struct pg) * (pgs->c - pgs->b));
- ++pgs->b;
- INC(*pgs, c);
- } else {
- memmove(pgs->c, pgs->c + 1, sizeof(struct pg) * (pgs->t - pgs->c - 1));
- --pgs->t;
- }
-}
-
-void waitbg(int sig) {
- int status;
- pid_t pid, pgid;
-
- (void)sig;
- for (bgpgs.c = bgpgs.b; bgpgs.c != bgpgs.t; INC(bgpgs, c)) {
- while ((pid = waitpid(-bgpgs.c->id, &status, WNOHANG | WUNTRACED)) > 0)
- if (WIFSTOPPED(status)) {
- push(&spgs, *bgpgs.c);
- cut(&bgpgs);
- recent = &spgs;
- pid = 0;
- }
- if (pid == -1 && errno != ECHILD)
- warn("Unable to wait on some child processes");
- }
-}
+++ /dev/null
-struct pg {
- pid_t id;
- struct termios config;
-};
-
-#define PLUSONE(s, m) ((s).data + ((s).m - (s).data + 1) % (s).len)
-#define INC(s, m) ((s).m = PLUSONE(s, m))
-#define MINUSONE(s, m) ((s).data + ((s).m - (s).data + (s).len - 1) % (s).len)
-#define DEC(s, m) ((s).m = MINUSONE(s, m))
-
-#define MAXPG 128
-struct pgs {
- size_t len;
- struct pg *b, *c, *t, data[MAXPG];
-};
-
-extern struct pgs spgs, bgpgs, *recent;
-
-void sigkill(pid_t pgid);
-void push(struct pgs *pgs, struct pg pg);
-struct pg peek(struct pgs *pgs);
-struct pg pull(struct pgs *pgs);
-struct pg *find(struct pgs *pgs, pid_t pgid);
-void cut(struct pgs *pgs);
-void waitbg(int sig);
--- /dev/null
+#include <string.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;
+ INC(*s, b);
+ } else result = 1;
+ memmove(s->t, d, s->size);
+ INC(*s, t);
+ return result;
+}
+
+void *peek(struct stack *s) {
+ if (s->t == s->b) return NULL;
+ return MINUSONE(*s, t);
+}
+
+void *pull(struct stack *s) {
+ if (s->t == s->b) return NULL;
+ return DEC(*s, t);
+}
--- /dev/null
+#define PLUSONE(s, m) ((s).data + ((s).m - (s).data + (s).size) % (s).cap)
+#define MINUSONE(s, m) \
+ ((s).data + ((s).m - (s).data - (s).size + (s).size * (s).cap) % (s).cap)
+#define INC(s, m) ((s).m = PLUSONE(s, m))
+#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 {
+ size_t size, cap; // In bytes
+ char *b, *c, *t, *data;
+ int overwrite;
+};
+
+int push(struct stack *s, void *d);
+void *peek(struct stack *s);
+void *pull(struct stack *s);
#include <sys/errno.h>
#include <stdio.h>
-#include "pg.h"
+#include "job.h"
+#include "history.h"
+#include "stack.h"
-struct termios canonical, raw;
-pid_t self;
+struct termios raw, canonical;
+static pid_t self;
+
+static int tcsetraw(void) {
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1) {
+ warn("Unable to set termios state to raw mode");
+ return 0;
+ }
+
+ return 1;
+}
void initterm(void) {
if (tcgetattr(STDIN_FILENO, &canonical) == -1)
raw = canonical;
raw.c_lflag &= ~ICANON & ~ECHO & ~ISIG;
raw.c_lflag |= ECHONL;
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
- err(EXIT_FAILURE, "Unable to initialize termios structure");
+ if (!tcsetraw()) exit(EXIT_FAILURE);
if ((self = getpgid(0)) == -1) err(EXIT_FAILURE, "Unable to get pgid of self");
}
warn("Unable to return to initial terminal settings");
}
-int setfg(pid_t pgid) {
- if (tcsetattr(STDIN_FILENO, TCSANOW, &canonical) == -1)
+static void tcsetself(void) {
+ if (signal(SIGTTOU, SIG_IGN) == SIG_ERR) {
+ warn("Unable to ignore SIGTTOU signal");
+ goto quit;
+ }
+ if (tcsetpgrp(STDIN_FILENO, self) == -1) {
+ warn("Unable to set self as foreground process; exiting");
+ goto quit;
+ }
+ if (signal(SIGTTOU, SIG_DFL) == SIG_ERR) warn("Ignoring signal SIGTTOU");
+
+ return;
+
+quit:
+ writehist();
+ deinitterm();
+ exit(EXIT_FAILURE);
+}
+
+int setfg(struct job job) {
+ if (tcsetattr(STDIN_FILENO, TCSANOW, &job.config) == -1)
warn("Unable to set termios structure");
- if (tcsetpgrp(STDIN_FILENO, pgid) == -1) {
- warn("Unable to bring process group %d to foreground", pgid);
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
- warn("Unable to set termios state back to raw");
- sigkill(pgid);
- if (killpg(pgid, SIGKILL) == -1)
- warn("Unable to kill process group %d; manual termination required", pgid);
- return 0;
+ if (tcsetpgrp(STDIN_FILENO, job.id) == -1) {
+ warn("Unable to bring process group %d to foreground", job.id);
+ goto setraw;
}
+ if (killpg(job.id, SIGCONT) == -1) {
+ warn("Unable to wake up suspended process group %d", job.id);
+ goto setself;
+ }
+
return 1;
+
+setself:
+ tcsetself();
+
+setraw:
+ tcsetraw();
+
+ sigkill(job.id);
+
+ return 0;
}
-int waitfg(pid_t pgid) {
+int waitfg(struct job job) {
int status, result;
- struct pg pg;
- if (waitpid(pgid, &status, WUNTRACED) != -1 && !WIFSTOPPED(status)) {
- while (waitpid(-pgid, NULL, 0) != -1);
- if (errno != ECHILD && killpg(pgid, SIGKILL) == -1)
- err(EXIT_FAILURE, "Unable to kill child process group %d", pgid);
+wait:
+ if (waitpid(job.id, &status, WUNTRACED) != -1 && !WIFSTOPPED(status)) {
+ while (waitpid(-job.id, NULL, 0) != -1);
+ if (errno != ECHILD && killpg(job.id, SIGKILL) == -1)
+ err(EXIT_FAILURE, "Unable to kill child process group %d", job.id);
}
- if (signal(SIGTTOU, SIG_IGN) == SIG_ERR)
- err(EXIT_FAILURE, "Unable to ignore SIGTTOU signal");
- if (tcsetpgrp(STDIN_FILENO, self) == -1)
- err(EXIT_FAILURE, "Unable to set self back to foreground process group");
- if (signal(SIGTTOU, SIG_DFL) == SIG_ERR) warn("Ignoring signal SIGTTOU");
+
+ tcsetself();
if (WIFSIGNALED(status)) {
putchar('\n');
result = WTERMSIG(status);
} else if (WIFSTOPPED(status)) {
- pg = (struct pg){
- .id = pgid,
- .config = canonical,
- };
- if (tcgetattr(STDIN_FILENO, &pg.config) == -1)
- warn("Unable to save termios state for stopped process group %d", pgid);
- push(&spgs, pg);
- recent = &spgs;
+ if (tcgetattr(STDIN_FILENO, &job.config) == -1)
+ warn("Unable to save termios state for stopped process group %d", job.id);
+ job.type = SUSPENDED;
+ if (!push(&jobs, &job)) {
+ warnx("Unable to add process to job list; too many jobs\n"
+ "Press any key to continue");
+ getchar();
+ if (setfg(job)) goto wait;
+ warn("Unable to continue foreground process; terminating");
+ sigkill(job.id);
+ }
result = WSTOPSIG(status);
} else result = WEXITSTATUS(status);
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
- warn("Unable to set termios state back to raw");
+ tcsetraw();
return result;
}
-extern struct termios canonical, raw;
-extern pid_t self;
+extern struct termios raw, canonical;
void initterm(void);
void deinitterm(void);
-int setfg(pid_t pgid);
-int waitfg(pid_t pgid);
+int setfg(struct job job);
+int waitfg(struct job job);