use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use crate::ported::zsh_system_h::timespec;
use std::os::unix::fs::MetadataExt;
pub fn zgettime(ts: &mut timespec) -> i32 { let mut ret: i32 = -1; unsafe {
let mut dts: timespec = std::mem::zeroed();
if libc::clock_gettime(libc::CLOCK_REALTIME, &mut dts) < 0 { crate::ported::utils::zwarn(&format!(
"unable to retrieve time: {}",
std::io::Error::last_os_error()
));
ret -= 1; } else { ret += 1; ts.tv_sec = dts.tv_sec; ts.tv_nsec = dts.tv_nsec; }
if ret != 0 { let mut dtv: libc::timeval = std::mem::zeroed(); libc::gettimeofday(&mut dtv, std::ptr::null_mut()); ret += 1; ts.tv_sec = dtv.tv_sec; ts.tv_nsec = (dtv.tv_usec as libc::c_long) * 1000; }
}
ret }
pub fn zgettime_monotonic_if_available(ts: &mut timespec) -> i32 { let mut ret: i32 = -1; unsafe {
let mut dts: timespec = std::mem::zeroed(); #[cfg(target_os = "macos")]
let clk = libc::CLOCK_MONOTONIC_RAW;
#[cfg(not(target_os = "macos"))]
let clk = libc::CLOCK_MONOTONIC;
if libc::clock_gettime(clk, &mut dts) < 0 { crate::ported::utils::zwarn(&format!(
"unable to retrieve CLOCK_MONOTONIC time: {}",
std::io::Error::last_os_error()
));
ret -= 1; } else {
ret += 1; ts.tv_sec = dts.tv_sec; ts.tv_nsec = dts.tv_nsec; }
}
if ret != 0 { ret = zgettime(ts); }
ret }
pub fn difftime(t2: i64, t1: i64) -> f64 { (t2 - t1) as f64
}
pub fn zopenmax() -> i64 { const ZSH_INITIAL_OPEN_MAX: i64 = 1024;
const OPEN_MAX: i64 = 256;
#[cfg(unix)]
{
unsafe {
let mut openmax = libc::sysconf(libc::_SC_OPEN_MAX);
if openmax < 1 {
openmax = OPEN_MAX;
} else if openmax > OPEN_MAX {
if openmax > ZSH_INITIAL_OPEN_MAX {
openmax = ZSH_INITIAL_OPEN_MAX;
}
let mut j = OPEN_MAX;
let mut i = j;
while i < openmax {
let r = libc::fcntl(i as i32, libc::F_GETFL, 0);
if r < 0 {
let e = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
if e == libc::EBADF || e == libc::EINTR {
if e != libc::EINTR {
i += 1;
}
continue;
}
}
j = i;
i += 1;
}
openmax = j;
}
openmax
}
}
#[cfg(not(unix))]
{
OPEN_MAX
}
}
pub fn zgetcwd() -> Option<String> { env::current_dir()
.ok()
.and_then(|p| p.to_str().map(|s| s.to_string()))
}
pub fn zgetdir(d: Option<&mut crate::ported::zsh_h::dirsav>) -> Option<String> { let cwd = env::current_dir().ok()?;
let cwd_str = cwd.to_str()?.to_string();
#[cfg(unix)]
if let Some(dirsav) = d {
if let Ok(meta) = fs::metadata(&cwd) {
dirsav.ino = meta.ino();
dirsav.dev = meta.dev();
}
dirsav.dirname = Some(cwd_str.clone());
}
#[cfg(not(unix))]
if let Some(dirsav) = d {
dirsav.dirname = Some(cwd_str.clone());
}
Some(cwd_str)
}
pub fn zchdir(dir: &str) -> i32 { if dir.is_empty() {
return 0;
}
if env::set_current_dir(dir).is_ok() {
return 0;
}
let path = Path::new(dir);
if !path.is_absolute() {
return -1;
}
let saved_dir = env::current_dir().ok();
let mut current = PathBuf::from("/");
for component in path.components().skip(1) {
current.push(component);
if env::set_current_dir(¤t).is_err() {
if let Some(ref saved) = saved_dir {
if env::set_current_dir(saved).is_err() {
return -2; }
}
return -1;
}
}
0
}
pub fn output64(val: i64) -> String { val.to_string()
}
pub fn isprint_ascii(c: char) -> bool { let b = c as u32;
(0x20..=0x7e).contains(&b)
}
pub fn u9_wcwidth(ucs: char) -> i32 { unicode_width::UnicodeWidthChar::width(ucs)
.map(|w| w as i32)
.unwrap_or(if ucs.is_control() { -1 } else { 1 })
}
pub fn u9_iswprint(ucs: char) -> bool { !ucs.is_control() && u9_wcwidth(ucs) >= 0
}
pub fn strerror(errnum: i32) -> String { std::io::Error::from_raw_os_error(errnum).to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zgettime() {
let mut ts: timespec = unsafe { std::mem::zeroed() };
let r = zgettime(&mut ts);
assert!(r >= 0);
assert!(ts.tv_sec > 0);
}
#[test]
fn test_zgettime_monotonic() {
let mut t1: timespec = unsafe { std::mem::zeroed() };
let mut t2: timespec = unsafe { std::mem::zeroed() };
let r1 = zgettime_monotonic_if_available(&mut t1);
std::thread::sleep(std::time::Duration::from_millis(10));
let r2 = zgettime_monotonic_if_available(&mut t2);
assert!(r1 >= 0 && r2 >= 0);
let elapsed_ns = (t2.tv_sec - t1.tv_sec) * 1_000_000_000
+ (t2.tv_nsec - t1.tv_nsec) as i64;
assert!(elapsed_ns > 0);
}
#[test]
fn test_zgetcwd() {
let cwd = zgetcwd();
assert!(cwd.is_some());
assert!(!cwd.unwrap().is_empty());
}
#[test]
fn test_zopenmax() {
let max = zopenmax();
assert!(max > 0);
}
#[test]
fn test_isprint_safe() {
assert!(isprint_ascii('a'));
assert!(isprint_ascii('Z'));
assert!(isprint_ascii(' '));
assert!(!isprint_ascii('\x00'));
assert!(!isprint_ascii('\x1f'));
}
#[test]
fn test_wcwidth() {
assert_eq!(u9_wcwidth('a'), 1);
assert_eq!(u9_wcwidth('中'), 2);
assert!(u9_wcwidth('\x00') <= 0);
}
}