|
|
@@ -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;
|
|
|
+}
|