scroll

scrollbackbuffer program for st
git clone git://git.suckless.org/scroll
Log | Files | Refs | README | LICENSE

ptty.c (2734B)


      1 #include <sys/wait.h>
      2 
      3 #include <errno.h>
      4 #include <inttypes.h>
      5 #include <limits.h>
      6 #include <poll.h>
      7 #include <stdarg.h>
      8 #include <stdbool.h>
      9 #include <stdio.h>
     10 #include <stdlib.h>
     11 #include <string.h>
     12 #include <termios.h>
     13 #include <unistd.h>
     14 
     15 #if   defined(__linux)
     16  #include <pty.h>
     17 #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
     18  #include <util.h>
     19 #elif defined(__FreeBSD__) || defined(__DragonFly__)
     20  #include <libutil.h>
     21 #endif
     22 
     23 void
     24 die(const char *fmt, ...)
     25 {
     26 	va_list ap;
     27 	va_start(ap, fmt);
     28 	vfprintf(stderr, fmt, ap);
     29 	va_end(ap);
     30 
     31 	if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
     32 		fputc(' ', stderr);
     33 		perror(NULL);
     34 	} else {
     35 		fputc('\n', stderr);
     36 	}
     37 
     38 	exit(EXIT_FAILURE);
     39 }
     40 
     41 void
     42 usage(void)
     43 {
     44 	fputs("ptty [-C] [-c cols] [-r rows] cmd\n", stderr);
     45 	exit(EXIT_FAILURE);
     46 }
     47 
     48 int
     49 main(int argc, char *argv[])
     50 {
     51 	struct winsize ws = {.ws_row = 25, .ws_col = 80, 0, 0};
     52 	int ch;
     53 	bool closeflag = false;
     54 
     55 	while ((ch = getopt(argc, argv, "c:r:Ch")) != -1) {
     56 		switch (ch) {
     57 		case 'c':	/* cols */
     58 			ws.ws_col = strtoimax(optarg, NULL, 10);
     59 			if (errno != 0)
     60 				die("strtoimax: %s", optarg);
     61 			break;
     62 		case 'r':	/* lines */
     63 			ws.ws_row = strtoimax(optarg, NULL, 10);
     64 			if (errno != 0)
     65 				die("strtoimax: %s", optarg);
     66 			break;
     67 		case 'C':
     68 			closeflag = true;
     69 			break;
     70 		case 'h':
     71 		default:
     72 			usage();
     73 		}
     74 	}
     75 	argc -= optind;
     76 	argv += optind;
     77 
     78 	if (argc < 1)
     79 		usage();
     80 
     81 	int mfd;
     82 	pid_t child = forkpty(&mfd, NULL, NULL, &ws);
     83 	switch (child) {
     84 	case -1:
     85 		die("forkpty");
     86 	case  0: /* child */
     87 		execvp(argv[0], argv);
     88 		die("exec");
     89 	}
     90 
     91 	/* parent */
     92 
     93 	if (closeflag && close(mfd) == -1)
     94 		die("close:");
     95 
     96 	int pfds = 2;
     97 	struct pollfd pfd[2] = {
     98 		{ STDIN_FILENO, POLLIN, 0},
     99 		{ mfd,          POLLIN, 0}
    100 	};
    101 
    102 	for (;;) {
    103 		char buf[BUFSIZ];
    104 		ssize_t n;
    105 		int r;
    106 
    107 		if ((r = poll(pfd, pfds, -1)) == -1)
    108 			die("poll:");
    109 
    110 		if (pfd[0].revents & POLLIN) {
    111 			if ((n = read(STDIN_FILENO, buf, sizeof buf)) == -1)
    112 				die("read:");
    113 			if (n == 0) {
    114 				pfd[0].fd = -1;
    115 				if (close(mfd) == -1)
    116 					die("close:");
    117 				break;
    118 			}
    119 			if (write(mfd, buf, n) == -1)
    120 				die("write:");
    121 		}
    122 
    123 		if (pfd[1].revents & POLLIN) {
    124 			if ((n = read(mfd, buf, sizeof(buf)-1)) == -1)
    125 				die("read:");
    126 
    127 			if (n == 0) break;
    128 
    129 			buf[n] = '\0';
    130 
    131 			/* handle cursor position request */
    132 			if (strcmp("\033[6n", buf) == 0) {
    133 				dprintf(mfd, "\033[25;1R");
    134 				continue;
    135 			}
    136 
    137 			if (write(STDOUT_FILENO, buf, n) == -1)
    138 				die("write:");
    139 		}
    140 
    141 		if (pfd[0].revents & POLLHUP) {
    142 			pfd[0].fd = -1;
    143 			if (close(mfd) == -1)
    144 				die("close:");
    145 			break;
    146 		}
    147 		if (pfd[1].revents & POLLHUP)
    148 			break;
    149 	}
    150 
    151 	int status;
    152 	if (waitpid(child, &status, 0) != child)
    153 		die("waitpid:");
    154 
    155 	return WEXITSTATUS(status);
    156 }