use std::{
ffi::{CStr, OsStr, OsString, c_char, c_int, c_long},
os::{
fd::{AsRawFd, BorrowedFd},
unix::prelude::OsStrExt,
},
};
pub fn cerr<Int: Copy + TryInto<c_long>>(res: Int) -> std::io::Result<Int> {
match res.try_into() {
Ok(-1) => Err(std::io::Error::last_os_error()),
_ => Ok(res),
}
}
unsafe extern "C" {
#[cfg_attr(
any(target_os = "macos", target_os = "ios", target_os = "freebsd"),
link_name = "__error"
)]
#[cfg_attr(
any(target_os = "openbsd", target_os = "netbsd", target_os = "android"),
link_name = "__errno"
)]
#[cfg_attr(target_os = "linux", link_name = "__errno_location")]
safe fn errno_location() -> *mut c_int;
}
pub fn set_errno(no: c_int) {
unsafe { *errno_location() = no };
}
pub fn sysconf(name: c_int) -> Option<c_long> {
set_errno(0);
cerr(unsafe { libc::sysconf(name) }).ok()
}
pub unsafe fn string_from_ptr(ptr: *const c_char) -> String {
if ptr.is_null() {
String::new()
} else {
let cstr = unsafe { CStr::from_ptr(ptr) };
cstr.to_string_lossy().to_string()
}
}
pub unsafe fn os_string_from_ptr(ptr: *const c_char) -> OsString {
if ptr.is_null() {
OsString::new()
} else {
let cstr = unsafe { CStr::from_ptr(ptr) };
OsStr::from_bytes(cstr.to_bytes()).to_owned()
}
}
fn fstat_mode_any<const MASK: libc::mode_t>(fildes: &BorrowedFd) -> bool {
const {
assert!(MASK & libc::S_IFMT == MASK);
}
let mut maybe_stat = std::mem::MaybeUninit::<libc::stat>::uninit();
if unsafe { libc::fstat(fildes.as_raw_fd(), maybe_stat.as_mut_ptr()) } == 0 {
let mode = unsafe { maybe_stat.assume_init() }.st_mode;
(mode & MASK) != 0
} else {
false
}
}
pub fn safe_isatty(fildes: BorrowedFd) -> bool {
let is_char_device = fstat_mode_any::<{ libc::S_IFCHR }>(&fildes);
if is_char_device {
unsafe { libc::isatty(fildes.as_raw_fd()) != 0 }
} else {
false
}
}
pub fn is_fifo_or_sock(fildes: BorrowedFd) -> bool {
fstat_mode_any::<{ libc::S_IFIFO | libc::S_IFSOCK }>(&fildes)
}
#[allow(clippy::undocumented_unsafe_blocks)]
#[cfg(test)]
mod test {
use super::{os_string_from_ptr, string_from_ptr};
#[test]
fn miri_test_str_to_ptr() {
let strp = |ptr| unsafe { string_from_ptr(ptr) };
assert_eq!(strp(std::ptr::null()), "");
assert_eq!(strp(c"".as_ptr()), "");
assert_eq!(strp(c"hello".as_ptr()), "hello");
}
#[test]
fn miri_test_os_str_to_ptr() {
let strp = |ptr| unsafe { os_string_from_ptr(ptr) };
assert_eq!(strp(std::ptr::null()), "");
assert_eq!(strp(c"".as_ptr()), "");
assert_eq!(strp(c"hello".as_ptr()), "hello");
}
#[test]
fn test_tty() {
use crate::system::term::Pty;
use std::fs::File;
use std::os::fd::{AsFd, BorrowedFd};
assert!(!super::safe_isatty(File::open("/bin/sh").unwrap().as_fd()));
assert!(!super::safe_isatty(unsafe {
BorrowedFd::borrow_raw(-837492)
}));
let pty = Pty::open().unwrap();
assert!(super::safe_isatty(pty.leader.as_fd()));
assert!(super::safe_isatty(pty.follower.as_fd()));
}
}