]> Trent Huber's Code - thus.git/commitdiff
Login and interactive scripts
authorTrent Huber <trentmhuber@gmail.com>
Sun, 13 Jul 2025 23:19:41 +0000 (19:19 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Sun, 13 Jul 2025 23:19:41 +0000 (19:19 -0400)
build.c
src/input.c
src/input.h
src/main.c
src/options.c [new file with mode: 0644]
src/options.h [new file with mode: 0644]
src/run.c [new file with mode: 0644]
src/run.h [new file with mode: 0644]

diff --git a/build.c b/build.c
index de2b9b1fb31d14ee093e1e31da0c81687d549981..aaecd9d8d9257ce0ce8310a42adeeb687466e5b1 100644 (file)
--- a/build.c
+++ b/build.c
@@ -7,7 +7,9 @@
    SRCDIR "input", \
    SRCDIR "job", \
    SRCDIR "lex", \
+   SRCDIR "options", \
    SRCDIR "main", \
+   SRCDIR "run", \
    SRCDIR "stack", \
    SRCDIR "term", \
    SRCDIR "utils"
index 65fcdc4eeb1b3d389b215fa706ca7baa75b17fa3..5c56f9a25466c6e027f178490dcc18b633028c8f 100644 (file)
@@ -9,6 +9,7 @@
 #include "input.h"
 #include "job.h"
 #include "stack.h"
+#include "options.h"
 
 #define PROMPT "% "
 
@@ -29,23 +30,6 @@ enum character {
 
 char buffer[BUFLEN + 2]; // Terminating ";"
 
-char *strinput(char **strp) {
-   char *p;
-   size_t l;
-
-   if (!**strp) return NULL;
-   p = *strp;
-   while (**strp && **strp != '\n') ++*strp;
-   l = (*strp)++ - p;
-   if (l > BUFLEN)
-       errx(EXIT_FAILURE, "Line is too long; exceeds %d characters", BUFLEN);
-   strncpy(buffer, p, l);
-   *(buffer + l++) = ';';
-   *(buffer + l) = '\0';
-
-   return buffer;
-}
-
 char *input(void) {
    char *cursor, *end;
    int c, i;
index 9f38eb7ccd0caf04501739c69344ad23e2673130..1ca304b80bd104df4ac814b5ed27fd087ce6f068 100644 (file)
@@ -2,5 +2,4 @@
 
 extern char buffer[BUFLEN + 2];
 
-char *strinput(char **strp);
 char *input(void);
index 4e1f58ea093e1808229f2df3a5137ef26434a940..9c05d5c6d4b975c0d01b92921cbe10a75f6cd6fa 100644 (file)
-#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 <termios.h>
-#include <unistd.h>
+#include <stdio.h> // XXX
 
-#include "builtins.h"
-#include "stack.h"
-#include "history.h"
-#include "input.h"
-#include "lex.h"
 #include "job.h"
+#include "options.h"
+#include "run.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;
-   }
-   return result;
-}
-
-static int redirectfiles(struct redirect *r) {
-   int oflag, fd;
-
-   for (; r->mode; ++r) {
-       if (r->oldname) {
-           switch (r->mode) {
-           case READ:
-               oflag = O_RDONLY;
-               break;
-           case WRITE:
-               oflag = O_WRONLY | O_CREAT | O_TRUNC;
-               break;
-           case READWRITE:
-               oflag = O_RDWR | O_CREAT | O_APPEND;
-               break;
-           case APPEND:
-               oflag = O_WRONLY | O_CREAT | O_APPEND;
-               break;
-           default:;
-           }
-           if ((fd = open(r->oldname, oflag, 0644)) == -1) {
-               warn("Unable to open `%s'", r->oldname);
-               return 0;
-           }
-           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;
-           }
-       }
-   }
-   return 1;
-}
-
-void run(struct cmd *cmd) {
-   struct cmd *prev;
-   int ispipe, ispipestart, ispipeend;
-   static int status;
-   pid_t cpid, jobid;
-   struct job job;
-
-   for (prev = &empty; cmd->args; prev = cmd++) {
-       ispipe = cmd->term == PIPE || prev->term == PIPE;
-       ispipestart = ispipe && prev->term != PIPE;
-       ispipeend = ispipe && cmd->term != PIPE;
-
-       if (ispipe) {
-           if (!ispipeend && pipe(cmd->pipe) == -1) {
-               warn("Unable to create pipe");
-               if (!ispipestart) closepipe(prev);
-               break;
-           }
-           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 (!closepipe(cmd)) exit(EXIT_FAILURE);
-               }
-
-               if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
-               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);
-           }
-           if (!ispipestart) {
-               closepipe(prev);
-               jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
-           }
-       } else {
-           if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break;
-           if ((jobid = cpid = fork()) == -1) {
-               warn("Unable to create child process");
-               break;
-           } else if (cpid == 0) {
-               if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
-               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);
-           }
-       }
-       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);
-           }
-           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");
-               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 usage(void) {
-   puts("TODO: PRINT USAGE MESSAGE");
-}
-
-char *getoptions(int argc, char **argv, int *l) {
-   int opt, help, fd;
-   struct cmd *cmd;
-   char *r;
-   struct stat fdstat;
-
-   help = 0;
-   r = NULL;
-   while ((opt = getopt(argc, argv, ":c:h")) != -1) switch (opt) {
-   case 'c':
-       *l = strlen(optarg) + 2;
-       if (!(r = malloc(*l))) err(EXIT_FAILURE, "Memory allocation");
-       strcpy(r, optarg);
-       *(r + *l - 1) = ';';
-       *(r + *l) = '\0';
-       break;
-   case 'h':
-       help = 1;
-       break;
-   case ':':
-       errx(EXIT_FAILURE, "Expected argument following `-%c'", optopt);
-   case '?':
-   default:
-       errx(EXIT_FAILURE, "Unknown command line option `-%c'", optopt);
-   }
-   argc -= optind;
-   argv += optind;
-
-   if (help) {
-       usage();
-       return EXIT_SUCCESS;
-   }
-
-   if (r) *argv = NULL;
-   else if (*argv) {
-       if ((fd = open(*argv, O_RDONLY)) == -1)
-           err(EXIT_FAILURE, "Unable to open `%s'", *argv);
-       if (stat(*argv, &fdstat) == -1)
-           err(EXIT_FAILURE, "Unable to stat `%s'", *argv);
-       if ((r = mmap(NULL, *l = fdstat.st_size, PROT_READ, MAP_PRIVATE, fd, 0))
-           == MAP_FAILED)
-           err(EXIT_FAILURE, "Unable to memory map `%s'", *argv);
-       if (close(fd) == -1) err(EXIT_FAILURE, "Unable to close `%s'", *argv);
-   }
-
-   return r;
-}
 
 int main(int argc, char **argv) {
    struct cmd *cmd;
-   char *o, *p;
-   int l;
 
-   p = o = getoptions(argc, argv, &l);
+   // TODO: Have `cd' builtin affect $PWD$ env var
+
+   options(&argc, &argv);
 
-   initterm();
-   if (p) {
-       while ((cmd = lex(strinput(&p)))) run(cmd);
-       if (*argv) munmap(o, l); else free(o);
-   } else {
-       readhist();
-       while ((cmd = lex(input()))) run(cmd);
-       writehist();
+   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();
 
diff --git a/src/options.c b/src/options.c
new file mode 100644 (file)
index 0000000..3e07cc6
--- /dev/null
@@ -0,0 +1,41 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <err.h>
+#include <string.h>
+
+int login;
+char *string;
+
+void options(int *argcp, char ***argvp) {
+   int opt, l;
+   char *usage = "TODO: WRITE USAGE";
+
+   login = ***argvp == '-';
+
+   // -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';
+       break;
+   case 'h':
+       errx(EXIT_SUCCESS, "%s", usage);
+   case 'l':
+       login = 1;
+       break;
+   case ':':
+       errx(EXIT_FAILURE, "Expected argument following `-%c'\n%s", optopt, usage);
+   case '?':
+   default:
+       errx(EXIT_FAILURE, "Unknown command line option `-%c'\n%s", optopt, usage);
+   }
+   *argcp -= optind;
+   *argvp+= optind;
+}
diff --git a/src/options.h b/src/options.h
new file mode 100644 (file)
index 0000000..6bb8fa6
--- /dev/null
@@ -0,0 +1,4 @@
+extern int login;
+extern char *string;
+
+void options(int *argcp, char ***argvp);
diff --git a/src/run.c b/src/run.c
new file mode 100644 (file)
index 0000000..8ba727e
--- /dev/null
+++ b/src/run.c
@@ -0,0 +1,229 @@
+#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 "job.h"
+#include "lex.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;
+   }
+   return result;
+}
+
+static int redirectfiles(struct redirect *r) {
+   int oflag, fd;
+
+   for (; r->mode; ++r) {
+       if (r->oldname) {
+           switch (r->mode) {
+           case READ:
+               oflag = O_RDONLY;
+               break;
+           case WRITE:
+               oflag = O_WRONLY | O_CREAT | O_TRUNC;
+               break;
+           case READWRITE:
+               oflag = O_RDWR | O_CREAT | O_APPEND;
+               break;
+           case APPEND:
+               oflag = O_WRONLY | O_CREAT | O_APPEND;
+               break;
+           default:;
+           }
+           if ((fd = open(r->oldname, oflag, 0644)) == -1) {
+               warn("Unable to open `%s'", r->oldname);
+               return 0;
+           }
+           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;
+           }
+       }
+   }
+   return 1;
+}
+
+static void runcmd(struct cmd *cmd) {
+   struct cmd *prev;
+   int ispipe, ispipestart, ispipeend;
+   static int status;
+   pid_t cpid, jobid;
+   struct job job;
+
+   for (prev = &empty; cmd->args; prev = cmd++) {
+       ispipe = cmd->term == PIPE || prev->term == PIPE;
+       ispipestart = ispipe && prev->term != PIPE;
+       ispipeend = ispipe && cmd->term != PIPE;
+
+       if (ispipe) {
+           if (!ispipeend && pipe(cmd->pipe) == -1) {
+               warn("Unable to create pipe");
+               if (!ispipestart) closepipe(prev);
+               break;
+           }
+           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 (!closepipe(cmd)) exit(EXIT_FAILURE);
+               }
+
+               if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
+               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);
+           }
+           if (!ispipestart) {
+               closepipe(prev);
+               jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
+           }
+       } else {
+           if (cmd->rds->mode == END && isbuiltin(cmd->args, &status)) break;
+           if ((jobid = cpid = fork()) == -1) {
+               warn("Unable to create child process");
+               break;
+           } else if (cpid == 0) {
+               if (!redirectfiles(cmd->rds)) exit(EXIT_FAILURE);
+               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);
+           }
+       }
+       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);
+           }
+           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");
+               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;
+
+   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;
+
+   while ((cmd = lex(input()))) runcmd(cmd);
+}
+
+#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);
+}
diff --git a/src/run.h b/src/run.h
new file mode 100644 (file)
index 0000000..87eb890
--- /dev/null
+++ b/src/run.h
@@ -0,0 +1,6 @@
+char *prependhome(char *filename);
+
+void runstr(char *str);
+void runscript(char *filename);
+void runinteractive(void);
+void runhome(char *filename);