]> Trent Huber's Code - thus.git/commitdiff
Command line options, scripting, comments
authorTrent Huber <trentmhuber@gmail.com>
Tue, 8 Jul 2025 19:07:05 +0000 (15:07 -0400)
committerTrent Huber <trentmhuber@gmail.com>
Tue, 8 Jul 2025 19:07:05 +0000 (15:07 -0400)
src/input.c
src/input.h
src/lex.c
src/main.c

index 9d3115f2d08c290c3fc843871b4bcddf3fa8bcc0..65fcdc4eeb1b3d389b215fa706ca7baa75b17fa3 100644 (file)
@@ -1,3 +1,4 @@
+#include <err.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -26,7 +27,24 @@ enum character {
    BACKSPACE = '\177',
 };
 
-static char buffer[BUFLEN + 2]; // Terminating ";"
+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;
@@ -34,26 +52,29 @@ char *input(void) {
 
    signal(SIGCHLD, waitbg); // TODO: Use sigaction for portability
 
-reset:
    end = cursor = buffer;
    *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) continue;
-               memmove(cursor + 1, cursor, end - cursor);
-               *cursor++ = c;
-               *++end = '\0';
-
-               putchar(c);
-               fputs(cursor, stdout);
-               for (i = end - cursor; i > 0; --i) putchar('\b');
-           } else switch (c) {
+           switch (c) {
+           default:
+               if (c >= ' ' && c <= '~') {
+                   if (end - buffer == BUFLEN) continue;
+                   memmove(cursor + 1, cursor, end - cursor);
+                   *cursor++ = c;
+                   *++end = '\0';
+
+                   putchar(c);
+                   fputs(cursor, stdout);
+                   for (i = end - cursor; i > 0; --i) putchar('\b');
+               }
+               break;
            case CTRLC:
                puts("^C");
-               goto reset;
+               *buffer = '\0';
+               return buffer;
            case CTRLD:
                puts("^D");
                signal(SIGCHLD, SIG_DFL);
index 652daa8a340bb4e10ed0814e36c46c06c6fad970..9f38eb7ccd0caf04501739c69344ad23e2673130 100644 (file)
@@ -1,3 +1,6 @@
 #define BUFLEN 1000
 
+extern char buffer[BUFLEN + 2];
+
+char *strinput(char **strp);
 char *input(void);
index d032d5bdc8695ea3920c5d9b4ce3cde1100eb0a6..29410dfcb8e98dd6b33d22f77e08ec7070c0333e 100644 (file)
--- a/src/lex.c
+++ b/src/lex.c
@@ -125,12 +125,14 @@ struct cmd *lex(char *b) {
        b += offset - 1;
        *(end + offset) = '\0';
        break;
+   case '#':
+       *(b + 1) = '\0';
    case '&':
    case '|':
    case ';':
        if (name && *c->args == name) c->args = NULL;
        if (c->args) {
-           if ((c->term = *b) != ';' && *b == *(b + 1)) {
+           if ((c->term = *b) == *(b + 1) && *b == '&' || *b == '|') {
                ++c->term;
                *b++ = '\0';
            }
@@ -160,7 +162,7 @@ struct cmd *lex(char *b) {
        }
    }
 
-   switch ((c - 1)->term) {
+   if (c-- != cmds) switch (c->term) {
    case AND:
    case PIPE:
    case OR:
index c30e40ffc4e00c21b59ea6646a96f19ea584aa28..4e1f58ea093e1808229f2df3a5137ef26434a940 100644 (file)
@@ -5,6 +5,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
 #include <termios.h>
 #include <unistd.h>
 
@@ -74,95 +76,159 @@ static int redirectfiles(struct redirect *r) {
    return 1;
 }
 
-int main(void) {
-   struct cmd *cmd, *prev;
-   int ispipe, ispipestart, ispipeend, status;
+void run(struct cmd *cmd) {
+   struct cmd *prev;
+   int ispipe, ispipestart, ispipeend;
+   static int status;
    pid_t cpid, jobid;
    struct job job;
 
-   initterm();
-   readhist();
-
-   for (prev = &empty; (cmd = lex(input())); prev = &empty) {
-       for (; 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);
-               }
+   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) {
-                   closepipe(prev);
-                   jobid = ((struct job *)(ispipeend ? pull : peek)(&jobs))->id;
+                   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);
                }
-           } 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 (!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 (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 (!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;
+       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);
    }
+   waitbg(0);
+}
 
-   writehist();
+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);
+
+   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();
+   }
    deinitterm();
 
-   return 0;
+   return EXIT_SUCCESS;
 }