]> Trent Huber's Code - thus.git/commitdiff
Pipes added; extremely messy code...
authorTrent Huber <trentmhuber@gmail.com>
Wed, 21 May 2025 02:15:23 +0000 (22:15 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Wed, 21 May 2025 02:15:23 +0000 (22:15 -0400)
main.c

diff --git a/main.c b/main.c
index 101ececcfda281ae7b2c156e615409de025b0702..0c3b80d2bacd76a2d9220b53396b224671c0a66b 100644 (file)
--- a/main.c
+++ b/main.c
@@ -274,9 +274,9 @@ struct pg {
 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))
@@ -330,10 +330,13 @@ int waitfg(pid_t cpgid) {
 
    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)) {
@@ -346,8 +349,8 @@ int waitfg(pid_t cpgid) {
        };
        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);
 
@@ -357,96 +360,142 @@ int waitfg(pid_t cpgid) {
    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);
@@ -455,8 +504,9 @@ void waitbg(int sig) {
 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)
@@ -468,75 +518,101 @@ int main(void) {
    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;
 }