summary refs log tree commit diff
path: root/view.c
diff options
context:
space:
mode:
Diffstat (limited to 'view.c')
-rw-r--r--view.c177
1 files changed, 82 insertions, 95 deletions
diff --git a/view.c b/view.c
index 9ae0ec6..47c6c5b 100644
--- a/view.c
+++ b/view.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, Curtis McEnroe <programble@gmail.com>
+/* Copyright (C) 2018  Causal Agent June <june@causal.agency>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
@@ -19,10 +19,6 @@
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <sys/event.h>
 #include <sys/ioctl.h>
@@ -30,133 +26,124 @@
 #include <termios.h>
 #include <unistd.h>
 
-static struct termios saveTerm;
-static void restoreTerm(void) {
-    tcsetattr(STDOUT_FILENO, TCSADRAIN, &saveTerm);
-}
+extern int winch(void);
 
 static struct {
-    int read;
-    int write;
-} winch;
-
-static void sigwinch() {
-    struct winsize localWindow;
-    int error = ioctl(STDERR_FILENO, TIOCGWINSZ, &localWindow);
-    if (error) err(EX_IOERR, "ioctl(%d, TIOCGWINSZ, ...)", STDERR_FILENO);
-
-    ssize_t size = write(winch.write, &localWindow, sizeof(localWindow));
-    if (size < 0) err(EX_IOERR, "write(%d)", winch.write);
-    if ((size_t)size < sizeof(localWindow)) {
-        errx(EX_IOERR, "short write(%d)", winch.write);
-    }
-}
+    int winch;
+    int remote;
+    int local;
+    int input;
+} fd = { -1, -1, STDOUT_FILENO, STDIN_FILENO };
 
-static struct winsize remoteWindow;
-static void readRemoteWindow(int remote) {
-    ssize_t size = read(remote, &remoteWindow, sizeof(remoteWindow));
-    if (size < 0) err(EX_IOERR, "read(%d)", remote);
-    if ((size_t)size < sizeof(remoteWindow)) errx(EX_IOERR, "short read(%d)", remote);
+static struct termios saveTerm;
+static void restoreTerm(void) {
+    tcsetattr(fd.input, TCSADRAIN, &saveTerm);
 }
 
 int main(int argc, char *argv[]) {
     int error;
 
-    if (argc < 2) return EX_USAGE;
-    const char *path = argv[1];
+    if (argc < 2) errx(EX_USAGE, "missing public id");
+    if (!isatty(fd.local)) errx(EX_USAGE, "no terminal (use ssh -t)");
 
-    int input = STDIN_FILENO;
-    int local = STDOUT_FILENO;
+    const char *path = argv[1];
+    fd.remote = open(path, O_RDONLY);
+    if (fd.remote < 0) err(EX_NOINPUT, "%s", path);
 
-    error = tcgetattr(local, &saveTerm);
+    error = tcgetattr(fd.input, &saveTerm);
     if (error) err(EX_IOERR, "tcgetattr");
     atexit(restoreTerm);
 
     struct termios term = saveTerm;
     term.c_lflag &= ~(ICANON | ECHO);
-    error = tcsetattr(local, TCSADRAIN, &term);
+    error = tcsetattr(fd.input, TCSADRAIN, &term);
     if (error) err(EX_IOERR, "tcsetattr");
 
-    error = pipe((int *)&winch);
-    if (error) err(EX_OSERR, "pipe");
-    signal(SIGWINCH, sigwinch);
-    sigwinch();
-
-    int remote = open(path, O_RDONLY);
-    if (remote < 0) err(EX_NOINPUT, "%s", path);
-    readRemoteWindow(remote);
+    struct winsize localWindow;
+    error = ioctl(fd.local, TIOCGWINSZ, &localWindow);
+    if (error) err(EX_IOERR, "TIOCGWINSZ");
+    fd.winch = winch();
 
     int kq = kqueue();
     if (kq < 0) err(EX_OSERR, "kqueue");
 
     struct kevent events[3] = {
-        { .ident = winch.read, .filter = EVFILT_READ, .flags = EV_ADD },
-        { .ident = input, .filter = EVFILT_READ, .flags = EV_ADD },
-        { .ident = remote, .filter = EVFILT_READ, .flags = EV_ADD },
+        { .ident = fd.input, .filter = EVFILT_READ, .flags = EV_ADD },
+        { .ident = fd.remote, .filter = EVFILT_READ, .flags = EV_ADD },
+        { .ident = fd.winch, .filter = EVFILT_READ, .flags = EV_ADD },
     };
     int nevents = kevent(kq, events, 3, NULL, 0, NULL);
     if (nevents < 0) err(EX_OSERR, "kevent");
 
-    char buf[4096];
-    bool truncated = false;
     for (;;) {
-        struct kevent event;
-        int nevents = kevent(kq, NULL, 0, &event, 1, NULL);
-        if (nevents < 0) {
-            if (errno == EINTR) continue;
-            err(EX_OSERR, "kevent");
+        off_t off = lseek(fd.remote, 0, SEEK_SET);
+        if (off < 0) err(EX_IOERR, "%s", path);
+
+        // FIXME: Hack spin waiting for remote window.
+        struct winsize remoteWindow;
+        ssize_t size;
+        for (int i = 0; i < 100; ++i, usleep(100)) {
+            size = read(fd.remote, &remoteWindow, sizeof(remoteWindow));
+            if (size < 0) err(EX_IOERR, "read(%d)", fd.remote);
+            if (size) break;
         }
-        if (!nevents) continue;
-
-        if (event.ident == (uintptr_t)winch.read) {
-            for (;;) {
-                struct winsize localWindow;
-                ssize_t size = read(winch.read, &localWindow, sizeof(localWindow));
-                if (size < 0) err(EX_IOERR, "read(%d)", winch.read);
-                if ((size_t)size < sizeof(localWindow)) {
-                    errx(EX_IOERR, "short read(%d)", winch.read);
-                }
-
-                if (
-                    localWindow.ws_col == remoteWindow.ws_col
-                    && localWindow.ws_row == remoteWindow.ws_row
-                ) break;
-
-                warnx(
-                    "Please resize your terminal %hux%hu -> %hux%hu",
-                    localWindow.ws_col, localWindow.ws_row,
-                    remoteWindow.ws_col, remoteWindow.ws_row
-                );
-            }
+        if ((size_t)size < sizeof(remoteWindow)) {
+            errx(EX_DATAERR, "no window size (stream not started)");
         }
 
-        if (event.ident == (uintptr_t)input) {
-            ssize_t size = read(input, buf, sizeof(buf));
-            if (size < 0) err(EX_IOERR, "read(%d)", input);
-            if (size == 1 && buf[0] == 'q') return EX_OK;
+        if (
+            localWindow.ws_col < remoteWindow.ws_col
+            || localWindow.ws_row < remoteWindow.ws_row
+        ) {
+            warnx(
+                "resize window %hux%hu to at least %hux%hu",
+                localWindow.ws_col, localWindow.ws_row,
+                remoteWindow.ws_col, remoteWindow.ws_row
+            );
+
+            ssize_t size = read(fd.winch, &localWindow, sizeof(localWindow));
+            if (size < 0) err(EX_IOERR, "read(%d)", fd.winch);
+            if ((size_t)size < sizeof(localWindow)) {
+                errx(EX_IOERR, "short read(%d)", fd.winch);
+            }
+            continue;
         }
 
-        if (event.ident == (uintptr_t)remote) {
-            if (event.data < 0) {
-                off_t offset = lseek(remote, 0, SEEK_SET);
-                if (offset < 0) err(EX_IOERR, "lseek(%d)", remote);
-                truncated = true;
-                continue;
+        char buf[4096];
+        for (;;) {
+            struct kevent event;
+            int nevents = kevent(kq, NULL, 0, &event, 1, NULL);
+            if (nevents < 0) {
+                if (errno == EINTR) continue;
+                err(EX_OSERR, "kevent");
             }
+            if (!nevents) continue;
 
-            if (truncated) {
-                readRemoteWindow(remote);
-                sigwinch();
-                truncated = false;
-                continue;
+            if (event.ident == (uintptr_t)fd.input) {
+                ssize_t size = read(fd.input, buf, sizeof(buf));
+                if (size < 0) err(EX_IOERR, "read(%d)", fd.input);
+                if (size == 1 && buf[0] == 'q') return EX_OK;
             }
 
-            ssize_t readSize = read(remote, buf, sizeof(buf));
-            if (readSize < 0) err(EX_IOERR, "read(%d)", remote);
+            if (event.ident == (uintptr_t)fd.remote) {
+                if (event.data < 0) break;
 
-            ssize_t writeSize = write(local, buf, readSize);
-            if (writeSize < 0) err(EX_IOERR, "write(%d)", local);
-            if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", local);
+                ssize_t readSize = read(fd.remote, buf, sizeof(buf));
+                if (readSize < 0) err(EX_IOERR, "read(%d)", fd.remote);
+
+                ssize_t writeSize = write(fd.local, buf, readSize);
+                if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.local);
+                if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", fd.local);
+            }
+
+            if (event.ident == (uintptr_t)fd.winch) {
+                ssize_t size = read(fd.winch, &localWindow, sizeof(localWindow));
+                if (size < 0) err(EX_IOERR, "read(%d)", fd.winch);
+                if ((size_t)size < sizeof(localWindow)) {
+                    errx(EX_IOERR, "short read(%d)", fd.winch);
+                }
+                break;
+            }
         }
     }
 }