]> Trent Huber's Code - thus.git/commitdiff
Handle multiline commands, factoring, and bug fixes
authorTrent Huber <trentmhuber@gmail.com>
Sat, 8 Nov 2025 07:50:54 +0000 (02:50 -0500)
committerTrent Huber <trentmhuber@gmail.com>
Sat, 8 Nov 2025 07:50:54 +0000 (02:50 -0500)
src/build.c
src/builtins/fg.c
src/builtins/fg.h
src/history.c
src/history.h
src/input.c
src/input.h
src/run.c
src/signals.c [new file with mode: 0644]
src/signals.h [new file with mode: 0644]
src/utils.c

index 84ad1bdf79676d80c9a6226bffa2f3fc343850d9..1b0c587e60cf0c572c2969679c4cc96c0ed8117f 100644 (file)
@@ -1,7 +1,7 @@
 #include "../external/cbs/cbs.c"
 
-#define SRC1 "context", "history"
-#define SRC2 "input", "main", "options", "parse", "run", "utils"
+#define SRC1 "context", "history", "input", "signals"
+#define SRC2 "main", "options", "parse", "run", "utils"
 
 int main(void) {
    char **src;
index c40db18babe3ee4722c93d12911bbcb58df5a108..dd65f23d77a238585f02fa60ad3d301508d6db79 100644 (file)
 #include "bg.h"
 #include "builtin.h"
 #include "context.h"
-#include "input.h"
 #include "options.h"
+#include "signals.h"
 #include "utils.h"
 
-int sigquit, sigint;
 static struct {
    pid_t id;
    int status, done;
 } fgjob;
-static sigset_t shellsigmask;
 static struct sigaction fgaction;
-struct sigaction defaultaction;
 static pid_t pgid;
-sigset_t childsigmask;
 struct termios canonical;
 static struct termios raw;
 
-static void sigquithandler(int sig) {
-   (void)sig;
-
-   sigquit = 1;
-}
-
-static void siginthandler(int sig) {
-   (void)sig;
-
-   sigint = 1;
-}
-
 static void sigchldfghandler(int sig) {
    int e, s;
    pid_t id;
@@ -58,11 +42,6 @@ static void sigchldfghandler(int sig) {
    errno = e;
 }
 
-void setsig(int sig, struct sigaction *act) {
-   if (sigaction(sig, act, NULL) == -1)
-       fatal("Unable to install %s handler", strsignal(sig));
-}
-
 static int setconfig(struct termios *mode) {
    if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
        note("Unable to configure TTY");
@@ -72,35 +51,15 @@ static int setconfig(struct termios *mode) {
 }
 
 void initfg(void) {
-   struct sigaction action;
    pid_t pid;
 
-   sigemptyset(&shellsigmask);
-   sigaddset(&shellsigmask, SIGTSTP);
-   sigaddset(&shellsigmask, SIGTTIN);
-   sigaddset(&shellsigmask, SIGTTOU);
-
-   action = (struct sigaction){.sa_handler = sigquithandler};
-   setsig(SIGHUP, &action);
-   setsig(SIGQUIT, &action);
-   setsig(SIGTERM, &action);
-
-   action = (struct sigaction){.sa_handler = siginthandler};
-   setsig(SIGINT, &action);
-
    fgaction = (struct sigaction){.sa_handler = sigchldfghandler};
 
-   defaultaction = (struct sigaction){.sa_handler = SIG_DFL};
-   setsig(SIGTSTP, &defaultaction);
-   setsig(SIGTTOU, &defaultaction);
-   setsig(SIGTTIN, &defaultaction);
-
    pid = getpid();
    pgid = getpgrp();
    if (login && pid != pgid && setpgid(0, pgid = pid) == -1) exit(errno);
 
-   if (sigprocmask(SIG_BLOCK, &shellsigmask, &childsigmask) == -1
-       || tcsetpgrp(STDIN_FILENO, pgid) == -1)
+   if (tcsetpgrp(STDIN_FILENO, pgid) == -1)
        exit(errno);
 
    if (tcgetattr(STDIN_FILENO, &canonical) == -1) exit(errno);
index 086646203f358fdcffa0e8f0371d907d2f951450..f1122f96ffcafd95876420ace0f50343e3410c9e 100644 (file)
@@ -1,9 +1,5 @@
-extern int sigquit, sigint;
-extern struct sigaction defaultaction;
-extern sigset_t childsigmask;
 extern struct termios canonical;
 
-void setsig(int sig, struct sigaction *act);
 void initfg(void);
 int runfg(pid_t id);
 void deinitfg(void);
index cac16e8c9402b4505ae88d18a3a6ba00ffe97a06..717cfbf200a755c0238e5d612b79fb9be86eb614 100644 (file)
@@ -55,12 +55,13 @@ int gethistory(int back, char *buffer) {
    return 1;
 }
 
-void sethistory(char *buffer) {
-   strcpy(history.entries[history.t], buffer);
-   history.c = INC(t);
-   if (history.t == history.b) INC(b);
-   if (history.t == history.s) INC(s);
-   *history.entries[history.t] = '\0';
+void addhistory(char *buffer) {
+   if (buffer) {
+       strcpy(history.entries[history.t], buffer);
+       if (INC(t) == history.b) INC(b);
+       if (history.t == history.s) INC(s);
+   }
+   *history.entries[history.c = history.t] = '\0';
 }
 
 static void writehistory(FILE *file) {
index 49dce14aaf8fb9b102f19367c26f51f800b3c589..961aaf8b979912bf96da031b4f42fb2511b7b105 100644 (file)
@@ -1,4 +1,4 @@
 void inithistory(void);
 int gethistory(int back, char *buffer);
-void sethistory(char *buffer);
+void addhistory(char *buffer);
 void deinithistory(void);
index b9049bc270ddcb9b769c53f002276c7cc7aa661a..a0940d3a4bed6048bef962c74748f9aa0869e9f2 100644 (file)
@@ -1,16 +1,20 @@
 #include <fcntl.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "context.h"
-#include "fg.h"
 #include "history.h"
+#include "signals.h"
 #include "utils.h"
 
+#define OFFSET(x) ((promptlen + (x) - start) % window.ws_col)
+
 enum {
    CTRLD = '\004',
    CLEAR = '\014',
@@ -28,8 +32,16 @@ enum {
    DEL = '\177',
 };
 
+static struct winsize window;
+static char *start, *cursor, *end;
+static size_t promptlen;
+
+void getcolumns(void) {
+   if (ioctl(STDIN_FILENO, TIOCGWINSZ, &window) == -1 && window.ws_col == 0)
+       window.ws_col = 80;
+}
+
 int stringinput(struct context *c) {
-   char *end;
    size_t l;
 
    if (!c->string[0]) {
@@ -88,127 +100,175 @@ int scriptinput(struct context *c) {
    return c->input(c);
 }
 
+static void moveright(void) {
+   putchar(*cursor);
+   if (OFFSET(cursor++) == window.ws_col - 1) putchar('\n');
+}
+
 static void prompt(void) {
-   char *p;
+   char *p, *oldstart, *oldcursor;
 
-   if (!(p = getenv("PROMPT")) && setenv("PROMPT", p = ">", 1) == -1)
+   if (!(p = getenv("PROMPT")) && setenv("PROMPT", p = "> ", 1) == -1)
        note("Unable to update $PROMPT$ environment variable");
-   printf("%s ", p);
+
+   oldstart = start;
+   oldcursor = cursor;
+
+   promptlen = 0;
+   cursor = start = p;
+   while (*cursor) moveright();
+   promptlen = cursor - start;
+
+   cursor = oldcursor;
+   start = oldstart;
+}
+
+static void moveleft(void) {
+   if (OFFSET(cursor--)) putchar('\b');
+   else printf("\033[A\033[%dC", window.ws_col - 1);
+}
+
+static void newline(void) {
+   size_t i;
+
+   for (i = (promptlen + end - cursor) / window.ws_col; i > 0; --i) putchar('\n');
+   if (OFFSET(cursor) > OFFSET(end)) putchar('\n');
+   if (OFFSET(end)) putchar('\n');
+   putchar('\r');
 }
 
 int userinput(struct context *c) {
-   char *start, *cursor, *end;
-   int current, i;
-   size_t oldlen, newlen;
+   int current;
+   char *oldcursor, *oldend;
 
    clear(c);
    end = cursor = start = c->buffer;
    while (start == end) {
        prompt();
-       while ((current = getchar()) != '\n') switch (current) {
-       default:
-           if (current >= ' ' && current <= '~') {
-               if (end - start == MAXCHARS) break;
-               memmove(cursor + 1, cursor, end - cursor);
-               *cursor++ = current;
-               *++end = '\0';
-
-               putchar(current);
-               fputs(cursor, stdout);
-               for (i = end - cursor; i > 0; --i) putchar('\b');
-           }
-           break;
-       case EOF:
-           if (sigquit) {
-           case CTRLD:
-               putchar('\n');
-               return 0;
-           }
-           if (sigint) {
-               sigint = 0;
-               putchar('\n');
+       while ((current = getchar()) != '\n') {
+           switch (current) {
+           default:
+               if (current >= ' ' && current <= '~') {
+                   if (end - start == MAXCHARS) break;
+                   memmove(cursor + 1, cursor, end - cursor);
+                   *cursor = current;
+                   *++end = '\0';
+
+                   oldcursor = cursor + 1;
+                   while (cursor != end) moveright();
+                   while (cursor != oldcursor) moveleft();
+               }
+               break;
+           case EOF:
+               if (sigquit) {
+               case CTRLD:
+                   newline();
+                   return 0;
+               }
+               if (sigint) {
+                   sigint = 0;
+
+                   newline();
+                   prompt();
+
+                   end = cursor = start;
+                   *start = '\0';
+
+                   addhistory(NULL);
+               }
+               if (sigwinch) {
+                   sigwinch = 0;
+
+                   getcolumns();
+               }
+               break;
+           case CLEAR:
+               fputs("\033[H\033[J", stdout);
                prompt();
-           }
-           break;
-       case CLEAR:
-           fputs("\033[H\033[J", stdout);
-           prompt();
-           fputs(c->buffer, stdout);
-           break;
-
-           /* This is a very minimal way to handle arrow keys. All modifiers except for
-            * the ALT key are processed but ignored.
-            *
-            * See "Terminal Input Sequences" reference in `README.md'. */
-       case ESCAPE:
-           if ((current = getchar()) == '[') {
-               while ((current = getchar()) >= '0' && current <= '9');
-               if (current == ';') {
-                   if ((current = getchar()) == ALT) switch ((current = getchar())) {
+               oldcursor = cursor;
+               cursor = start;
+               while (cursor != end) moveright();
+               while (cursor != oldcursor) moveleft();
+               break;
+
+               /* This is a very minimal way to handle arrow keys. All modifiers except for
+                * the ALT key are processed but ignored.
+                *
+                * See "Terminal Input Sequences" reference in `README.md'. */
+           case ESCAPE:
+               if ((current = getchar()) == '[') {
+                   while ((current = getchar()) >= '0' && current <= '9');
+                   if (current == ';') {
+                       if ((current = getchar()) == ALT) switch ((current = getchar())) {
+                       case LEFT:
+                           current = BACKWARD;
+                           break;
+                       case RIGHT:
+                           current = FORWARD;
+                           break;
+                       } else if ((current = getchar()) >= '0' && current <= '6')
+                           current = getchar();
+                   }
+                   switch (current) {
+                   case UP:
+                   case DOWN:
+                       oldend = end;
+
+                       oldcursor = cursor;
+                       if (!gethistory(current == UP, c->buffer)) break;
+                       end = cursor = start + strlen(start);
+                       while (cursor < oldend) *cursor++ = ' ';
+                       cursor = oldcursor;
+
+                       while (cursor != start) moveleft();
+                       while (cursor < end || cursor < oldend) moveright();
+                       while (cursor != end) moveleft();
+
+                       *end = '\0';
+
+                       break;
                    case LEFT:
-                       current = BACKWARD;
+                       if (cursor > start) moveleft();
                        break;
                    case RIGHT:
-                       current = FORWARD;
+                       if (cursor < end) moveright();
                        break;
-                   } else if ((current = getchar()) >= '0' && current <= '6')
-                       current = getchar();
+                   }
                }
                switch (current) {
-               case UP:
-               case DOWN:
-                   oldlen = strlen(c->buffer);
-                   if (!gethistory(current == UP, c->buffer)) break;
-                   newlen = strlen(c->buffer);
-                   end = cursor = start + newlen;
-
-                   putchar('\r');
-                   prompt();
-                   fputs(c->buffer, stdout);
-                   for (i = oldlen - newlen; i > 0; --i) putchar(' ');
-                   for (i = oldlen - newlen; i > 0; --i) putchar('\b');
-
+               case FORWARD:
+                   while (cursor != end && *cursor != ' ') moveright();
+                   while (cursor != end && *cursor == ' ') moveright();
                    break;
-               case LEFT:
-                   if (cursor > start) putchar((--cursor, '\b'));
-                   break;
-               case RIGHT:
-                   if (cursor < end) putchar(*cursor++);
+               case BACKWARD:
+                   while (cursor != start && *(cursor - 1) == ' ') moveleft();
+                   while (cursor != start && *(cursor - 1) != ' ') moveleft();
                    break;
                }
-           }
-           switch (current) {
-           case FORWARD:
-               while (cursor != end && *cursor != ' ') putchar(*cursor++);
-               while (cursor != end && *cursor == ' ') putchar(*cursor++);
                break;
-           case BACKWARD:
-               while (cursor != start && *(cursor - 1) == ' ') putchar((--cursor, '\b'));
-               while (cursor != start && *(cursor - 1) != ' ') putchar((--cursor, '\b'));
-               break;
-           }
-           break;
+           case DEL:
+               if (cursor == start) break;
+               memmove(oldcursor = cursor - 1, cursor, end - cursor);
+               *(end - 1) = ' ';
 
-       case DEL:
-           if (cursor == start) break;
-           memmove(cursor - 1, cursor, end - cursor);
-           --cursor;
-           *--end = '\0';
+               moveleft();
+               while (cursor != end) moveright();
+               while (cursor != oldcursor) moveleft();
 
-           putchar('\b');
-           fputs(cursor, stdout);
-           putchar(' ');
-           for (i = end - cursor + 1; i > 0; --i) putchar('\b');
+               *--end = '\0';
 
-           break;
+               break;
+           }
        }
-       putchar('\n');
+       newline();
    }
 
    while (*start == ' ') ++start;
    if (start == end) return quit(c);
+   while (*(end - 1) == ' ') --end;
+   *end = '\0';
 
-   sethistory(c->buffer);
+   addhistory(start);
 
    *end++ = ';';
    *end = '\0';
index ba5568807f28eb24bce34c46ad2ee5a9d9344868..663d6793912f4a575a5d8d8c95d4d01c433c7bbc 100644 (file)
@@ -1,3 +1,4 @@
+void getcolumns(void);
 int stringinput(struct context *c);
 int scriptinput(struct context *c);
 int userinput(struct context *c);
index 8a1aef1ecf3185c50b51ecb7bf448719993e5e51..5ab813001a164e51b55634ffcd132746e2abe41f 100644 (file)
--- a/src/run.c
+++ b/src/run.c
@@ -12,6 +12,7 @@
 #include "context.h"
 #include "fg.h"
 #include "parse.h"
+#include "signals.h"
 #include "utils.h"
 #include "which.h"
 
diff --git a/src/signals.c b/src/signals.c
new file mode 100644 (file)
index 0000000..2d22a13
--- /dev/null
@@ -0,0 +1,60 @@
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+
+#include "utils.h"
+
+int sigquit, sigint, sigwinch;
+sigset_t shellsigmask, childsigmask;
+struct sigaction defaultaction;
+
+void setsig(int sig, struct sigaction *act) {
+   if (sigaction(sig, act, NULL) == -1)
+       fatal("Unable to install %s handler", strsignal(sig));
+}
+
+static void sigquithandler(int sig) {
+   (void)sig;
+
+   sigquit = 1;
+}
+
+static void siginthandler(int sig) {
+   (void)sig;
+
+   sigint = 1;
+}
+
+static void sigwinchhandler(int sig) {
+   (void)sig;
+
+   sigwinch = 1;
+}
+
+void initsignals(void) {
+   struct sigaction action;
+
+   sigemptyset(&shellsigmask);
+   sigaddset(&shellsigmask, SIGTSTP);
+   sigaddset(&shellsigmask, SIGTTIN);
+   sigaddset(&shellsigmask, SIGTTOU);
+
+   action = (struct sigaction){.sa_handler = sigquithandler};
+   setsig(SIGHUP, &action);
+   setsig(SIGQUIT, &action);
+   setsig(SIGTERM, &action);
+
+   action = (struct sigaction){.sa_handler = siginthandler};
+   setsig(SIGINT, &action);
+
+   action = (struct sigaction){.sa_handler = sigwinchhandler};
+   setsig(SIGWINCH, &action);
+
+   defaultaction = (struct sigaction){.sa_handler = SIG_DFL};
+   setsig(SIGTSTP, &defaultaction);
+   setsig(SIGTTOU, &defaultaction);
+   setsig(SIGTTIN, &defaultaction);
+
+   if (sigprocmask(SIG_BLOCK, &shellsigmask, &childsigmask) == -1) exit(errno);
+}
diff --git a/src/signals.h b/src/signals.h
new file mode 100644 (file)
index 0000000..a23a08a
--- /dev/null
@@ -0,0 +1,6 @@
+extern int sigquit, sigint, sigwinch;
+extern sigset_t shellsigmask, childsigmask;
+extern struct sigaction defaultaction;
+
+void setsig(int sig, struct sigaction *act);
+void initsignals(void);
index 154bd32377589aba71fcd301acd20cea42dbf933..3d01af21104a92c8cdf10b9ff44cb308dc5522a3 100644 (file)
@@ -1,4 +1,5 @@
 #include <limits.h>
+#include <signal.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -8,8 +9,11 @@
 #include <unistd.h>
 
 #include "bg.h"
+#include "context.h"
 #include "fg.h"
 #include "history.h"
+#include "input.h"
+#include "signals.h"
 
 int argcount, status;
 char **arglist, *home;
@@ -80,6 +84,9 @@ void init(void) {
                          "/usr/bin/:/usr/sbin/:/bin/:/sbin/", 1) == -1)
        note("Unable to initialize $PATH$");
 
+   getcolumns();
+
+   initsignals();
    initfg();
    initbg();
    inithistory();