scroll

scrollbackbuffer program for st
git clone git://git.suckless.org//gitrepos
Log | Files | Refs

commit fe3d1feec129390659819f5f99a15cd54e6da281
Author: Jan Klemkow <j.klemkow@wemelug.de>
Date:   Mon, 30 Dec 2019 02:56:02 +0100

first version of scroll from Roberto E. Vargas Caballero

https://lists.suckless.org/dev/1703/31256.html

Diffstat:
Ascroll.c | 188+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 188 insertions(+), 0 deletions(-)

diff --git a/scroll.c b/scroll.c @@ -0,0 +1,188 @@ +#define _DEFAULT_SOURCE +#include <stdio.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <stdlib.h> +#include <pty.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> +#include <signal.h> +#include <sys/wait.h> +#include <errno.h> + +typedef struct Line Line; +struct Line { + Line *next; + Line *prev; + size_t len; + char str[]; +}; + +pid_t child; +int mfd; +struct termios dfl; +struct winsize ws; +Line *lines, *bottom; + +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(1); +} + +void +sigchld(int sig) +{ + pid_t pid; + while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) + if (pid == child) + die("child died"); +} + +void +sigwinch(int sig) +{ + if (ioctl(1, TIOCGWINSZ, &ws) < 0) + die("ioctl:"); + if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) + die("ioctl:"); + kill(-child, SIGWINCH); +} + +void +reset(void) +{ + if (tcsetattr(0, TCSANOW, &dfl) < 0) + die("tcsetattr:"); +} + +void +addline(char *str) +{ + size_t len = strchr(str, '\n') - str + 1; + Line *lp = malloc(sizeof(*lp) + len * sizeof(*lp->str)); + + if (!lp) + die("malloc:"); + memcpy(lp->str, str, len); + lp->len = len; + if (lines) + lines->next = lp; + lp->prev = lines; + lp->next = NULL; + bottom = lines = lp; +} + +void +scrollup(void) +{ + Line *lp; + int rows = ws.ws_row-1; + int cols = ws.ws_col; + + if (!bottom || !(bottom = bottom->prev)) + return; + + for (lp = bottom; lp && rows > 0; lp = lp->prev) + rows -= lp->len / cols + 1; + + if (rows < 0) { + write(1, lp->str + -rows * cols, lp->len - -rows * cols); + rows = 0; + lp = lp->next; + } + + for (; lp && lp != bottom->next; lp = lp->next) + write(1, lp->str, lp->len); +} + +int +main(int argc, char *argv[]) +{ + if (argc <= 1) + die("usage"); + + if (tcgetattr(0, &dfl) < 0) + die("tcgetattr:"); + if (atexit(reset)) + die("atexit:"); + + if (ioctl(1, TIOCGWINSZ, &ws) < 0) + die("ioctl:"); + + child = forkpty(&mfd, NULL, &dfl, &ws); + if (child < 0) + die("forkpty:"); + if (!child) { + execvp(argv[1], argv + 1); + perror("execvp"); + _exit(127); + } + + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("signal:"); + if (signal(SIGWINCH, sigwinch) == SIG_ERR) + die("signal:"); + + int f; + if ((f = fcntl(mfd, F_GETFL)) < 0) + die("fcntl:"); + if (fcntl(mfd, F_SETFL, f /*| O_NONBLOCK*/) < 0) + die("fcntl:"); + + struct termios new; + new = dfl; + cfmakeraw(&new); + new.c_cc[VMIN ] = 1; + new.c_cc[VTIME] = 0; + if (tcsetattr(0, TCSANOW, &new) < 0) + die("tcsetattr:"); + + fd_set rd; + char buf[10000], *p = buf; + for (;;) { + char c; + + FD_ZERO(&rd); + FD_SET( 0, &rd); + FD_SET(mfd, &rd); + + if (select(mfd + 1, &rd, NULL, NULL, NULL) < 0 && errno != EINTR) + die("select:"); + + if (FD_ISSET(0, &rd)) { + if (read(0, &c, 1) <= 0 && errno != EINTR) + die("read:"); + if (c == 17) /* ^Q */ + scrollup(); + else if (write(mfd, &c, 1) < 0) + die("write:"); + } + if (FD_ISSET(mfd, &rd)) { + if (read(mfd, &c, 1) <= 0 && errno != EINTR) + die("read:"); + *p++ = c; + if (c == '\n') { + p = buf; + addline(buf); + } + if (write(1, &c, 1) < 0) + die("write:"); + } + } + return 0; +}