From: Trent Huber Date: Sat, 8 Nov 2025 07:50:54 +0000 (-0500) Subject: Handle multiline commands, factoring, and bug fixes X-Git-Url: https://trenthuber.com/code?a=commitdiff_plain;h=082b3b5650877667901a56ae967c6ecb90506b80;p=thus.git Handle multiline commands, factoring, and bug fixes --- diff --git a/src/build.c b/src/build.c index 84ad1bd..1b0c587 100644 --- a/src/build.c +++ b/src/build.c @@ -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; diff --git a/src/builtins/fg.c b/src/builtins/fg.c index c40db18..dd65f23 100644 --- a/src/builtins/fg.c +++ b/src/builtins/fg.c @@ -11,35 +11,19 @@ #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); diff --git a/src/builtins/fg.h b/src/builtins/fg.h index 0866462..f1122f9 100644 --- a/src/builtins/fg.h +++ b/src/builtins/fg.h @@ -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); diff --git a/src/history.c b/src/history.c index cac16e8..717cfbf 100644 --- a/src/history.c +++ b/src/history.c @@ -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) { diff --git a/src/history.h b/src/history.h index 49dce14..961aaf8 100644 --- a/src/history.h +++ b/src/history.h @@ -1,4 +1,4 @@ void inithistory(void); int gethistory(int back, char *buffer); -void sethistory(char *buffer); +void addhistory(char *buffer); void deinithistory(void); diff --git a/src/input.c b/src/input.c index b9049bc..a0940d3 100644 --- a/src/input.c +++ b/src/input.c @@ -1,16 +1,20 @@ #include +#include #include #include #include +#include #include #include #include #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'; diff --git a/src/input.h b/src/input.h index ba55688..663d679 100644 --- a/src/input.h +++ b/src/input.h @@ -1,3 +1,4 @@ +void getcolumns(void); int stringinput(struct context *c); int scriptinput(struct context *c); int userinput(struct context *c); diff --git a/src/run.c b/src/run.c index 8a1aef1..5ab8130 100644 --- 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 index 0000000..2d22a13 --- /dev/null +++ b/src/signals.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#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 index 0000000..a23a08a --- /dev/null +++ b/src/signals.h @@ -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); diff --git a/src/utils.c b/src/utils.c index 154bd32..3d01af2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -8,8 +9,11 @@ #include #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();