aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/bindings/wtf-bindings.cpp
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-08-19 00:11:24 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-19 00:11:24 -0700
commitdb09ed15fd561b89b24b979b986e21a04576f7cc (patch)
treeafe32e4eccaf1fe3f2f4cd536c1f2a61efad28c1 /src/bun.js/bindings/wtf-bindings.cpp
parentbf517d9f8e993a9ed3a02d21094c3ce76d7953a1 (diff)
downloadbun-db09ed15fd561b89b24b979b986e21a04576f7cc.tar.gz
bun-db09ed15fd561b89b24b979b986e21a04576f7cc.tar.zst
bun-db09ed15fd561b89b24b979b986e21a04576f7cc.zip
tty `ReadStream`, `WriteStream`, and readline rawmode (#4179)
* tty `WriteStream`, `ReadStream`, and rawmode * tests * refactor prototypes * fix failing test * fix test and library usage * more merge * fix child_process test * create pseudo terminal for tty tests * match node logic * handle invalid tty * close descriptors * move tests to another process * fix test again * fix test on linux
Diffstat (limited to 'src/bun.js/bindings/wtf-bindings.cpp')
-rw-r--r--src/bun.js/bindings/wtf-bindings.cpp134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/bun.js/bindings/wtf-bindings.cpp b/src/bun.js/bindings/wtf-bindings.cpp
index ccf71f8eb..6fd10721a 100644
--- a/src/bun.js/bindings/wtf-bindings.cpp
+++ b/src/bun.js/bindings/wtf-bindings.cpp
@@ -2,6 +2,8 @@
#include "wtf/StackTrace.h"
#include "wtf/dtoa.h"
+#include "wtf/Lock.h"
+#include "termios.h"
extern "C" double WTF__parseDouble(const LChar* string, size_t length, size_t* position)
{
@@ -13,6 +15,138 @@ extern "C" void WTF__copyLCharsFromUCharSource(LChar* destination, const UChar*
WTF::StringImpl::copyCharacters(destination, source, length);
}
+static int orig_termios_fd = -1;
+static struct termios orig_termios;
+static WTF::Lock orig_termios_lock;
+
+static int current_tty_mode = 0;
+static struct termios orig_tty_termios;
+
+int uv__tcsetattr(int fd, int how, const struct termios* term)
+{
+ int rc;
+
+ do
+ rc = tcsetattr(fd, how, term);
+ while (rc == -1 && errno == EINTR);
+
+ if (rc == -1)
+ return errno;
+
+ return 0;
+}
+
+static void uv__tty_make_raw(struct termios* tio)
+{
+ assert(tio != NULL);
+
+#if defined __sun || defined __MVS__
+ /*
+ * This implementation of cfmakeraw for Solaris and derivatives is taken from
+ * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html.
+ */
+ tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
+ tio->c_oflag &= ~OPOST;
+ tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ tio->c_cflag &= ~(CSIZE | PARENB);
+ tio->c_cflag |= CS8;
+
+ /*
+ * By default, most software expects a pending read to block until at
+ * least one byte becomes available. As per termio(7I), this requires
+ * setting the MIN and TIME parameters appropriately.
+ *
+ * As a somewhat unfortunate artifact of history, the MIN and TIME slots
+ * in the control character array overlap with the EOF and EOL slots used
+ * for canonical mode processing. Because the EOF character needs to be
+ * the ASCII EOT value (aka Control-D), it has the byte value 4. When
+ * switching to raw mode, this is interpreted as a MIN value of 4; i.e.,
+ * reads will block until at least four bytes have been input.
+ *
+ * Other platforms with a distinct MIN slot like Linux and FreeBSD appear
+ * to default to a MIN value of 1, so we'll force that value here:
+ */
+ tio->c_cc[VMIN] = 1;
+ tio->c_cc[VTIME] = 0;
+#else
+ cfmakeraw(tio);
+#endif /* #ifdef __sun */
+}
+
+extern "C" int
+Bun__ttySetMode(int fd, int mode)
+{
+ struct termios tmp;
+ int expected;
+ int rc;
+
+ if (current_tty_mode == mode)
+ return 0;
+
+ if (current_tty_mode == 0 && mode != 0) {
+ do {
+ rc = tcgetattr(fd, &orig_tty_termios);
+ } while (rc == -1 && errno == EINTR);
+
+ if (rc == -1)
+ return errno;
+
+ {
+ /* This is used for uv_tty_reset_mode() */
+ LockHolder locker(orig_termios_lock);
+
+ if (orig_termios_fd == -1) {
+ orig_termios = orig_termios;
+ orig_termios_fd = fd;
+ }
+ }
+ }
+
+ tmp = orig_tty_termios;
+ switch (mode) {
+ case 0: // normal
+ break;
+ case 1: // raw
+ tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ tmp.c_oflag |= (ONLCR);
+ tmp.c_cflag |= (CS8);
+ tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+ tmp.c_cc[VMIN] = 1;
+ tmp.c_cc[VTIME] = 0;
+ break;
+ case 2: // io
+ uv__tty_make_raw(&tmp);
+ break;
+ }
+
+ /* Apply changes after draining */
+ rc = uv__tcsetattr(fd, TCSADRAIN, &tmp);
+ if (rc == 0)
+ current_tty_mode = mode;
+
+ return rc;
+}
+
+int uv_tty_reset_mode(void)
+{
+ int saved_errno;
+ int err;
+
+ saved_errno = errno;
+
+ if (orig_termios_lock.tryLock())
+ return 16; // UV_EBUSY; /* In uv_tty_set_mode(). */
+
+ err = 0;
+ if (orig_termios_fd != -1)
+ err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios);
+
+ orig_termios_lock.unlock();
+ errno = saved_errno;
+
+ return err;
+}
+
extern "C" void Bun__crashReportWrite(void* ctx, const char* message, size_t length);
extern "C" void Bun__crashReportDumpStackTrace(void* ctx)
{