From: Michael D. Lowis Date: Wed, 5 Jul 2017 16:45:23 +0000 (-0400) Subject: Added pty helper app to spawn a command in a pseudo-terminal X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=83e588f662e582f98936fac64dad6560378497a0;p=projs%2Ftide.git Added pty helper app to spawn a command in a pseudo-terminal --- diff --git a/.gitignore b/.gitignore index 929dd13..576417b 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ tide pick tests/pick hl-cpp +pty diff --git a/Makefile b/Makefile index 7f57b98..e8bc34c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ INCS = -Iinc/ -BINS = tide pick xcpd term hl-cpp +BINS = tide pick xcpd term pty hl-cpp MAN1 = docs/tide.1 docs/pick.1 docs/picktag.1 docs/pickfile.1 LIBEDIT_OBJS = \ @@ -63,6 +63,7 @@ tide: tide.o libedit.a pick: pick.o libedit.a xcpd: xcpd.o libedit.a term: term.o libedit.a +pty: pty.o libedit.a hl-cpp: hl-cpp.o libedit.a tests/libedit: tests/libedit.o tests/lib/buf.o tests/lib/utf8.o libedit.a tests/tide: tests/tide.o libedit.a diff --git a/pty.c b/pty.c new file mode 100644 index 0000000..06562ad --- /dev/null +++ b/pty.c @@ -0,0 +1,100 @@ +#define _XOPEN_SOURCE 700 +#include +#include +#ifdef __MACH__ + #include +#else + #include +#endif +#include +#include + +void usage(void) { + fprintf(stderr, "usage: pty COMMAND ...\n"); + exit(EXIT_FAILURE); +} + +int pty_spawn(char** argv) { + int fd; + struct termios tio; + pid_t pid; + putenv("TERM=dumb"); + switch ( (pid = forkpty(&fd, NULL, NULL, NULL)) ) { + case -1: // Failed + die("forkpty() :"); + break; + + case 0: // Child Process + execvp(argv[0], argv); + exit(EXIT_FAILURE); + break; + + default: // Parent Process + tcgetattr(fd, &tio); + tio.c_lflag &= ~(ECHO | ECHONL); + tio.c_cc[ VMIN ] = 1; + tio.c_cc[ VTIME ] = 0; + tcsetattr(fd, TCSANOW, &tio); + break; + } + return fd; +} + +int pty_input(int ptyfd, char* buf, size_t bufsz) { + long n; + if ((n = read(STDIN_FILENO, buf, bufsz)) < 0) + die("read() stdin :"); + if (n == 0) + return 1; + if ((n = write(ptyfd, buf, n)) < 0) + die("write() subprocess :"); + return 0; +} + +int pty_output(int ptyfd, char* buf, size_t bufsz) { + long n; + if ((n = read(ptyfd, buf, bufsz)) < 0) { + if (errno == EIO) + return 1; + else + die("read() subprocess :"); + } + if ((n = write(STDOUT_FILENO, buf, n)) < 0) + die("write() stdout :"); + return 0; +} + +int main(int argc, char** argv) { + if (argc == 1) usage(); + + /* allocate the buffer, spawn the pty, and setup the polling structures */ + static char buf[4096]; + int done = 0, ptyfd = pty_spawn(++argv); + struct pollfd pfds[2] = { + { .fd = STDIN_FILENO, .events = POLLIN }, + { .fd = ptyfd, .events = POLLIN }, + }; + + /* enter the event loop */ + while (!done) { + /* clear previous events and poll for new ones */ + pfds[0].revents = pfds[1].revents = 0; + if (poll(pfds,2,-1) < 0) + die("poll() :"); + + /* shutdown if there's an error or the pipes are closed */ + if ((pfds[0].revents | pfds[1].revents) & (POLLERR|POLLHUP)) + done = 1; + + /* pass stdin to the subprocess */ + if (pfds[0].revents & POLLIN) + done = pty_input(ptyfd, buf, sizeof(buf)); + + /* pass subprocess output to stdout */ + if (pfds[1].revents & POLLIN) + done = pty_output(ptyfd, buf, sizeof(buf)); + } + close(ptyfd); + + return 0; +}