struct pgs {
size_t len;
struct pg *b, *c, *t, data[MAXPG];
-} sus = INITPGS(sus), bg = INITPGS(bg);
+} spgs = INITPGS(spgs), bgpgs = INITPGS(bgpgs);
-struct pgs *recent = &sus;
+struct pgs *recent = &spgs;
#define PLUSONE(s, m) ((s).data + ((s).m - (s).data + 1) % (s).len)
#define INC(s, m) ((s).m = PLUSONE(s, m))
if (waitpid(cpgid, &status, WUNTRACED) != -1 && !WIFSTOPPED(status)) {
while (waitpid(-cpgid, NULL, 0) != -1);
- if (errno != ECHILD && killpg(cpgid, SIGKILL) == -1) _Exit(EXIT_FAILURE);
+ if (errno != ECHILD && killpg(cpgid, SIGKILL) == -1)
+ err(EXIT_FAILURE, "Unable to kill child process group %d", cpgid);
}
- if (signal(SIGTTOU, SIG_IGN) == SIG_ERR) _Exit(EXIT_FAILURE);
- if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE);
+ 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");
if (WIFSIGNALED(status)) {
};
if (tcgetattr(STDIN_FILENO, &pg.config) == -1)
warn("Unable to save termios state for stopped process group %d", cpgid);
- push(&sus, pg);
- recent = &sus;
+ push(&spgs, pg);
+ recent = &spgs;
result = WSTOPSIG(status);
} else result = WEXITSTATUS(status);
return result;
}
-int builtin(char **tokens) {
+#define BUILTINSIG(name) int name(char **tokens)
+
+BUILTINSIG(cd) {
+ if (!tokens[1]) return 1;
+ if (chdir(tokens[1]) != -1) return 0;
+ warn("Unable to change directory to `%s'", tokens[1]);
+ return 1;
+}
+
+BUILTINSIG(fg) {
long id;
struct pg pg;
- int fg;
-
- if (strcmp(tokens[0], "cd") == 0) {
- if (chdir(tokens[1]) == -1)
- warn("Unable to change directory to `%s'", tokens[1]);
- } else if (strcmp(tokens[0], "fg") == 0) {
- if (tokens[1]) {
- errno = 0;
- if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) {
- warn("Invalid process group id");
- return 1;
- }
- if (find(&sus, (pid_t)id)) pg = *sus.c;
- else if (find(&bg, (pid_t)id)) pg = *bg.c;
- else {
- warn("Unable to find process group %ld", id);
- return 1;
- }
- } else if (!(pg = peek(recent)).id) {
- 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");
+
+ if (tokens[1]) {
+ errno = 0;
+ if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) {
+ warn("Invalid process group id");
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");
+ 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);
return 1;
}
- if (tokens[1]) delete(recent); else pull(recent);
- waitfg(pg.id);
- } else if (strcmp(tokens[0], "bg") == 0) {
- if (tokens[1]) {
- errno = 0;
- if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) {
- warn("Invalid process group id");
- return 1;
- }
- if (!find(&sus, (pid_t)id)) {
- warn("Unable to find process group %ld", id);
- return 1;
- }
- pg = *sus.c;
- } else if (!(pg = peek(&sus)).id) {
- warnx("No suspended processes to run in the background");
+ } else if (!(pg = peek(recent)).id) {
+ 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]) delete(recent); else pull(recent);
+ waitfg(pg.id);
+ return 0;
+}
+
+BUILTINSIG(bg) {
+ long id;
+ struct pg pg;
+
+ if (tokens[1]) {
+ errno = 0;
+ if ((id = strtol(tokens[1], NULL, 10)) == LONG_MAX && errno || id <= 0) {
+ warn("Invalid process group id");
return 1;
}
- if (killpg(pg.id, SIGCONT) == -1) {
- warn("Unable to wake up suspended process group %d", pg.id);
+ if (!find(&spgs, (pid_t)id)) {
+ warn("Unable to find process group %ld", id);
return 1;
}
- if (tokens[1]) delete(&sus); else pull(&sus);
- push(&bg, pg);
- recent = &bg;
- } else {
- return 0;
+ pg = *spgs.c;
+ } else if (!(pg = peek(&spgs)).id) {
+ warnx("No suspended processes to run in the background");
+ return 1;
+ }
+ if (killpg(pg.id, SIGCONT) == -1) {
+ warn("Unable to wake up suspended process group %d", pg.id);
+ return 1;
}
+ if (tokens[1]) delete(&spgs); else pull(&spgs);
+ push(&bgpgs, pg);
+ recent = &bgpgs;
+ return 0;
+}
+
+struct builtin {
+ char *name;
+ int (*func)(char **tokens);
+};
+
+#define BUILTIN(name) {#name, name}
+BUILTINSIG(which);
+struct builtin builtins[] = {
+ BUILTIN(cd),
+ BUILTIN(fg),
+ BUILTIN(bg),
+ BUILTIN(which),
+ BUILTIN(NULL),
+};
+
+BUILTINSIG(which) {
+ struct builtin *builtin;
+
+ if (!tokens[1]) return 1;
+ for (builtin = builtins; builtin->func; ++builtin)
+ if (strcmp(tokens[1], builtin->name) == 0) {
+ puts("Built-in command");
+ return 0;
+ }
+ // TODO: Find command in PATH
return 1;
}
+int checkbuiltin(char **args, int *statusp) {
+ struct builtin *builtinp;
+
+ for (builtinp = builtins; builtinp->func; ++builtinp)
+ if (strcmp(*args, builtinp->name) == 0) {
+ *statusp = builtinp->func(args);
+ return 1;
+ }
+ return 0;
+}
+
void waitbg(int sig) {
int status;
pid_t pid, pgid;
(void)sig;
- for (bg.c = bg.b; bg.c != bg.t; INC(bg, c))
- do switch ((pid = waitpid(-bg.c->id, &status, WNOHANG | WUNTRACED))) {
+ for (bgpgs.c = bgpgs.b; bgpgs.c != bgpgs.t; INC(bgpgs, c))
+ do switch ((pid = waitpid(-bgpgs.c->id, &status, WNOHANG | WUNTRACED))) {
case -1:
if (errno != ECHILD) warn("Unable to wait on some child processes");
case 0:
break;
default:
if (WIFSTOPPED(status)) {
- push(&sus, *bg.c);
- delete(&bg);
- recent = &sus;
+ push(&spgs, *bgpgs.c);
+ delete(&bgpgs);
+ recent = &spgs;
pid = 0;
}
} while (pid > 0);
int main(void) {
char *input;
struct cmd *cmds;
- pid_t cpid;
- int status;
+ struct builtin *builtin;
+ pid_t pipegpid, cpid, id;
+ int status, cpipe[2], ppipe[2];
readhist();
if (atexit(writehist) == -1)
raw.c_lflag &= ~ICANON & ~ECHO & ~ISIG;
raw.c_lflag |= ECHONL;
if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
- err(EXIT_FAILURE, "Unable to set termios structure");
+ err(EXIT_FAILURE, "Unable to initialize termios structure");
if (atexit(ashexit) == -1)
err(EXIT_FAILURE, "Unable to add `ashexit()' to `atexit()'");
if ((self = getpgid(0)) == -1) err(EXIT_FAILURE, "Unable to get pgid of self");
- for (;; waitbg(0)) {
+ for (pipegpid = 0;; waitbg(0)) {
signal(SIGCHLD, waitbg);
- if (!(input = getinput())) continue;
- signal(SIGCHLD, SIG_DFL);
- for (cmds = getcmds(input); (*cmds).args; ++cmds) {
- switch ((*cmds).type) {
- case PIPE:
- break;
- case OR:
- case BG:
- case AND:
- case SEMI:
- if (!builtin((*cmds).args)) {
- if ((cpid = fork()) == -1) {
- warn("Unable to fork command `%s'", *(*cmds).args);
- goto end;
- } else if (cpid == 0) {
- if (execvp(*(*cmds).args, (*cmds).args) == -1) {
- warn("Unable to run command `%s'", *(*cmds).args);
+ if (!(input = getinput())) {
+ signal(SIGCHLD, SIG_DFL); // TODO: Make this not repeated...
+ continue;
+ }
+ signal(SIGCHLD, SIG_DFL); // ...with here
+ ppipe[1] = ppipe[0] = 0;
+ for (cmds = getcmds(input); cmds->args; ++cmds) {
+ if (cmds->type == PIPE) {
+ if (pipe(cpipe) == -1) warn("Unable to create pipe");
+ if ((cpid = fork()) == 0) {
+ if (dup2(ppipe[0], 0) == -1) warn("Unable to read from pipe");
+ if (dup2(cpipe[1], 1) == -1) warn("Unable to write to pipe");
+ close(ppipe[0]);
+ close(ppipe[1]);
+ close(cpipe[0]);
+ close(cpipe[1]);
+ if (checkbuiltin(cmds->args, &status)) _Exit(EXIT_SUCCESS);
+ if (execvp(*cmds->args, cmds->args) == -1) {
+ warn("Unable to exec `%s' in pipe", *cmds->args);
+ _Exit(EXIT_FAILURE);
+ }
+ }
+ if (ppipe[0] && close(ppipe[0]) == -1)
+ warn("Unable to close read end of previous pipe");
+ if (ppipe[1] && close(ppipe[1]) == -1)
+ warn("Unable to close write end of previous pipe");
+ ppipe[0] = cpipe[0];
+ ppipe[1] = cpipe[1];
+ if (!pipegpid) {
+ pipegpid = cpid;
+ push(&bgpgs, (struct pg){.id = pipegpid, .config = canonical,});
+ }
+ setpgid(cpid, pipegpid);
+ continue;
+ } else {
+ if (pipegpid || !checkbuiltin(cmds->args, &status)) {
+ if ((cpid = fork()) == 0) {
+ if (pipegpid) {
+ if (dup2(ppipe[0], 0) == -1) {
+ warn("Unable to read from pipe");
+ _Exit(EXIT_FAILURE);
+ }
+ close(ppipe[0]);
+ close(ppipe[1]);
+ if (checkbuiltin(cmds->args, &status)) _Exit(EXIT_SUCCESS);
+ }
+ if (execvp(*cmds->args, cmds->args) == -1) {
+ warn("Unable to exec `%s'", *cmds->args);
_Exit(EXIT_FAILURE);
}
}
- if (setpgid(cpid, cpid) == -1) {
- warn("Unable to set process group of process %d", cpid);
- if (kill(cpid, SIGKILL) == -1)
- warn("Unable to kill process %d; manual termination required", cpid);
- goto end;
- }
- if ((*cmds).type == BG) {
- push(&bg, (struct pg){
- .id = cpid,
- .config = canonical,
- });
- recent = &bg;
- } else {
+ if (pipegpid) {
+ id = pipegpid;
+ close(ppipe[0]);
+ close(ppipe[1]);
+ } else id = cpid;
+ setpgid(cpid, id);
+ if (cmds->type == BG) recent = &bgpgs;
+ else {
if (tcsetattr(STDIN_FILENO, TCSANOW, &canonical) == -1)
warn("Unable to set termios structure");
- if (tcsetpgrp(STDIN_FILENO, cpid) == -1) {
- warn("Unable to bring process group %d to foreground", cpid);
- if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
- warn("Unable to set termios state back to raw");
- if (killpg(cpid, SIGKILL) == -1)
- warn("Unable to kill process group %d; manual termination required", cpid);
- goto end;
- }
- if (killpg(cpid, SIGCONT) == -1) {
- if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE);
- warn("Process group %d may be blocked; killing it", cpid);
- if (killpg(cpid, SIGKILL) == -1)
- warn("Unable to kill process group %d; manual termination required", cpid);
- else warn("Successfully terminated process group %d", cpid);
+ if (tcsetpgrp(STDIN_FILENO, id) == -1) {
+ warn("Unable to bring process group %d to foreground", id);
if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
warn("Unable to set termios state back to raw");
- goto end;
+ if (killpg(id, SIGKILL) == -1)
+ warn("Unable to kill process group %d; manual termination required", id);
+ break;
}
- status = waitfg(cpid);
- if ((*cmds).type == AND && status != 0) goto end;
- if ((*cmds).type == OR && status == 0) goto end;
+ // if (killpg(id, SIGCONT) == -1) { // ??
+ // if (tcsetpgrp(STDIN_FILENO, self) == -1) _Exit(EXIT_FAILURE);
+ // warn("Process group %d may be blocked; killing it", id);
+ // if (killpg(id, SIGKILL) == -1)
+ // warn("Unable to kill process group %d; manual termination required", id);
+ // else warn("Successfully terminated process group %d", id);
+ // if (tcsetattr(STDIN_FILENO, TCSANOW, &raw) == -1)
+ // warn("Unable to set termios state back to raw");
+ // break;
+ // }
+ if (pipegpid) pull(&bgpgs);
+ status = waitfg(id);
}
}
+ if (cmds->type == AND && status != 0) break;
+ if (cmds->type == OR && status == 0) break;
+ if (pipegpid) pipegpid = 0;
}
}
- end:;
}
-
return 0;
}