scroll.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #define _DEFAULT_SOURCE
  2. #include <stdio.h>
  3. #include <termios.h>
  4. #include <sys/ioctl.h>
  5. #include <sys/select.h>
  6. #include <stdlib.h>
  7. #include <pty.h>
  8. #include <sys/types.h>
  9. #include <unistd.h>
  10. #include <fcntl.h>
  11. #include <stdarg.h>
  12. #include <string.h>
  13. #include <signal.h>
  14. #include <sys/wait.h>
  15. #include <errno.h>
  16. typedef struct Line Line;
  17. struct Line {
  18. Line *next;
  19. Line *prev;
  20. size_t len;
  21. char str[];
  22. };
  23. pid_t child;
  24. int mfd;
  25. struct termios dfl;
  26. struct winsize ws;
  27. Line *lines, *bottom;
  28. void
  29. die(const char *fmt, ...)
  30. {
  31. va_list ap;
  32. va_start(ap, fmt);
  33. vfprintf(stderr, fmt, ap);
  34. va_end(ap);
  35. if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
  36. fputc(' ', stderr);
  37. perror(NULL);
  38. } else {
  39. fputc('\n', stderr);
  40. }
  41. exit(1);
  42. }
  43. void
  44. sigchld(int sig)
  45. {
  46. pid_t pid;
  47. while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
  48. if (pid == child)
  49. die("child died");
  50. }
  51. void
  52. sigwinch(int sig)
  53. {
  54. if (ioctl(1, TIOCGWINSZ, &ws) < 0)
  55. die("ioctl:");
  56. if (ioctl(mfd, TIOCSWINSZ, &ws) < 0)
  57. die("ioctl:");
  58. kill(-child, SIGWINCH);
  59. }
  60. void
  61. reset(void)
  62. {
  63. if (tcsetattr(0, TCSANOW, &dfl) < 0)
  64. die("tcsetattr:");
  65. }
  66. void
  67. addline(char *str)
  68. {
  69. size_t len = strchr(str, '\n') - str + 1;
  70. Line *lp = malloc(sizeof(*lp) + len * sizeof(*lp->str));
  71. if (!lp)
  72. die("malloc:");
  73. memcpy(lp->str, str, len);
  74. lp->len = len;
  75. if (lines)
  76. lines->next = lp;
  77. lp->prev = lines;
  78. lp->next = NULL;
  79. bottom = lines = lp;
  80. }
  81. void
  82. scrollup(void)
  83. {
  84. Line *lp;
  85. int rows = ws.ws_row-1;
  86. int cols = ws.ws_col;
  87. if (!bottom || !(bottom = bottom->prev))
  88. return;
  89. for (lp = bottom; lp && rows > 0; lp = lp->prev)
  90. rows -= lp->len / cols + 1;
  91. if (rows < 0) {
  92. write(1, lp->str + -rows * cols, lp->len - -rows * cols);
  93. rows = 0;
  94. lp = lp->next;
  95. }
  96. for (; lp && lp != bottom->next; lp = lp->next)
  97. write(1, lp->str, lp->len);
  98. }
  99. int
  100. main(int argc, char *argv[])
  101. {
  102. if (argc <= 1)
  103. die("usage");
  104. if (tcgetattr(0, &dfl) < 0)
  105. die("tcgetattr:");
  106. if (atexit(reset))
  107. die("atexit:");
  108. if (ioctl(1, TIOCGWINSZ, &ws) < 0)
  109. die("ioctl:");
  110. child = forkpty(&mfd, NULL, &dfl, &ws);
  111. if (child < 0)
  112. die("forkpty:");
  113. if (!child) {
  114. execvp(argv[1], argv + 1);
  115. perror("execvp");
  116. _exit(127);
  117. }
  118. if (signal(SIGCHLD, sigchld) == SIG_ERR)
  119. die("signal:");
  120. if (signal(SIGWINCH, sigwinch) == SIG_ERR)
  121. die("signal:");
  122. int f;
  123. if ((f = fcntl(mfd, F_GETFL)) < 0)
  124. die("fcntl:");
  125. if (fcntl(mfd, F_SETFL, f /*| O_NONBLOCK*/) < 0)
  126. die("fcntl:");
  127. struct termios new;
  128. new = dfl;
  129. cfmakeraw(&new);
  130. new.c_cc[VMIN ] = 1;
  131. new.c_cc[VTIME] = 0;
  132. if (tcsetattr(0, TCSANOW, &new) < 0)
  133. die("tcsetattr:");
  134. fd_set rd;
  135. char buf[10000], *p = buf;
  136. for (;;) {
  137. char c;
  138. FD_ZERO(&rd);
  139. FD_SET( 0, &rd);
  140. FD_SET(mfd, &rd);
  141. if (select(mfd + 1, &rd, NULL, NULL, NULL) < 0 && errno != EINTR)
  142. die("select:");
  143. if (FD_ISSET(0, &rd)) {
  144. if (read(0, &c, 1) <= 0 && errno != EINTR)
  145. die("read:");
  146. if (c == 17) /* ^Q */
  147. scrollup();
  148. else if (write(mfd, &c, 1) < 0)
  149. die("write:");
  150. }
  151. if (FD_ISSET(mfd, &rd)) {
  152. if (read(mfd, &c, 1) <= 0 && errno != EINTR)
  153. die("read:");
  154. *p++ = c;
  155. if (c == '\n') {
  156. p = buf;
  157. addline(buf);
  158. }
  159. if (write(1, &c, 1) < 0)
  160. die("write:");
  161. }
  162. }
  163. return 0;
  164. }