commit 84c28db4dc003377b02e2f0272c816fd94c2c7b3
parent 4f295f0b6c48f9da46186af46b2479e3e2a18574
Author: Lorenz (xha) <me@xha.li>
Date: Sat, 25 Nov 2023 15:18:15 +0100
OpenBSD: add unix::tty
Signed-off-by: Lorenz (xha) <me@xha.li>
Diffstat:
5 files changed, 212 insertions(+), 0 deletions(-)
diff --git a/unix/tty/+openbsd/isatty.ha b/unix/tty/+openbsd/isatty.ha
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use rt;
+use io;
+use os;
+
+// Returns whether the given stream is connected to a terminal.
+export fn isatty(fd: io::file) bool = {
+ let wsz = rt::winsize { ... };
+ match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *opaque)) {
+ case let e: rt::errno =>
+ return false;
+ case let r: int =>
+ return r == 0;
+ };
+};
diff --git a/unix/tty/+openbsd/open.ha b/unix/tty/+openbsd/open.ha
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fs;
+use io;
+use os;
+
+// Returns a stream connected to the TTY of the current process. The caller must
+// close it using [[io::close]].
+export fn open() (io::file | error) = {
+ match (os::open("/dev/tty", fs::flag::RDWR)) {
+ case let f: io::file =>
+ return f;
+ case fs::error =>
+ return errors::noentry;
+ };
+};
diff --git a/unix/tty/+openbsd/pty.ha b/unix/tty/+openbsd/pty.ha
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fmt;
+use fs;
+use io;
+use os;
+use rt;
+use types::c;
+
+// Opens an available pseudoterminal and returns the file descriptors of the
+// master and slave.
+export fn openpty() ((io::file, io::file) | fs::error) = {
+ let master = open_master()?;
+ defer io::close(master)!;
+
+ let ptm = rt::ptmget { ... };
+ match (rt::ioctl(master, rt::PTMGET, &ptm)) {
+ case let e: rt::errno =>
+ return errors::errno(e);
+ case => void;
+ };
+
+ return (ptm.cfd, ptm.sfd);
+};
+
+// Opens an available pseudoterminal master.
+fn open_master() (io::file | fs::error) = {
+ match (rt::open(rt::PATH_PTMDEV, rt::O_RDWR, 0)) {
+ case let e: rt::errno =>
+ return errors::errno(e);
+ case let i: int =>
+ return io::fdopen(i);
+ };
+};
+
+// Returns the filename of the pseudoterminal slave.
+export fn ptsname(master: io::file) (str | error) = {
+ static let path_buf: [rt::PATH_MAX]u8 = [0...];
+
+ let name = match (rt::ptsname(master)) {
+ case let name: *u8 =>
+ yield name: *[*]u8;
+ case let err: rt::errno =>
+ switch (err) {
+ // master is not a pseudo-terminal device
+ case rt::EINVAL =>
+ return errors::unsupported;
+ // master is not an open valid file descriptor
+ case rt::EBADF =>
+ return errors::invalid;
+ case =>
+ abort("Unexpected error from ptsname");
+ };
+ };
+ let namelen = c::strlen(name: *const c::char);
+ path_buf[..namelen] = name[..namelen];
+
+ return c::tostrn(&path_buf: *const c::char, namelen)!;
+};
+
+// Sets the dimensions of the underlying pseudoterminal for an [[io::file]].
+export fn set_winsize(pty: io::file, sz: ttysize) (void | error) = {
+ let wsz = rt::winsize { ws_row = sz.rows, ws_col = sz.columns, ... };
+ match (rt::ioctl(pty, rt::TIOCSWINSZ, &wsz)) {
+ case let e: rt::errno =>
+ switch (e) {
+ case rt::EBADF, rt::EINVAL =>
+ return errors::invalid;
+ case rt::ENOTTY =>
+ return errors::unsupported;
+ case =>
+ abort("Unexpected error from ioctl");
+ };
+ case => void;
+ };
+};
diff --git a/unix/tty/+openbsd/termios.ha b/unix/tty/+openbsd/termios.ha
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// TODO: Not in love with this interface
+use io;
+use rt;
+use errors;
+
+export type termios = struct {
+ file: io::file,
+ saved: rt::termios,
+ current: rt::termios,
+};
+
+// Retrieves serial port settings of the given terminal.
+export fn termios_query(file: io::file) (termios | errors::error) = {
+ let settings = rt::termios { ... };
+
+ match (rt::ioctl(file, rt::TIOCGETA, &settings)) {
+ case int => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+
+ return termios {
+ file = file,
+ saved = settings,
+ current = settings,
+ };
+};
+
+// Restores original serial port settings.
+export fn termios_restore(termios: *const termios) void = {
+ rt::ioctl(termios.file, rt::TIOCSETA, &termios.saved): void;
+};
+
+// Sets serial port settings.
+export fn termios_set(termios: *const termios) (void | errors::error) = {
+ match (rt::ioctl(termios.file, rt::TIOCSETA, &termios.current)) {
+ case int => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Enables "raw" mode for this terminal, disabling echoing, line
+// editing, and signal handling. Users should call [[termios_query]]
+// prior to this to save the previous terminal settings, and
+// [[termios_restore]] to restore them before exiting.
+export fn makeraw(termios: *termios) (void | errors::error) = {
+ // Disable break signal and CR<->LF processing
+ termios.current.c_iflag &= ~(rt::tcflag::IGNBRK | rt::tcflag::BRKINT
+ | rt::tcflag::INLCR | rt::tcflag::IGNCR
+ | rt::tcflag::ICRNL);
+ // Disable output post-processing
+ termios.current.c_oflag &= ~rt::tcflag::OPOST;
+ // Disable character echo, canonical mode, implementation defined
+ // extensions and INTR/QUIT/SUSP characters
+ termios.current.c_lflag &= ~(rt::tcflag::ECHO | rt::tcflag::ECHONL
+ | rt::tcflag::ICANON | rt::tcflag::IEXTEN
+ | rt::tcflag::ISIG);
+
+ termios_set(termios)?;
+};
+
+// Disables "echo" on this terminal. Users should call [[termios_restore]] to
+// restore settings.
+export fn noecho(termios: *termios) (void | errors::error) = {
+ termios.current.c_lflag &= ~rt::tcflag::ECHO;
+ termios_set(termios)?;
+};
diff --git a/unix/tty/+openbsd/winsize.ha b/unix/tty/+openbsd/winsize.ha
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use os;
+use rt;
+
+// Returns the dimensions of underlying terminal for an [[io::file]].
+export fn winsize(fd: io::file) (ttysize | error) = {
+ let wsz = rt::winsize { ... };
+ match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *opaque)) {
+ case let e: rt::errno =>
+ switch (e) {
+ case rt::EBADF =>
+ return errors::invalid;
+ case rt::ENOTTY =>
+ return errors::unsupported;
+ case =>
+ abort("Unexpected error from ioctl");
+ };
+ case int =>
+ return ttysize {
+ rows = wsz.ws_row,
+ columns = wsz.ws_col,
+ };
+ };
+};