diff options
| author | stkhan <personal@slickd.xyz> | 2022-06-20 19:54:29 +0000 |
|---|---|---|
| committer | stkhan <personal@slickd.xyz> | 2022-06-20 19:54:29 +0000 |
| commit | 4c40a4dc245f7715f4891ed02d71475628e7a959 (patch) | |
| tree | fa52419490b5a0d89d746f6e3a0b0c1e675b7726 /scroll-0.1/scroll.c | |
| parent | 513b0e238b98f72a01d740a0bd99a3739918645c (diff) | |
new directory structure
Diffstat (limited to 'scroll-0.1/scroll.c')
| -rw-r--r-- | scroll-0.1/scroll.c | 594 |
1 files changed, 0 insertions, 594 deletions
diff --git a/scroll-0.1/scroll.c b/scroll-0.1/scroll.c deleted file mode 100644 index 8f66d54..0000000 --- a/scroll-0.1/scroll.c +++ /dev/null @@ -1,594 +0,0 @@ -/* - * Based on an example code from Roberto E. Vargas Caballero. - * - * See LICENSE file for copyright and license details. - */ - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/wait.h> -#include <sys/queue.h> -#include <sys/resource.h> - -#include <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <poll.h> -#include <pwd.h> -#include <signal.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <termios.h> -#include <unistd.h> - -#if defined(__linux) - #include <pty.h> -#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) - #include <util.h> -#elif defined(__FreeBSD__) || defined(__DragonFly__) - #include <libutil.h> -#endif - -#define LENGTH(X) (sizeof (X) / sizeof ((X)[0])) - -const char *argv0; - -TAILQ_HEAD(tailhead, line) head; - -struct line { - TAILQ_ENTRY(line) entries; - size_t size; - size_t len; - char *buf; -} *bottom; - -pid_t child; -int mfd; -struct termios dfl; -struct winsize ws; -static bool altscreen = false; /* is alternative screen active */ -static bool doredraw = false; /* redraw upon sigwinch */ - -struct rule { - const char *seq; - enum {SCROLL_UP, SCROLL_DOWN} event; - short lines; -}; - -#include "config.h" - -void -die(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - if (fmt[0] && fmt[strlen(fmt)-1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } - - exit(EXIT_FAILURE); -} - -void -sigwinch(int sig) -{ - assert(sig == SIGWINCH); - - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) - die("ioctl:"); - if (ioctl(mfd, TIOCSWINSZ, &ws) == -1) { - if (errno == EBADF) /* child already exited */ - return; - die("ioctl:"); - } - kill(-child, SIGWINCH); - doredraw = true; -} - -void -reset(void) -{ - if (tcsetattr(STDIN_FILENO, TCSANOW, &dfl) == -1) - die("tcsetattr:"); -} - -/* error avoiding remalloc */ -void * -earealloc(void *ptr, size_t size) -{ - void *mem; - - while ((mem = realloc(ptr, size)) == NULL) { - struct line *line = TAILQ_LAST(&head, tailhead); - - if (line == NULL) - die("realloc:"); - - TAILQ_REMOVE(&head, line, entries); - free(line->buf); - free(line); - } - - return mem; -} - -/* Count string length w/o ansi esc sequences. */ -size_t -strelen(const char *buf, size_t size) -{ - enum {CHAR, BREK, ESC} state = CHAR; - size_t len = 0; - - for (size_t i = 0; i < size; i++) { - char c = buf[i]; - - switch (state) { - case CHAR: - if (c == '\033') - state = BREK; - else - len++; - break; - case BREK: - if (c == '[') { - state = ESC; - } else { - state = CHAR; - len++; - } - break; - case ESC: - if (c >= 64 && c <= 126) - state = CHAR; - break; - } - } - - return len; -} - -/* detect alternative screen switching and clear screen */ -bool -skipesc(char c) -{ - static enum {CHAR, BREK, ESC} state = CHAR; - static char buf[BUFSIZ]; - static size_t i = 0; - - switch (state) { - case CHAR: - if (c == '\033') - state = BREK; - break; - case BREK: - if (c == '[') - state = ESC; - else - state = CHAR; - break; - case ESC: - buf[i++] = c; - if (i == sizeof buf) { - /* TODO: find a better way to handle this situation */ - state = CHAR; - i = 0; - } else if (c >= 64 && c <= 126) { - state = CHAR; - buf[i] = '\0'; - i = 0; - - /* esc seq. enable alternative screen */ - if (strcmp(buf, "?1049h") == 0 || - strcmp(buf, "?1047h") == 0 || - strcmp(buf, "?47h" ) == 0) - altscreen = true; - - /* esc seq. disable alternative screen */ - if (strcmp(buf, "?1049l") == 0 || - strcmp(buf, "?1047l") == 0 || - strcmp(buf, "?47l" ) == 0) - altscreen = false; - - /* don't save cursor move or clear screen */ - /* esc sequences to log */ - switch (c) { - case 'A': - case 'B': - case 'C': - case 'D': - case 'H': - case 'J': - case 'K': - case 'f': - return true; - } - } - break; - } - - return altscreen; -} - -void -getcursorposition(int *x, int *y) -{ - char input[BUFSIZ]; - ssize_t n; - - if (write(STDOUT_FILENO, "\033[6n", 4) == -1) - die("requesting cursor position"); - - do { - if ((n = read(STDIN_FILENO, input, sizeof(input)-1)) == -1) - die("reading cursor position"); - input[n] = '\0'; - } while (sscanf(input, "\033[%d;%dR", y, x) != 2); - - if (*x <= 0 || *y <= 0) - die("invalid cursor position: x=%d y=%d", *x, *y); -} - -void -addline(char *buf, size_t size) -{ - struct line *line = earealloc(NULL, sizeof *line); - - line->size = size; - line->len = strelen(buf, size); - line->buf = earealloc(NULL, size); - memcpy(line->buf, buf, size); - - TAILQ_INSERT_HEAD(&head, line, entries); -} - -void -redraw() -{ - int rows = 0, x, y; - - if (bottom == NULL) - return; - - getcursorposition(&x, &y); - - if (y < ws.ws_row-1) - y--; - - /* wind back bottom pointer by shown history */ - for (; bottom != NULL && TAILQ_NEXT(bottom, entries) != NULL && - rows < y - 1; rows++) - bottom = TAILQ_NEXT(bottom, entries); - - /* clear screen */ - dprintf(STDOUT_FILENO, "\033[2J"); - /* set cursor position to upper left corner */ - write(STDOUT_FILENO, "\033[0;0H", 6); - - /* remove newline of first line as we are at 0,0 already */ - if (bottom->size > 0 && bottom->buf[0] == '\n') - write(STDOUT_FILENO, bottom->buf + 1, bottom->size - 1); - else - write(STDOUT_FILENO, bottom->buf, bottom->size); - - for (rows = ws.ws_row; rows > 0 && - TAILQ_PREV(bottom, tailhead, entries) != NULL; rows--) { - bottom = TAILQ_PREV(bottom, tailhead, entries); - write(STDOUT_FILENO, bottom->buf, bottom->size); - } - - if (bottom == TAILQ_FIRST(&head)) { - /* add new line in front of the shell prompt */ - write(STDOUT_FILENO, "\n", 1); - write(STDOUT_FILENO, "\033[?25h", 6); /* show cursor */ - } else - bottom = TAILQ_NEXT(bottom, entries); -} - -void -scrollup(int n) -{ - int rows = 2, x, y, extra = 0; - struct line *scrollend = bottom; - - if (bottom == NULL) - return; - - getcursorposition(&x, &y); - - if (n < 0) /* scroll by fraction of ws.ws_row, but at least one line */ - n = ws.ws_row > (-n) ? ws.ws_row / (-n) : 1; - - /* wind back scrollend pointer by the current screen */ - while (rows < y && TAILQ_NEXT(scrollend, entries) != NULL) { - scrollend = TAILQ_NEXT(scrollend, entries); - rows += (scrollend->len - 1) / ws.ws_col + 1; - } - - if (rows <= 0) - return; - - /* wind back scrollend pointer n lines */ - for (rows = 0; rows + extra < n && - TAILQ_NEXT(scrollend, entries) != NULL; rows++) { - scrollend = TAILQ_NEXT(scrollend, entries); - extra += (scrollend->len - 1) / ws.ws_col; - } - - /* move the text in terminal rows lines down */ - dprintf(STDOUT_FILENO, "\033[%dT", n); - /* set cursor position to upper left corner */ - write(STDOUT_FILENO, "\033[0;0H", 6); - /* hide cursor */ - write(STDOUT_FILENO, "\033[?25l", 6); - - /* remove newline of first line as we are at 0,0 already */ - if (scrollend->size > 0 && scrollend->buf[0] == '\n') - write(STDOUT_FILENO, scrollend->buf + 1, scrollend->size - 1); - else - write(STDOUT_FILENO, scrollend->buf, scrollend->size); - if (y + n >= ws.ws_row) - bottom = TAILQ_NEXT(bottom, entries); - - /* print rows lines and move bottom forward to the new screen bottom */ - for (; rows > 1; rows--) { - scrollend = TAILQ_PREV(scrollend, tailhead, entries); - if (y + n >= ws.ws_row) - bottom = TAILQ_NEXT(bottom, entries); - write(STDOUT_FILENO, scrollend->buf, scrollend->size); - } - /* move cursor from line n to the old bottom position */ - if (y + n < ws.ws_row) { - dprintf(STDOUT_FILENO, "\033[%d;%dH", y + n, x); - write(STDOUT_FILENO, "\033[?25h", 6); /* show cursor */ - } else - dprintf(STDOUT_FILENO, "\033[%d;0H", ws.ws_row); -} - -void -scrolldown(char *buf, size_t size, int n) -{ - if (bottom == NULL || bottom == TAILQ_FIRST(&head)) - return; - - if (n < 0) /* scroll by fraction of ws.ws_row, but at least one line */ - n = ws.ws_row > (-n) ? ws.ws_row / (-n) : 1; - - bottom = TAILQ_PREV(bottom, tailhead, entries); - /* print n lines */ - while (n > 0 && bottom != NULL && bottom != TAILQ_FIRST(&head)) { - bottom = TAILQ_PREV(bottom, tailhead, entries); - write(STDOUT_FILENO, bottom->buf, bottom->size); - n -= (bottom->len - 1) / ws.ws_col + 1; - } - if (n > 0 && bottom == TAILQ_FIRST(&head)) { - write(STDOUT_FILENO, "\033[?25h", 6); /* show cursor */ - write(STDOUT_FILENO, buf, size); - } else if (bottom != NULL) - bottom = TAILQ_NEXT(bottom, entries); -} - -void -jumpdown(char *buf, size_t size) -{ - int rows = ws.ws_row; - - /* wind back by one page starting from the latest line */ - bottom = TAILQ_FIRST(&head); - for (; TAILQ_NEXT(bottom, entries) != NULL && rows > 0; rows--) - bottom = TAILQ_NEXT(bottom, entries); - - scrolldown(buf, size, ws.ws_row); -} - -void -usage(void) { - die("usage: %s [-Mvh] [-m mem] [program]", argv0); -} - -int -main(int argc, char *argv[]) -{ - int ch; - struct rlimit rlimit; - - argv0 = argv[0]; - - if (getrlimit(RLIMIT_DATA, &rlimit) == -1) - die("getrlimit"); - - const char *optstring = "Mm:vh"; - while ((ch = getopt(argc, argv, optstring)) != -1) { - switch (ch) { - case 'M': - rlimit.rlim_cur = rlimit.rlim_max; - break; - case 'm': - rlimit.rlim_cur = strtoull(optarg, NULL, 0); - if (errno != 0) - die("strtoull: %s", optarg); - break; - case 'v': - die("%s " VERSION, argv0); - break; - case 'h': - default: - usage(); - } - } - argc -= optind; - argv += optind; - - TAILQ_INIT(&head); - - if (isatty(STDIN_FILENO) == 0 || isatty(STDOUT_FILENO) == 0) - die("parent it not a tty"); - - /* save terminal settings for resetting after exit */ - if (tcgetattr(STDIN_FILENO, &dfl) == -1) - die("tcgetattr:"); - if (atexit(reset)) - die("atexit:"); - - /* get window size of the terminal */ - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) - die("ioctl:"); - - child = forkpty(&mfd, NULL, &dfl, &ws); - if (child == -1) - die("forkpty:"); - if (child == 0) { /* child */ - if (argc >= 1) { - execvp(argv[0], argv); - } else { - struct passwd *passwd = getpwuid(getuid()); - if (passwd == NULL) - die("getpwid:"); - execlp(passwd->pw_shell, passwd->pw_shell, NULL); - } - - perror("execvp"); - _exit(127); - } - - /* set maximum memory size for scrollback buffer */ - if (setrlimit(RLIMIT_DATA, &rlimit) == -1) - die("setrlimit:"); - -#ifdef __OpenBSD__ - if (pledge("stdio tty proc", NULL) == -1) - die("pledge:"); -#endif - - if (signal(SIGWINCH, sigwinch) == SIG_ERR) - die("signal:"); - - struct termios new = dfl; - cfmakeraw(&new); - new.c_cc[VMIN ] = 1; /* return read if at least one byte in buffer */ - new.c_cc[VTIME] = 0; /* no polling time for read from terminal */ - if (tcsetattr(STDIN_FILENO, TCSANOW, &new) == -1) - die("tcsetattr:"); - - size_t size = BUFSIZ, len = 0, pos = 0; - char *buf = calloc(size, sizeof *buf); - if (buf == NULL) - die("calloc:"); - - struct pollfd pfd[2] = { - {STDIN_FILENO, POLLIN, 0}, - {mfd, POLLIN, 0} - }; - - for (;;) { - char input[BUFSIZ]; - - if (poll(pfd, LENGTH(pfd), -1) == -1 && errno != EINTR) - die("poll:"); - - if (doredraw) { - redraw(); - doredraw = false; - } - - if (pfd[0].revents & POLLHUP || pfd[1].revents & POLLHUP) - break; - - if (pfd[0].revents & POLLIN) { - ssize_t n = read(STDIN_FILENO, input, sizeof(input)-1); - - if (n == -1 && errno != EINTR) - die("read:"); - if (n == 0) - break; - - input[n] = '\0'; - - if (altscreen) - goto noevent; - - for (size_t i = 0; i < LENGTH(rules); i++) { - if (strncmp(rules[i].seq, input, - strlen(rules[i].seq)) == 0) { - if (rules[i].event == SCROLL_UP) - scrollup(rules[i].lines); - if (rules[i].event == SCROLL_DOWN) - scrolldown(buf, len, - rules[i].lines); - goto out; - } - } - noevent: - if (write(mfd, input, n) == -1) - die("write:"); - - if (bottom != TAILQ_FIRST(&head)) - jumpdown(buf, len); - } - out: - if (pfd[1].revents & POLLIN) { - ssize_t n = read(mfd, input, sizeof(input)-1); - - if (n == -1 && errno != EINTR) - die("read:"); - if (n == 0) /* on exit of child we continue here */ - continue; /* let signal handler catch SIGCHLD */ - - input[n] = '\0'; - - /* don't print child output while scrolling */ - if (bottom == TAILQ_FIRST(&head)) - if (write(STDOUT_FILENO, input, n) == -1) - die("write:"); - - /* iterate over the input buffer */ - for (char *c = input; n-- > 0; c++) { - /* don't save alternative screen and */ - /* clear screen esc sequences to scrollback */ - if (skipesc(*c)) - continue; - - if (*c == '\n') { - addline(buf, len); - /* only advance bottom if scroll is */ - /* at the end of the scroll back */ - if (bottom == NULL || - TAILQ_PREV(bottom, tailhead, - entries) == TAILQ_FIRST(&head)) - bottom = TAILQ_FIRST(&head); - - memset(buf, 0, size); - len = pos = 0; - buf[pos++] = '\r'; - } else if (*c == '\r') { - pos = 0; - continue; - } - buf[pos++] = *c; - if (pos > len) - len = pos; - if (len == size) { - size *= 2; - buf = earealloc(buf, size); - } - } - } - } - - if (close(mfd) == -1) - die("close:"); - - int status; - if (waitpid(child, &status, 0) == -1) - die("waitpid:"); - - return WEXITSTATUS(status); -} |