use crate::{
errors::LisaError,
termios::{Termios, cfmakeraw, os::target::TCSADRAIN, tcsetattr},
};
use libc::{F_GETFL, F_SETFL, O_NONBLOCK, OPOST, SIGINT, fcntl, raise, read};
use std::{
cmp::Ordering as CmpOrdering,
ffi::c_void,
io::{Error as IOError, ErrorKind as IOErrorKind},
};
pub type CachedModeType = Termios;
#[must_use]
pub fn default_cached_mode() -> CachedModeType {
Termios::empty()
}
pub fn enable_raw_mode() -> Result<CachedModeType, LisaError> {
let mut original_termios = Termios::from_fd(0)?;
let cached_mode_type = original_termios;
cfmakeraw(&mut original_termios);
original_termios.c_oflag |= OPOST;
tcsetattr(0, TCSADRAIN, &original_termios)?;
let flags = unsafe { fcntl(0, F_GETFL, 0) };
if flags < 0 {
return Err(LisaError::IOError(IOError::last_os_error()));
}
if flags & O_NONBLOCK == 0 {
let rc = unsafe { fcntl(0, F_SETFL, flags | O_NONBLOCK) };
if rc < 0 {
return Err(LisaError::IOError(IOError::last_os_error()));
}
}
Ok(cached_mode_type)
}
pub fn disable_raw_mode(cached: CachedModeType) -> Result<CachedModeType, LisaError> {
tcsetattr(0, TCSADRAIN, &cached)?;
let flags = unsafe { fcntl(0, F_GETFL, 0) };
if flags < 0 {
return Err(LisaError::IOError(IOError::last_os_error()));
}
if flags & O_NONBLOCK != 0 {
let rc = unsafe { fcntl(0, F_SETFL, flags & !O_NONBLOCK) };
if rc < 0 {
return Err(LisaError::IOError(IOError::last_os_error()));
}
}
Ok(Termios::empty())
}
#[allow(
// Is necessary to match with other OS's.
clippy::unnecessary_wraps,
)]
pub fn os_pre_reqs() -> Result<(), LisaError> {
Ok(())
}
pub fn raise_sigint() {
unsafe {
raise(SIGINT);
}
}
pub fn read_non_blocking_stdin() -> Result<String, LisaError> {
let mut final_bytes: Vec<u8> = Vec::new();
let mut current_buf: Vec<u8> = vec![0; 8192];
loop {
let read_bytes: isize = unsafe { read(0, current_buf.as_mut_ptr().cast::<c_void>(), 16) };
match read_bytes.cmp(&0) {
CmpOrdering::Less => {
let error = IOError::last_os_error();
if error.kind() == IOErrorKind::WouldBlock {
break;
}
return Err(LisaError::IOError(error));
}
CmpOrdering::Equal => {
break;
}
CmpOrdering::Greater => {
final_bytes
.extend(current_buf.drain(..usize::try_from(read_bytes).unwrap_or(usize::MAX)));
current_buf = vec![0; 16];
}
}
}
if final_bytes.is_empty() {
Ok(String::with_capacity(0))
} else {
Ok(String::from_utf8(final_bytes)?)
}
}