const TMP_BUF_SZ: usize = 128;
extern "C" {
#[cfg_attr(
any(
target_os = "linux",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "l4re"
),
link_name = "__errno_location"
)]
#[cfg_attr(
any(
target_os = "netbsd",
target_os = "openbsd",
target_os = "android",
target_os = "redox",
target_env = "newlib"
),
link_name = "__errno"
)]
#[cfg_attr(
any(target_os = "solaris", target_os = "illumos"),
link_name = "___errno"
)]
#[cfg_attr(
any(target_os = "macos", target_os = "ios", target_os = "freebsd"),
link_name = "__error"
)]
#[cfg_attr(target_os = "haiku", link_name = "_errnop")]
fn errno_location() -> *mut libc::c_int;
#[cfg_attr(
any(target_os = "linux", target_env = "newlib"),
link_name = "__xpg_strerror_r"
)]
fn strerror_r(errnum: libc::c_int, buf: *mut libc::c_char, buflen: libc::size_t)
-> libc::c_int;
}
#[allow(private_doc_tests)]
#[inline]
pub fn get_errno() -> i32 { unsafe { *errno_location() } }
#[allow(private_doc_tests)]
#[inline]
pub fn set_errno(errno: i32) { unsafe { *errno_location() = errno } }
#[allow(private_doc_tests)]
#[inline]
pub fn clear_errno() { set_errno(0); }
#[allow(private_doc_tests)]
#[inline]
pub fn describe_errno(errno: i32) -> std::io::Result<String> {
let mut buf: [libc::c_char; TMP_BUF_SZ] = [0; TMP_BUF_SZ];
unsafe {
if strerror_r(errno, buf.as_mut_ptr(), buf.len()) < 0 {
let e = get_errno();
if e != libc::ERANGE {
return Err(std::io::Error::from_raw_os_error(e));
}
}
Ok(
std::str::from_utf8(std::ffi::CStr::from_ptr(buf.as_ptr()).to_bytes())
.unwrap()
.to_owned(),
)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn set_get_errno() {
(-1..134).for_each(|err_code| {
set_errno(err_code);
assert_eq!(err_code, get_errno());
});
clear_errno();
}
#[test]
fn error_string() {
(1..134).for_each(|err_code| {
let err_string = describe_errno(err_code).unwrap();
let os_err = std::io::Error::from_raw_os_error(err_code);
assert_eq!(
format!("{} (os error {})", err_string, err_code),
format!("{}", os_err)
);
})
}
}