ree-cli 0.1.0

A fast terminal reset for macOS and Linux
Documentation
use std::os::fd::{BorrowedFd, OwnedFd};

use rustix::fs::{self, Mode, OFlags};
use rustix::io::{self, Errno};
use rustix::stdio;
use rustix::termios::{
    self, ControlModes, InputModes, LocalModes, OutputModes, SpecialCodeIndex, Termios,
};

#[cfg(target_os = "macos")]
const POSIX_VDISABLE: u8 = 0xff;

#[cfg(target_os = "linux")]
const POSIX_VDISABLE: u8 = 0;

pub fn find() -> Option<OwnedFd> {
    for fd in [stdio::stderr(), stdio::stdout()] {
        if termios::isatty(fd)
            && is_writable(fd)
            && let Ok(owned) = io::dup(fd)
        {
            return Some(owned);
        }
    }

    let input = stdio::stdin();
    if termios::isatty(input)
        && let Ok(path) = termios::ttyname(input, Vec::new())
        && let Ok(owned) = fs::open(
            path.as_c_str(),
            OFlags::RDWR | OFlags::NOCTTY | OFlags::CLOEXEC,
            Mode::empty(),
        )
    {
        return Some(owned);
    }

    fs::open(
        "/dev/tty",
        OFlags::RDWR | OFlags::NOCTTY | OFlags::CLOEXEC,
        Mode::empty(),
    )
    .ok()
}

fn is_writable(fd: BorrowedFd<'_>) -> bool {
    fs::fcntl_getfl(fd).is_ok_and(|flags| flags & OFlags::ACCMODE != OFlags::RDONLY)
}

pub fn retry_on_interrupt<T>(mut operation: impl FnMut() -> io::Result<T>) -> io::Result<T> {
    loop {
        match operation() {
            Err(Errno::INTR) => {}
            result => return result,
        }
    }
}

pub fn repair_terminal_mode(mode: &mut Termios) {
    repair_input_modes(&mut mode.input_modes);
    repair_output_modes(&mut mode.output_modes);
    repair_control_modes(&mut mode.control_modes);
    repair_local_modes(&mut mode.local_modes);

    repair_control_character(mode, SpecialCodeIndex::VEOF, 0o4);
    repair_control_character(mode, SpecialCodeIndex::VERASE, 0o177);
    repair_control_character(mode, SpecialCodeIndex::VWERASE, 0o27);
    repair_control_character(mode, SpecialCodeIndex::VKILL, 0o25);
    repair_control_character(mode, SpecialCodeIndex::VREPRINT, 0o22);
    repair_control_character(mode, SpecialCodeIndex::VINTR, 0o3);
    repair_control_character(mode, SpecialCodeIndex::VQUIT, 0o34);
    repair_control_character(mode, SpecialCodeIndex::VSUSP, 0o32);
    repair_control_character(mode, SpecialCodeIndex::VSTART, 0o21);
    repair_control_character(mode, SpecialCodeIndex::VSTOP, 0o23);
    repair_control_character(mode, SpecialCodeIndex::VLNEXT, 0o26);
    repair_control_character(mode, SpecialCodeIndex::VDISCARD, 0o17);
    repair_darwin_control_characters(mode);
}

fn repair_input_modes(modes: &mut InputModes) {
    modes.remove(
        InputModes::IGNBRK
            | InputModes::PARMRK
            | InputModes::INPCK
            | InputModes::ISTRIP
            | InputModes::INLCR
            | InputModes::IGNCR
            | InputModes::IXOFF
            | InputModes::IXANY,
    );
    modes.insert(
        InputModes::BRKINT
            | InputModes::IGNPAR
            | InputModes::ICRNL
            | InputModes::IXON
            | InputModes::IMAXBEL,
    );
}

fn repair_output_modes(modes: &mut OutputModes) {
    modes.remove(OutputModes::OCRNL | OutputModes::ONOCR | OutputModes::ONLRET);
    remove_output_delay_modes(modes);
    modes.insert(OutputModes::OPOST | OutputModes::ONLCR);
}

fn repair_control_modes(modes: &mut ControlModes) {
    modes.remove(
        ControlModes::CSIZE
            | ControlModes::PARENB
            | ControlModes::PARODD
            | ControlModes::CSTOPB
            | ControlModes::CLOCAL,
    );
    modes.insert(ControlModes::CS8 | ControlModes::CREAD);
}

fn repair_local_modes(modes: &mut LocalModes) {
    modes.remove(
        LocalModes::ECHONL
            | LocalModes::NOFLSH
            | LocalModes::TOSTOP
            | LocalModes::ECHOPRT
            | LocalModes::FLUSHO
            | LocalModes::PENDIN
            | LocalModes::EXTPROC,
    );
    modes.insert(
        LocalModes::ISIG
            | LocalModes::ICANON
            | LocalModes::IEXTEN
            | LocalModes::ECHO
            | LocalModes::ECHOE
            | LocalModes::ECHOK
            | LocalModes::ECHOKE
            | LocalModes::ECHOCTL,
    );
}

#[cfg(target_os = "linux")]
fn remove_output_delay_modes(modes: &mut OutputModes) {
    modes.remove(
        OutputModes::OFILL
            | OutputModes::OFDEL
            | OutputModes::NLDLY
            | OutputModes::CRDLY
            | OutputModes::TABDLY
            | OutputModes::BSDLY
            | OutputModes::VTDLY
            | OutputModes::FFDLY,
    );
}

#[cfg(target_os = "macos")]
fn remove_output_delay_modes(modes: &mut OutputModes) {
    modes.remove(OutputModes::from_bits_retain(
        libc::OFILL
            | libc::OFDEL
            | libc::NLDLY
            | libc::CRDLY
            | libc::TABDLY
            | libc::BSDLY
            | libc::VTDLY
            | libc::FFDLY,
    ));
}

#[cfg(target_os = "macos")]
fn repair_darwin_control_characters(mode: &mut Termios) {
    repair_control_character(mode, SpecialCodeIndex::VDSUSP, 0o31);
    repair_control_character(mode, SpecialCodeIndex::VSTATUS, 0o24);
}

#[cfg(target_os = "linux")]
const fn repair_darwin_control_characters(_mode: &mut Termios) {}

fn repair_control_character(mode: &mut Termios, index: SpecialCodeIndex, default: u8) {
    repair_control_character_value(&mut mode.special_codes[index], default);
}

const fn repair_control_character_value(current: &mut u8, default: u8) {
    if *current == 0 || *current == POSIX_VDISABLE {
        *current = default;
    }
}

#[cfg(test)]
#[path = "tty_tests.rs"]
mod tests;