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:
| A | scroll.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;
+}