]> Trent Huber's Code - thus.git/commitdiff
Second phase refactoring; minor feature additions
authorTrent Huber <trentmhuber@gmail.com>
Mon, 26 May 2025 02:55:07 +0000 (22:55 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Mon, 26 May 2025 02:55:07 +0000 (22:55 -0400)
17 files changed:
build.c
src/builtins.c
src/history.c
src/history.h
src/input.c
src/input.h
src/job.c [new file with mode: 0644]
src/job.h [new file with mode: 0644]
src/lex.c [moved from src/cmd.c with 57% similarity]
src/lex.h [moved from src/cmd.h with 80% similarity]
src/main.c
src/pg.c [deleted file]
src/pg.h [deleted file]
src/stack.c [new file with mode: 0644]
src/stack.h [new file with mode: 0644]
src/term.c
src/term.h

diff --git a/build.c b/build.c
index ff40a7529533992a839cff264db65ed0c9a1cbf4..de2b9b1fb31d14ee093e1e31da0c81687d549981 100644 (file)
--- a/build.c
+++ b/build.c
@@ -3,14 +3,14 @@
 #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"
 
@@ -20,7 +20,7 @@ int main(void) {
    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);
index 5edcdf88e2c732d40e5f99d1840b8409b06b86eb..8199fbfbde866b02db2f215898c76df40ea1e951 100644 (file)
@@ -1,19 +1,24 @@
 #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;
@@ -22,83 +27,72 @@ BUILTINSIG(cd) {
 }
 
 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),
index 1509e664a055fdfc4d331fe1186a6888f995acd6..fc2a351c3698e506372fe67de57087e93aea10d0 100644 (file)
@@ -1,21 +1,21 @@
+#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;
 
@@ -26,23 +26,18 @@ void readhist(void) {
    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) {
@@ -53,8 +48,8 @@ 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;
        }
@@ -62,7 +57,6 @@ void writehist(void) {
            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");
index 54e558fa8470abb94f2daef3266fa6db5c028259..c395a45fd8e59863910f79c1197cb563ccc43fd5 100644 (file)
@@ -1,7 +1,7 @@
-#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);
index 134c578db64428c65df515c830c9097576cb991b..f08463af6c9e93102e7dc72bb0bdab9254497e1a 100644 (file)
@@ -5,14 +5,19 @@
 #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,
@@ -20,19 +25,23 @@ enum {
    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';
@@ -43,44 +52,54 @@ int getinput(char *buffer) {
            } 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:
@@ -98,15 +117,11 @@ int getinput(char *buffer) {
            }
        }
    }
-
    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;
 }
 
index 6e39fb1dca5a080a2645ecaf798ef2945ff7d7b9..dee8d7a21fc13541b5cdf2232665310a857b0c15 100644 (file)
@@ -1 +1 @@
-int getinput(char *buffer);
+char *input(void);
diff --git a/src/job.c b/src/job.c
new file mode 100644 (file)
index 0000000..4125d2d
--- /dev/null
+++ b/src/job.c
@@ -0,0 +1,48 @@
+#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");
+   }
+}
diff --git a/src/job.h b/src/job.h
new file mode 100644 (file)
index 0000000..4034cfc
--- /dev/null
+++ b/src/job.h
@@ -0,0 +1,18 @@
+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);
similarity index 57%
rename from src/cmd.c
rename to src/lex.c
index 74e536fbbb92f80a746bdff115331dc6a6843ecb..fe20f40a5b51d3ca6a7723a4cf6e2ed5da5c1979 100644 (file)
--- a/src/cmd.c
+++ b/src/lex.c
@@ -1,42 +1,41 @@
 #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) {
@@ -51,13 +50,11 @@ struct cmd *getcmds(char *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;
@@ -71,5 +68,5 @@ struct cmd *getcmds(char *b) {
    }
    *c = (struct cmd){0};
 
-   return result;
+   return cmds;
 }
similarity index 80%
rename from src/cmd.h
rename to src/lex.h
index 82850f31b56cda2381fb6d4787ba220180c83870..d5b862d95da5320326e6f9cd032b310cdb164798 100644 (file)
--- a/src/cmd.h
+++ b/src/lex.h
@@ -12,4 +12,4 @@ struct cmd {
    int pipe[2];
 };
 
-struct cmd *getcmds(char *b);
+struct cmd *lex(char *b);
index 5906e06dc9c6719675405c0f009ce7056d339706..2b00fe5bf6fbe3756ac05cb6c7506d31825c36b8 100644 (file)
@@ -1,20 +1,22 @@
 #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;
@@ -32,42 +34,37 @@ int closepipe(struct cmd *cmd) {
 }
 
 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);
@@ -78,30 +75,42 @@ int main(void) {
                    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);
    }
diff --git a/src/pg.c b/src/pg.c
deleted file mode 100644 (file)
index ec3cedf..0000000
--- a/src/pg.c
+++ /dev/null
@@ -1,74 +0,0 @@
-#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");
-   }
-}
diff --git a/src/pg.h b/src/pg.h
deleted file mode 100644 (file)
index 522f958..0000000
--- a/src/pg.h
+++ /dev/null
@@ -1,25 +0,0 @@
-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);
diff --git a/src/stack.c b/src/stack.c
new file mode 100644 (file)
index 0000000..4d21bd7
--- /dev/null
@@ -0,0 +1,26 @@
+#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);
+}
diff --git a/src/stack.h b/src/stack.h
new file mode 100644 (file)
index 0000000..d33cbe5
--- /dev/null
@@ -0,0 +1,17 @@
+#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);
index a2bbe1c87ea91046f5042c6ce27feae04c4c9682..52ec18436bc89da22368ec2cd2bddcfe1c4e87dd 100644 (file)
@@ -6,10 +6,21 @@
 #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)
@@ -17,8 +28,7 @@ void initterm(void) {
    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");
 }
@@ -28,53 +38,81 @@ void deinitterm(void) {
        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;
 }
index 0603b8e04c4d829ca1aac0226a8d57fe87cfcc10..3bc68064abe882222595bbc28c8e9d0de69e4715 100644 (file)
@@ -1,7 +1,6 @@
-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);