hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Aunix/tty/+openbsd/isatty.ha | 17+++++++++++++++++
Aunix/tty/+openbsd/open.ha | 18++++++++++++++++++
Aunix/tty/+openbsd/pty.ha | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aunix/tty/+openbsd/termios.ha | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aunix/tty/+openbsd/winsize.ha | 28++++++++++++++++++++++++++++
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, + }; + }; +};