use std::{
ffi::{CStr, OsStr, OsString},
os::unix::prelude::OsStrExt,
};
pub fn cerr<Int: Copy + TryInto<libc::c_long>>(res: Int) -> std::io::Result<Int> {
match res.try_into() {
Ok(-1) => Err(std::io::Error::last_os_error()),
_ => Ok(res),
}
}
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")]
fn errno_location() -> *mut libc::c_int;
}
pub fn set_errno(no: libc::c_int) {
unsafe { *errno_location() = no };
}
pub fn sysconf(name: libc::c_int) -> Option<libc::c_long> {
set_errno(0);
cerr(unsafe { libc::sysconf(name) }).ok()
}
pub unsafe fn string_from_ptr(ptr: *const libc::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 libc::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()
}
}
#[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("\0".as_ptr() as *const libc::c_char), "");
assert_eq!(strp("hello\0".as_ptr() as *const libc::c_char), "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("\0".as_ptr() as *const libc::c_char), "");
assert_eq!(strp("hello\0".as_ptr() as *const libc::c_char), "hello");
}
}