]> git.mdlowis.com Git - projs/tide.git/commitdiff
Added pty helper app to spawn a command in a pseudo-terminal
authorMichael D. Lowis <mike.lowis@gentex.com>
Wed, 5 Jul 2017 16:45:23 +0000 (12:45 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Wed, 5 Jul 2017 16:45:23 +0000 (12:45 -0400)
.gitignore
Makefile
pty.c [new file with mode: 0644]

index 929dd13661c46b20f6036be095f9f79e7c89412f..576417bacd72cefd4090a1763a394cd2e72ed9a5 100644 (file)
@@ -52,3 +52,4 @@ tide
 pick
 tests/pick
 hl-cpp
+pty
index 7f57b98f37a3aec1186ebe7460f762ab4c339ad8..e8bc34c03eea530b005e41c5705d9987ec591e24 100644 (file)
--- 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 (file)
index 0000000..06562ad
--- /dev/null
+++ b/pty.c
@@ -0,0 +1,100 @@
+#define _XOPEN_SOURCE 700
+#include <stdc.h>
+#include <unistd.h>
+#ifdef __MACH__
+    #include <util.h>
+#else
+    #include <pty.h>
+#endif
+#include <poll.h>
+#include <termios.h>
+
+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;
+}