use std::cmp::Ordering;
use std::{env, fs};
use crate::params::getsparam;
use crate::ported::zsh_h::dirsav;
use crate::utils::{unmeta, zwarn};
use crate::zsh_system_h::{timespec, OPEN_MAX, ZSH_INITIAL_OPEN_MAX};
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 {
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 {
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 strerror(errnum: i32) -> String {
unsafe {
let p = libc::strerror(errnum);
if p.is_null() {
return String::new();
}
match std::ffi::CStr::from_ptr(p).to_str() {
Ok(s) => s.to_string(),
Err(_) => std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned(),
}
}
}
pub fn last_errstr() -> String {
let e = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
strerror(e)
}
pub fn zopenmax() -> i64 {
#[cfg(unix)]
{
unsafe {
let mut openmax = libc::sysconf(libc::_SC_OPEN_MAX);
if openmax < 1 {
openmax = OPEN_MAX as i64;
} else if openmax > OPEN_MAX as i64 {
if openmax > ZSH_INITIAL_OPEN_MAX as i64 {
openmax = ZSH_INITIAL_OPEN_MAX as i64;
}
let mut j = OPEN_MAX as i64;
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 zgetdir(d: Option<&mut 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 zgetcwd() -> String {
if let Some(ret) = zgetdir(None) {
if !ret.is_empty() {
return ret;
}
}
if let Some(pwd) = getsparam("PWD") {
let unmeta_pwd = unmeta(&pwd); if !unmeta_pwd.is_empty() {
return unmeta_pwd;
}
}
".".to_string() }
pub fn zchdir(dir: &str) -> i32 {
#[cfg(unix)]
{
let path_max: usize = libc::PATH_MAX as usize;
let mut remaining: Vec<u8> = dir.as_bytes().to_vec();
let mut saved_currdir: i32 = -2; loop {
if remaining.is_empty() {
if saved_currdir >= 0 {
unsafe {
libc::close(saved_currdir);
}
}
return 0;
}
let c_dir = match std::ffi::CString::new(remaining.clone()) {
Ok(c) => c,
Err(_) => return -1, };
let rc = unsafe { libc::chdir(c_dir.as_ptr()) };
if rc == 0 {
if saved_currdir >= 0 {
unsafe {
libc::close(saved_currdir);
} }
return 0; }
let err = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
let ok_errno = err == libc::ENAMETOOLONG || err == libc::ENOMEM;
if !ok_errno || remaining.len() < path_max {
break;
}
let mut s_idx: isize = (path_max - 1) as isize;
while s_idx > 0 && remaining.get(s_idx as usize) != Some(&b'/') {
s_idx -= 1;
}
if s_idx == 0 {
break;
}
if saved_currdir == -2 {
let dot = std::ffi::CString::new(".").unwrap();
saved_currdir =
unsafe { libc::open(dot.as_ptr(), libc::O_RDONLY | libc::O_NOCTTY) };
}
let prefix: Vec<u8> = remaining[..s_idx as usize].to_vec();
let c_prefix = match std::ffi::CString::new(prefix) {
Ok(c) => c,
Err(_) => break,
};
if unsafe { libc::chdir(c_prefix.as_ptr()) } < 0 {
break;
}
let mut tail_start = s_idx as usize + 1;
while tail_start < remaining.len() && remaining[tail_start] == b'/' {
tail_start += 1;
}
remaining = remaining[tail_start..].to_vec();
}
if saved_currdir >= 0 {
let rc = unsafe { libc::fchdir(saved_currdir) };
unsafe {
libc::close(saved_currdir);
} if rc < 0 {
return -2; }
return -1; }
if saved_currdir == -2 {
-1
} else {
-2
} }
#[cfg(not(unix))]
{
let _ = (dir, env::set_current_dir);
if dir.is_empty() {
return 0;
}
match env::set_current_dir(dir) {
Ok(_) => 0,
Err(_) => -1,
}
}
}
pub fn output64(val: i64) -> String {
val.to_string()
}
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 })
}
const WCWIDTH9_NONPRINT: &[(u32, u32)] = &[
(0x0000, 0x001f), (0x007f, 0x009f), (0x00ad, 0x00ad), (0x070f, 0x070f), (0x180b, 0x180e), (0x200b, 0x200f), (0x2028, 0x2029), (0x202a, 0x202e), (0x206a, 0x206f), (0xfeff, 0xfeff), (0xfff9, 0xfffb), (0xfffe, 0xffff), ];
const WCWIDTH9_NOT_ASSIGNED: &[(u32, u32)] = &[
(0x0378, 0x0379), (0x0380, 0x0383), (0x038b, 0x038b), (0x038d, 0x038d), (0x03a2, 0x03a2), (0x0530, 0x0530), (0x0557, 0x0558), (0x0560, 0x0560), (0x0588, 0x0588), (0x058b, 0x058c), (0x0590, 0x0590), (0x05c8, 0x05cf), (0x05eb, 0x05ef), (0x05f5, 0x05ff), (0x061d, 0x061d), (0x070e, 0x070e), (0x074b, 0x074c), (0x07b2, 0x07bf), (0x07fb, 0x07ff), (0x082e, 0x082f), (0x083f, 0x083f), (0x085c, 0x085d), (0x085f, 0x089f), (0x08b5, 0x08b5), (0x08be, 0x08d3), (0x0984, 0x0984), (0x098d, 0x098e), (0x0991, 0x0992), (0x09a9, 0x09a9), (0x09b1, 0x09b1), (0x09b3, 0x09b5), (0x09ba, 0x09bb), (0x09c5, 0x09c6), (0x09c9, 0x09ca), (0x09cf, 0x09d6), (0x09d8, 0x09db), (0x09de, 0x09de), (0x09e4, 0x09e5), (0x09fc, 0x0a00), (0x0a04, 0x0a04), (0x0a0b, 0x0a0e), (0x0a11, 0x0a12), (0x0a29, 0x0a29), (0x0a31, 0x0a31), (0x0a34, 0x0a34), (0x0a37, 0x0a37), (0x0a3a, 0x0a3b), (0x0a3d, 0x0a3d), (0x0a43, 0x0a46), (0x0a49, 0x0a4a), (0x0a4e, 0x0a50), (0x0a52, 0x0a58), (0x0a5d, 0x0a5d), (0x0a5f, 0x0a65), (0x0a76, 0x0a80), (0x0a84, 0x0a84), (0x0a8e, 0x0a8e), (0x0a92, 0x0a92), (0x0aa9, 0x0aa9), (0x0ab1, 0x0ab1), (0x0ab4, 0x0ab4), (0x0aba, 0x0abb), (0x0ac6, 0x0ac6), (0x0aca, 0x0aca), (0x0ace, 0x0acf), (0x0ad1, 0x0adf), (0x0ae4, 0x0ae5), (0x0af2, 0x0af8), (0x0afa, 0x0b00), (0x0b04, 0x0b04), (0x0b0d, 0x0b0e), (0x0b11, 0x0b12), (0x0b29, 0x0b29), (0x0b31, 0x0b31), (0x0b34, 0x0b34), (0x0b3a, 0x0b3b), (0x0b45, 0x0b46), (0x0b49, 0x0b4a), (0x0b4e, 0x0b55), (0x0b58, 0x0b5b), (0x0b5e, 0x0b5e), (0x0b64, 0x0b65), (0x0b78, 0x0b81), (0x0b84, 0x0b84), (0x0b8b, 0x0b8d), (0x0b91, 0x0b91), (0x0b96, 0x0b98), (0x0b9b, 0x0b9b), (0x0b9d, 0x0b9d), (0x0ba0, 0x0ba2), (0x0ba5, 0x0ba7), (0x0bab, 0x0bad), (0x0bba, 0x0bbd), (0x0bc3, 0x0bc5), (0x0bc9, 0x0bc9), (0x0bce, 0x0bcf), (0x0bd1, 0x0bd6), (0x0bd8, 0x0be5), (0x0bfb, 0x0bff), (0x0c04, 0x0c04), (0x0c0d, 0x0c0d), (0x0c11, 0x0c11), (0x0c29, 0x0c29), (0x0c3a, 0x0c3c), (0x0c45, 0x0c45), (0x0c49, 0x0c49), (0x0c4e, 0x0c54), (0x0c57, 0x0c57), (0x0c5b, 0x0c5f), (0x0c64, 0x0c65), (0x0c70, 0x0c77), (0x0c84, 0x0c84), (0x0c8d, 0x0c8d), (0x0c91, 0x0c91), (0x0ca9, 0x0ca9), (0x0cb4, 0x0cb4), (0x0cba, 0x0cbb), (0x0cc5, 0x0cc5), (0x0cc9, 0x0cc9), (0x0cce, 0x0cd4), (0x0cd7, 0x0cdd), (0x0cdf, 0x0cdf), (0x0ce4, 0x0ce5), (0x0cf0, 0x0cf0), (0x0cf3, 0x0d00), (0x0d04, 0x0d04), (0x0d0d, 0x0d0d), (0x0d11, 0x0d11), (0x0d3b, 0x0d3c), (0x0d45, 0x0d45), (0x0d49, 0x0d49), (0x0d50, 0x0d53), (0x0d64, 0x0d65), (0x0d80, 0x0d81), (0x0d84, 0x0d84), (0x0d97, 0x0d99), (0x0db2, 0x0db2), (0x0dbc, 0x0dbc), (0x0dbe, 0x0dbf), (0x0dc7, 0x0dc9), (0x0dcb, 0x0dce), (0x0dd5, 0x0dd5), (0x0dd7, 0x0dd7), (0x0de0, 0x0de5), (0x0df0, 0x0df1), (0x0df5, 0x0e00), (0x0e3b, 0x0e3e), (0x0e5c, 0x0e80), (0x0e83, 0x0e83), (0x0e85, 0x0e86), (0x0e89, 0x0e89), (0x0e8b, 0x0e8c), (0x0e8e, 0x0e93), (0x0e98, 0x0e98), (0x0ea0, 0x0ea0), (0x0ea4, 0x0ea4), (0x0ea6, 0x0ea6), (0x0ea8, 0x0ea9), (0x0eac, 0x0eac), (0x0eba, 0x0eba), (0x0ebe, 0x0ebf), (0x0ec5, 0x0ec5), (0x0ec7, 0x0ec7), (0x0ece, 0x0ecf), (0x0eda, 0x0edb), (0x0ee0, 0x0eff), (0x0f48, 0x0f48), (0x0f6d, 0x0f70), (0x0f98, 0x0f98), (0x0fbd, 0x0fbd), (0x0fcd, 0x0fcd), (0x0fdb, 0x0fff), (0x10c6, 0x10c6), (0x10c8, 0x10cc), (0x10ce, 0x10cf), (0x1249, 0x1249), (0x124e, 0x124f), (0x1257, 0x1257), (0x1259, 0x1259), (0x125e, 0x125f), (0x1289, 0x1289), (0x128e, 0x128f), (0x12b1, 0x12b1), (0x12b6, 0x12b7), (0x12bf, 0x12bf), (0x12c1, 0x12c1), (0x12c6, 0x12c7), (0x12d7, 0x12d7), (0x1311, 0x1311), (0x1316, 0x1317), (0x135b, 0x135c), (0x137d, 0x137f), (0x139a, 0x139f), (0x13f6, 0x13f7), (0x13fe, 0x13ff), (0x169d, 0x169f), (0x16f9, 0x16ff), (0x170d, 0x170d), (0x1715, 0x171f), (0x1737, 0x173f), (0x1754, 0x175f), (0x176d, 0x176d), (0x1771, 0x1771), (0x1774, 0x177f), (0x17de, 0x17df), (0x17ea, 0x17ef), (0x17fa, 0x17ff), (0x180f, 0x180f), (0x181a, 0x181f), (0x1878, 0x187f), (0x18ab, 0x18af), (0x18f6, 0x18ff), (0x191f, 0x191f), (0x192c, 0x192f), (0x193c, 0x193f), (0x1941, 0x1943), (0x196e, 0x196f), (0x1975, 0x197f), (0x19ac, 0x19af), (0x19ca, 0x19cf), (0x19db, 0x19dd), (0x1a1c, 0x1a1d), (0x1a5f, 0x1a5f), (0x1a7d, 0x1a7e), (0x1a8a, 0x1a8f), (0x1a9a, 0x1a9f), (0x1aae, 0x1aaf), (0x1abf, 0x1aff), (0x1b4c, 0x1b4f), (0x1b7d, 0x1b7f), (0x1bf4, 0x1bfb), (0x1c38, 0x1c3a), (0x1c4a, 0x1c4c), (0x1c89, 0x1cbf), (0x1cc8, 0x1ccf), (0x1cf7, 0x1cf7), (0x1cfa, 0x1cff), (0x1df6, 0x1dfa), (0x1f16, 0x1f17), (0x1f1e, 0x1f1f), (0x1f46, 0x1f47), (0x1f4e, 0x1f4f), (0x1f58, 0x1f58), (0x1f5a, 0x1f5a), (0x1f5c, 0x1f5c), (0x1f5e, 0x1f5e), (0x1f7e, 0x1f7f), (0x1fb5, 0x1fb5), (0x1fc5, 0x1fc5), (0x1fd4, 0x1fd5), (0x1fdc, 0x1fdc), (0x1ff0, 0x1ff1), (0x1ff5, 0x1ff5), (0x1fff, 0x1fff), (0x2065, 0x2065), (0x2072, 0x2073), (0x208f, 0x208f), (0x209d, 0x209f), (0x20bf, 0x20cf), (0x20f1, 0x20ff), (0x218c, 0x218f), (0x23ff, 0x23ff), (0x2427, 0x243f), (0x244b, 0x245f), (0x2b74, 0x2b75), (0x2b96, 0x2b97), (0x2bba, 0x2bbc), (0x2bc9, 0x2bc9), (0x2bd2, 0x2beb), (0x2bf0, 0x2bff), (0x2c2f, 0x2c2f), (0x2c5f, 0x2c5f), (0x2cf4, 0x2cf8), (0x2d26, 0x2d26), (0x2d28, 0x2d2c), (0x2d2e, 0x2d2f), (0x2d68, 0x2d6e), (0x2d71, 0x2d7e), (0x2d97, 0x2d9f), (0x2da7, 0x2da7), (0x2daf, 0x2daf), (0x2db7, 0x2db7), (0x2dbf, 0x2dbf), (0x2dc7, 0x2dc7), (0x2dcf, 0x2dcf), (0x2dd7, 0x2dd7), (0x2ddf, 0x2ddf), (0x2e45, 0x2e7f), (0x2e9a, 0x2e9a), (0x2ef4, 0x2eff), (0x2fd6, 0x2fef), (0x2ffc, 0x2fff), (0x3040, 0x3040), (0x3097, 0x3098), (0x3100, 0x3104), (0x312e, 0x3130), (0x318f, 0x318f), (0x31bb, 0x31bf), (0x31e4, 0x31ef), (0x321f, 0x321f), (0x32ff, 0x32ff), (0x4db6, 0x4dbf), (0x9fd6, 0x9fff), (0xa48d, 0xa48f), (0xa4c7, 0xa4cf), (0xa62c, 0xa63f), (0xa6f8, 0xa6ff), (0xa7af, 0xa7af), (0xa7b8, 0xa7f6), (0xa82c, 0xa82f), (0xa83a, 0xa83f), (0xa878, 0xa87f), (0xa8c6, 0xa8cd), (0xa8da, 0xa8df), (0xa8fe, 0xa8ff), (0xa954, 0xa95e), (0xa97d, 0xa97f), (0xa9ce, 0xa9ce), (0xa9da, 0xa9dd), (0xa9ff, 0xa9ff), (0xaa37, 0xaa3f), (0xaa4e, 0xaa4f), (0xaa5a, 0xaa5b), (0xaac3, 0xaada), (0xaaf7, 0xab00), (0xab07, 0xab08), (0xab0f, 0xab10), (0xab17, 0xab1f), (0xab27, 0xab27), (0xab2f, 0xab2f), (0xab66, 0xab6f), (0xabee, 0xabef), (0xabfa, 0xabff), (0xd7a4, 0xd7af), (0xd7c7, 0xd7ca), (0xd7fc, 0xd7ff), (0xfa6e, 0xfa6f), (0xfada, 0xfaff), (0xfb07, 0xfb12), (0xfb18, 0xfb1c), (0xfb37, 0xfb37), (0xfb3d, 0xfb3d), (0xfb3f, 0xfb3f), (0xfb42, 0xfb42), (0xfb45, 0xfb45), (0xfbc2, 0xfbd2), (0xfd40, 0xfd4f), (0xfd90, 0xfd91), (0xfdc8, 0xfdef), (0xfdfe, 0xfdff), (0xfe1a, 0xfe1f), (0xfe53, 0xfe53), (0xfe67, 0xfe67), (0xfe6c, 0xfe6f), (0xfe75, 0xfe75), (0xfefd, 0xfefe), (0xff00, 0xff00), (0xffbf, 0xffc1), (0xffc8, 0xffc9), (0xffd0, 0xffd1), (0xffd8, 0xffd9), (0xffdd, 0xffdf), (0xffe7, 0xffe7), (0xffef, 0xfff8), (0xfffe, 0xffff), (0x1000c, 0x1000c), (0x10027, 0x10027), (0x1003b, 0x1003b), (0x1003e, 0x1003e), (0x1004e, 0x1004f), (0x1005e, 0x1007f), (0x100fb, 0x100ff), (0x10103, 0x10106), (0x10134, 0x10136), (0x1018f, 0x1018f), (0x1019c, 0x1019f), (0x101a1, 0x101cf), (0x101fe, 0x1027f), (0x1029d, 0x1029f), (0x102d1, 0x102df), (0x102fc, 0x102ff), (0x10324, 0x1032f), (0x1034b, 0x1034f), (0x1037b, 0x1037f), (0x1039e, 0x1039e), (0x103c4, 0x103c7), (0x103d6, 0x103ff), (0x1049e, 0x1049f), (0x104aa, 0x104af), (0x104d4, 0x104d7), (0x104fc, 0x104ff), (0x10528, 0x1052f), (0x10564, 0x1056e), (0x10570, 0x105ff), (0x10737, 0x1073f), (0x10756, 0x1075f), (0x10768, 0x107ff), (0x10806, 0x10807), (0x10809, 0x10809), (0x10836, 0x10836), (0x10839, 0x1083b), (0x1083d, 0x1083e), (0x10856, 0x10856), (0x1089f, 0x108a6), (0x108b0, 0x108df), (0x108f3, 0x108f3), (0x108f6, 0x108fa), (0x1091c, 0x1091e), (0x1093a, 0x1093e), (0x10940, 0x1097f), (0x109b8, 0x109bb), (0x109d0, 0x109d1), (0x10a04, 0x10a04), (0x10a07, 0x10a0b), (0x10a14, 0x10a14), (0x10a18, 0x10a18), (0x10a34, 0x10a37), (0x10a3b, 0x10a3e), (0x10a48, 0x10a4f), (0x10a59, 0x10a5f), (0x10aa0, 0x10abf), (0x10ae7, 0x10aea), (0x10af7, 0x10aff), (0x10b36, 0x10b38), (0x10b56, 0x10b57), (0x10b73, 0x10b77), (0x10b92, 0x10b98), (0x10b9d, 0x10ba8), (0x10bb0, 0x10bff), (0x10c49, 0x10c7f), (0x10cb3, 0x10cbf), (0x10cf3, 0x10cf9), (0x10d00, 0x10e5f), (0x10e7f, 0x10fff), (0x1104e, 0x11051), (0x11070, 0x1107e), (0x110c2, 0x110cf), (0x110e9, 0x110ef), (0x110fa, 0x110ff), (0x11135, 0x11135), (0x11144, 0x1114f), (0x11177, 0x1117f), (0x111ce, 0x111cf), (0x111e0, 0x111e0), (0x111f5, 0x111ff), (0x11212, 0x11212), (0x1123f, 0x1127f), (0x11287, 0x11287), (0x11289, 0x11289), (0x1128e, 0x1128e), (0x1129e, 0x1129e), (0x112aa, 0x112af), (0x112eb, 0x112ef), (0x112fa, 0x112ff), (0x11304, 0x11304), (0x1130d, 0x1130e), (0x11311, 0x11312), (0x11329, 0x11329), (0x11331, 0x11331), (0x11334, 0x11334), (0x1133a, 0x1133b), (0x11345, 0x11346), (0x11349, 0x1134a), (0x1134e, 0x1134f), (0x11351, 0x11356), (0x11358, 0x1135c), (0x11364, 0x11365), (0x1136d, 0x1136f), (0x11375, 0x113ff), (0x1145a, 0x1145a), (0x1145c, 0x1145c), (0x1145e, 0x1147f), (0x114c8, 0x114cf), (0x114da, 0x1157f), (0x115b6, 0x115b7), (0x115de, 0x115ff), (0x11645, 0x1164f), (0x1165a, 0x1165f), (0x1166d, 0x1167f), (0x116b8, 0x116bf), (0x116ca, 0x116ff), (0x1171a, 0x1171c), (0x1172c, 0x1172f), (0x11740, 0x1189f), (0x118f3, 0x118fe), (0x11900, 0x11abf), (0x11af9, 0x11bff), (0x11c09, 0x11c09), (0x11c37, 0x11c37), (0x11c46, 0x11c4f), (0x11c6d, 0x11c6f), (0x11c90, 0x11c91), (0x11ca8, 0x11ca8), (0x11cb7, 0x11fff), (0x1239a, 0x123ff), (0x1246f, 0x1246f), (0x12475, 0x1247f), (0x12544, 0x12fff), (0x1342f, 0x143ff), (0x14647, 0x167ff), (0x16a39, 0x16a3f), (0x16a5f, 0x16a5f), (0x16a6a, 0x16a6d), (0x16a70, 0x16acf), (0x16aee, 0x16aef), (0x16af6, 0x16aff), (0x16b46, 0x16b4f), (0x16b5a, 0x16b5a), (0x16b62, 0x16b62), (0x16b78, 0x16b7c), (0x16b90, 0x16eff), (0x16f45, 0x16f4f), (0x16f7f, 0x16f8e), (0x16fa0, 0x16fdf), (0x16fe1, 0x16fff), (0x187ed, 0x187ff), (0x18af3, 0x1afff), (0x1b002, 0x1bbff), (0x1bc6b, 0x1bc6f), (0x1bc7d, 0x1bc7f), (0x1bc89, 0x1bc8f), (0x1bc9a, 0x1bc9b), (0x1bca4, 0x1cfff), (0x1d0f6, 0x1d0ff), (0x1d127, 0x1d128), (0x1d1e9, 0x1d1ff), (0x1d246, 0x1d2ff), (0x1d357, 0x1d35f), (0x1d372, 0x1d3ff), (0x1d455, 0x1d455), (0x1d49d, 0x1d49d), (0x1d4a0, 0x1d4a1), (0x1d4a3, 0x1d4a4), (0x1d4a7, 0x1d4a8), (0x1d4ad, 0x1d4ad), (0x1d4ba, 0x1d4ba), (0x1d4bc, 0x1d4bc), (0x1d4c4, 0x1d4c4), (0x1d506, 0x1d506), (0x1d50b, 0x1d50c), (0x1d515, 0x1d515), (0x1d51d, 0x1d51d), (0x1d53a, 0x1d53a), (0x1d53f, 0x1d53f), (0x1d545, 0x1d545), (0x1d547, 0x1d549), (0x1d551, 0x1d551), (0x1d6a6, 0x1d6a7), (0x1d7cc, 0x1d7cd), (0x1da8c, 0x1da9a), (0x1daa0, 0x1daa0), (0x1dab0, 0x1dfff), (0x1e007, 0x1e007), (0x1e019, 0x1e01a), (0x1e022, 0x1e022), (0x1e025, 0x1e025), (0x1e02b, 0x1e7ff), (0x1e8c5, 0x1e8c6), (0x1e8d7, 0x1e8ff), (0x1e94b, 0x1e94f), (0x1e95a, 0x1e95d), (0x1e960, 0x1edff), (0x1ee04, 0x1ee04), (0x1ee20, 0x1ee20), (0x1ee23, 0x1ee23), (0x1ee25, 0x1ee26), (0x1ee28, 0x1ee28), (0x1ee33, 0x1ee33), (0x1ee38, 0x1ee38), (0x1ee3a, 0x1ee3a), (0x1ee3c, 0x1ee41), (0x1ee43, 0x1ee46), (0x1ee48, 0x1ee48), (0x1ee4a, 0x1ee4a), (0x1ee4c, 0x1ee4c), (0x1ee50, 0x1ee50), (0x1ee53, 0x1ee53), (0x1ee55, 0x1ee56), (0x1ee58, 0x1ee58), (0x1ee5a, 0x1ee5a), (0x1ee5c, 0x1ee5c), (0x1ee5e, 0x1ee5e), (0x1ee60, 0x1ee60), (0x1ee63, 0x1ee63), (0x1ee65, 0x1ee66), (0x1ee6b, 0x1ee6b), (0x1ee73, 0x1ee73), (0x1ee78, 0x1ee78), (0x1ee7d, 0x1ee7d), (0x1ee7f, 0x1ee7f), (0x1ee8a, 0x1ee8a), (0x1ee9c, 0x1eea0), (0x1eea4, 0x1eea4), (0x1eeaa, 0x1eeaa), (0x1eebc, 0x1eeef), (0x1eef2, 0x1efff), (0x1f02c, 0x1f02f), (0x1f094, 0x1f09f), (0x1f0af, 0x1f0b0), (0x1f0c0, 0x1f0c0), (0x1f0d0, 0x1f0d0), (0x1f0f6, 0x1f0ff), (0x1f10d, 0x1f10f), (0x1f12f, 0x1f12f), (0x1f16c, 0x1f16f), (0x1f1ad, 0x1f1e5), (0x1f203, 0x1f20f), (0x1f23c, 0x1f23f), (0x1f249, 0x1f24f), (0x1f252, 0x1f2ff), (0x1f6d3, 0x1f6df), (0x1f6ed, 0x1f6ef), (0x1f6f7, 0x1f6ff), (0x1f774, 0x1f77f), (0x1f7d5, 0x1f7ff), (0x1f80c, 0x1f80f), (0x1f848, 0x1f84f), (0x1f85a, 0x1f85f), (0x1f888, 0x1f88f), (0x1f8ae, 0x1f90f), (0x1f91f, 0x1f91f), (0x1f928, 0x1f92f), (0x1f931, 0x1f932), (0x1f93f, 0x1f93f), (0x1f94c, 0x1f94f), (0x1f95f, 0x1f97f), (0x1f992, 0x1f9bf), (0x1f9c1, 0x1ffff), (0x2a6d7, 0x2a6ff), (0x2b735, 0x2b73f), (0x2b81e, 0x2b81f), (0x2cea2, 0x2f7ff), (0x2fa1e, 0xe0000), (0xe0002, 0xe001f), (0xe0080, 0xe00ff), (0xe01f0, 0xeffff), (0xffffe, 0xfffff), ];
pub fn u9_iswprint(ucs: char) -> bool {
if ucs == '\0' {
return false;
}
let cp = ucs as u32;
let wcwidth9_intable = |table: &[(u32, u32)]| {
table
.binary_search_by(|&(first, last)| {
if last < cp {
Ordering::Less } else if first > cp {
Ordering::Greater } else {
Ordering::Equal }
})
.is_ok()
};
!(wcwidth9_intable(WCWIDTH9_NONPRINT) || wcwidth9_intable(WCWIDTH9_NOT_ASSIGNED))
}
pub fn isprint_ascii(c: char) -> bool {
let b = c as u32;
(0x20..=0x7e).contains(&b)
}
pub fn strstr(s: &str, t: &str) -> Option<usize> {
s.find(t) }
pub fn gettimeofday() -> (i64, i64) {
#[cfg(unix)]
{
let mut tv: libc::timeval = unsafe { std::mem::zeroed() };
unsafe {
libc::gettimeofday(&mut tv, std::ptr::null_mut());
} (tv.tv_sec as i64, tv.tv_usec as i64)
}
#[cfg(not(unix))]
{
(0, 0)
}
}
pub fn strtoul(nptr: &str, base: u32) -> (u64, usize) {
let bytes = nptr.as_bytes();
let mut i = 0;
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
i += 1;
} let neg = i < bytes.len() && bytes[i] == b'-'; if neg || (i < bytes.len() && bytes[i] == b'+') {
i += 1;
} let (radix, start) = if (base == 0 || base == 16)
&& bytes.get(i).copied() == Some(b'0')
&& bytes
.get(i + 1)
.map(|b| b.eq_ignore_ascii_case(&b'x'))
.unwrap_or(false)
{
(16u32, i + 2) } else if base == 0 {
(
if bytes.get(i).copied() == Some(b'0') {
8
} else {
10
},
i,
) } else {
(base, i)
};
let mut acc: u64 = 0;
let mut consumed = start;
for &b in &bytes[start..] {
let digit = if b.is_ascii_digit() {
(b - b'0') as u32
} else if b.is_ascii_uppercase() {
(b - b'A' + 10) as u32
} else if b.is_ascii_lowercase() {
(b - b'a' + 10) as u32
} else {
break;
};
if digit >= radix {
break;
}
acc = acc
.saturating_mul(radix as u64)
.saturating_add(digit as u64);
consumed += 1;
}
(if neg { acc.wrapping_neg() } else { acc }, consumed)
}
pub fn zpathmax(dir: &str) -> i64 {
#[cfg(unix)]
unsafe {
let mut buf: Vec<u8> = dir.as_bytes().to_vec(); #[cfg(target_os = "macos")]
let errno_loc: *mut libc::c_int = libc::__error();
#[cfg(target_os = "linux")]
let errno_loc: *mut libc::c_int = libc::__errno_location();
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
let errno_loc: *mut libc::c_int = std::ptr::null_mut();
if errno_loc.is_null() {
let dirlen = buf.len() as i64;
let path_max = 4096i64;
return if dirlen >= path_max {
-1
} else {
path_max - dirlen
};
}
let mut accumulated_taillen: libc::c_long = 0; loop {
let cs = match std::ffi::CString::new(buf.clone()) {
Ok(c) => c,
Err(_) => return -1,
};
*errno_loc = 0; let pathmax = libc::pathconf(cs.as_ptr(), libc::_PC_PATH_MAX); if pathmax >= 0 {
if accumulated_taillen == 0 {
return pathmax as i64; }
if accumulated_taillen < pathmax {
return (pathmax - accumulated_taillen) as i64; } else {
*errno_loc = libc::ENAMETOOLONG; return -1;
}
}
let err = *errno_loc;
if err != libc::EINVAL && err != libc::ENOENT && err != libc::ENOTDIR {
return if *errno_loc != 0 { -1 } else { 0 }; }
let tail_pos: Option<usize> = buf.iter().rposition(|&b| b == b'/');
let mut tail = match tail_pos {
Some(t) => t,
None => {
*errno_loc = 0;
let dot = std::ffi::CString::new(".").unwrap();
let pm = libc::pathconf(dot.as_ptr(), libc::_PC_PATH_MAX);
let taillen = (buf.len() + 1) as libc::c_long;
if pm > 0 && taillen < pm {
return (pm - taillen) as i64; }
if pm > 0 {
*errno_loc = libc::ENAMETOOLONG;
} return if *errno_loc != 0 { -1 } else { 0 }; }
};
while tail > 0 && buf[tail - 1] == b'/' {
tail -= 1;
} let taillen_now = (buf.len() - tail) as libc::c_long; accumulated_taillen += taillen_now;
if tail > 0 {
buf.truncate(tail); continue;
} else {
*errno_loc = 0;
let root = std::ffi::CString::new("/").unwrap();
let pm = libc::pathconf(root.as_ptr(), libc::_PC_PATH_MAX);
if pm > 0 && accumulated_taillen < pm {
return (pm - accumulated_taillen) as i64; }
if pm > 0 {
*errno_loc = libc::ENAMETOOLONG;
} return if *errno_loc != 0 { -1 } else { 0 }; }
}
}
#[cfg(not(unix))]
{
let dirlen = dir.len() as i64;
let path_max = 4096i64;
if dirlen >= path_max {
-1
} else {
path_max - dirlen
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zgettime() {
let _g = crate::test_util::global_state_lock();
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 _g = crate::test_util::global_state_lock();
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 _g = crate::test_util::global_state_lock();
let cwd = zgetcwd();
assert!(!cwd.is_empty(), "c:564-565 — zgetcwd never returns empty");
}
#[test]
fn test_zopenmax() {
let _g = crate::test_util::global_state_lock();
let max = zopenmax();
assert!(max > 0);
}
#[test]
fn test_isprint_safe() {
let _g = crate::test_util::global_state_lock();
assert!(isprint_ascii('a'));
assert!(isprint_ascii('Z'));
assert!(isprint_ascii(' '));
assert!(!isprint_ascii('\x00'));
assert!(!isprint_ascii('\x1f'));
}
#[test]
fn test_wcwidth() {
let _g = crate::test_util::global_state_lock();
assert_eq!(u9_wcwidth('a'), 1);
assert_eq!(u9_wcwidth('中'), 2);
assert!(u9_wcwidth('\x00') <= 0);
}
#[test]
fn strstr_substring_hit_returns_byte_offset() {
let _g = crate::test_util::global_state_lock();
assert_eq!(strstr("hello world", "world"), Some(6));
assert_eq!(strstr("hello world", "hello"), Some(0));
assert_eq!(strstr("hello world", "xyz"), None);
assert_eq!(strstr("", "x"), None);
assert_eq!(strstr("anything", ""), Some(0));
}
#[cfg(unix)]
#[test]
fn gettimeofday_returns_positive_secs() {
let _g = crate::test_util::global_state_lock();
let (sec, _usec) = gettimeofday();
assert!(sec > 1_000_000_000, "epoch seconds should be past 2001");
}
#[test]
fn strtoul_parses_decimal() {
let _g = crate::test_util::global_state_lock();
let (v, n) = strtoul("12345", 10);
assert_eq!(v, 12345);
assert_eq!(n, 5);
}
#[test]
fn strtoul_parses_hex_with_0x_prefix_when_base_zero() {
let _g = crate::test_util::global_state_lock();
let (v, n) = strtoul("0xff", 0);
assert_eq!(v, 255);
assert_eq!(n, 4);
}
#[test]
fn strtoul_parses_octal_when_base_zero_with_leading_zero() {
let _g = crate::test_util::global_state_lock();
let (v, _n) = strtoul("0777", 0);
assert_eq!(v, 511);
}
#[test]
fn strtoul_skips_leading_whitespace() {
let _g = crate::test_util::global_state_lock();
let (v, _) = strtoul(" 42", 10);
assert_eq!(v, 42);
}
#[test]
fn strtoul_stops_at_first_non_digit() {
let _g = crate::test_util::global_state_lock();
let (v, n) = strtoul("100abc", 10);
assert_eq!(v, 100);
assert_eq!(n, 3);
}
#[test]
fn difftime_returns_signed_double_difference() {
let _g = crate::test_util::global_state_lock();
assert_eq!(difftime(1_700_000_010, 1_700_000_000), 10.0);
assert_eq!(
difftime(1_700_000_000, 1_700_000_010),
-10.0,
"c:178 — signed cast; t1 > t2 must be negative"
);
assert_eq!(difftime(42, 42), 0.0);
}
#[test]
fn isprint_ascii_matches_strict_ascii_printable_range() {
let _g = crate::test_util::global_state_lock();
assert!(isprint_ascii(' '), "c:786 — 0x20 is printable");
assert!(isprint_ascii('~'), "c:786 — 0x7e is printable");
assert!(!isprint_ascii('\x1f'), "c:786 — 0x1f is NOT printable");
assert!(!isprint_ascii('\x7f'), "c:786 — DEL is NOT printable");
assert!(isprint_ascii('A'));
assert!(isprint_ascii('0'));
assert!(isprint_ascii('!'));
assert!(!isprint_ascii('\t'));
assert!(!isprint_ascii('\n'));
assert!(!isprint_ascii('\0'));
assert!(!isprint_ascii('é'), "c:786 — non-ASCII outside range");
assert!(!isprint_ascii('字'), "c:786 — wide char outside range");
}
#[test]
fn output64_formats_i64_boundaries_and_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(output64(0), "0");
assert_eq!(output64(42), "42");
assert_eq!(output64(-1), "-1");
assert_eq!(output64(i64::MAX), "9223372036854775807");
assert_eq!(output64(i64::MIN), "-9223372036854775808");
}
#[test]
fn u9_iswprint_accepts_printable_rejects_controls() {
let _g = crate::test_util::global_state_lock();
assert!(u9_iswprint('a'));
assert!(u9_iswprint(' '));
assert!(u9_iswprint('é'), "Latin-1 letter is printable");
assert!(u9_iswprint('字'), "CJK ideograph is printable");
assert!(!u9_iswprint('\0'));
assert!(!u9_iswprint('\t'));
assert!(!u9_iswprint('\n'));
assert!(!u9_iswprint('\x07'));
assert!(!u9_iswprint('\x1b'));
assert!(!u9_iswprint('\x7f'), "DEL is a C0 control");
}
#[test]
fn u9_iswprint_rejects_unassigned_codepoints() {
let _g = crate::test_util::global_state_lock();
for cp in [
0x0378_u32, 0x0381, 0x0530, 0x083f, 0x0ab4, 0x0cda, 0x1759, 0x2daf, 0xfe1c, 0x10aaf, 0x16a3c, 0x1e95b, 0x1f0d0, 0x1f91f, 0xffffe, ] {
let c = char::from_u32(cp).unwrap();
assert!(
!u9_iswprint(c),
"c:1302-1304 — U+{:04X} is in wcwidth9_not_assigned, so \
wcwidth9() is -1 and u9_iswprint() must be false",
cp
);
}
for cp in [
0x0377_u32, 0x037f, 0x052f, 0x083e, 0x0ab3, 0x0cd6, 0x1753, 0x2dae, 0xfe19, 0x10a9f,
0x16a38, 0x1e959, 0x1f0cf, 0x1f91e, 0xffffd,
] {
let c = char::from_u32(cp).unwrap();
assert!(
u9_iswprint(c),
"c:774 — U+{:04X} is outside both -1 tables, so wcwidth9() \
is not -1 and u9_iswprint() must be true",
cp
);
}
}
#[test]
fn wcwidth9_minus_one_tables_are_sorted_and_disjoint() {
for (name, table) in [
("wcwidth9_nonprint", WCWIDTH9_NONPRINT),
("wcwidth9_not_assigned", WCWIDTH9_NOT_ASSIGNED),
] {
for w in table.windows(2) {
assert!(
w[0].0 <= w[0].1 && w[0].1 < w[1].0,
"c:1262-1284 — {} must be sorted and disjoint, but \
({:#x},{:#x}) is followed by ({:#x},{:#x})",
name,
w[0].0,
w[0].1,
w[1].0,
w[1].1
);
}
}
assert_eq!(
WCWIDTH9_NOT_ASSIGNED.len(),
637,
"Src/wcwidth9.h:620-1258 holds 637 intervals"
);
}
#[test]
fn u9_wcwidth_returns_canonical_widths() {
let _g = crate::test_util::global_state_lock();
assert_eq!(u9_wcwidth('\x07'), -1);
assert_eq!(u9_wcwidth('a'), 1);
assert_eq!(u9_wcwidth(' '), 1);
assert_eq!(u9_wcwidth('字'), 2);
assert_eq!(u9_wcwidth('\u{0301}'), 0);
}
#[test]
fn strerror_returns_non_empty_string_for_known_errno() {
let _g = crate::test_util::global_state_lock();
let s = strerror(2 );
assert!(
!s.is_empty(),
"c:194 — strerror must return non-empty for ENOENT"
);
}
#[test]
fn zopenmax_caps_within_canonical_ladder() {
let _g = crate::test_util::global_state_lock();
assert_eq!(
ZSH_INITIAL_OPEN_MAX, 64,
"Src/zsh_system.h:307 — ZSH_INITIAL_OPEN_MAX must be 64"
);
let m = zopenmax();
assert!(m > 0, "c:307 — zopenmax must report a positive ceiling");
}
#[test]
fn zgetcwd_always_returns_non_empty() {
let _g = crate::test_util::global_state_lock();
let cwd = zgetcwd();
assert!(
!cwd.is_empty(),
"c:564-565 — zgetcwd must NEVER return empty (falls through to dupstring(\".\"))"
);
#[cfg(unix)]
{
assert!(
cwd.starts_with('/') || cwd == ".",
"c:561 — zgetdir(NULL) returns absolute path, or c:565 fallback `.`"
);
}
}
#[test]
fn zchdir_empty_path_returns_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(zchdir(""), 0, "c:585 — empty dir short-circuits to success");
}
#[test]
fn zchdir_existing_path_succeeds_without_fallback() {
let _g = crate::test_util::global_state_lock();
let saved = env::current_dir().unwrap();
let rc = zchdir("/");
assert_eq!(rc, 0, "c:585 — zchdir(\"/\") direct success");
env::set_current_dir(&saved).unwrap();
}
#[test]
fn zchdir_nonexistent_path_returns_minus_one_without_fallback() {
let _g = crate::test_util::global_state_lock();
let saved = env::current_dir().unwrap();
let rc = zchdir("/tmp/this_zshrs_test_path_does_not_exist_xyz_abc");
assert_eq!(
rc, -1,
"c:592-594 — non-ENAMETOOLONG failure breaks loop, returns -1"
);
assert_eq!(
env::current_dir().unwrap(),
saved,
"no chdir side-effect on non-recoverable failure"
);
}
#[test]
fn compat_corpus_output64_zero() {
assert_eq!(output64(0), "0");
}
#[test]
fn compat_corpus_output64_positive() {
assert_eq!(output64(42), "42");
assert_eq!(output64(1234567890), "1234567890");
}
#[test]
fn compat_corpus_output64_int_max() {
assert_eq!(output64(i64::MAX), i64::MAX.to_string());
}
#[test]
fn compat_corpus_output64_negative() {
assert_eq!(output64(-42), "-42");
assert_eq!(output64(i64::MIN), i64::MIN.to_string());
}
#[test]
fn compat_corpus_strstr_finds_substring() {
assert_eq!(strstr("hello world", "world"), Some(6));
}
#[test]
fn compat_corpus_strstr_empty_needle() {
let r = strstr("hello", "");
assert_eq!(r, Some(0), "empty needle matches at position 0");
}
#[test]
fn compat_corpus_strstr_missing_returns_none() {
assert_eq!(strstr("hello world", "zzz"), None);
}
#[test]
fn compat_corpus_strtoul_decimal() {
let (val, consumed) = strtoul("42", 10);
assert_eq!(val, 42);
assert_eq!(consumed, 2, "consumed 2 chars");
}
#[test]
fn compat_corpus_strtoul_hex() {
let (val, _) = strtoul("ff", 16);
assert_eq!(val, 255);
}
#[test]
fn compat_corpus_strtoul_stops_at_nondigit() {
let (val, consumed) = strtoul("123abc", 10);
assert_eq!(val, 123);
assert_eq!(consumed, 3, "stopped at non-digit");
}
#[test]
fn compat_corpus_difftime_positive() {
let d = difftime(100, 60);
assert!((d - 40.0).abs() < 1e-9, "100 - 60 = 40, got {d}");
}
#[test]
fn compat_corpus_difftime_negative() {
let d = difftime(60, 100);
assert!((d + 40.0).abs() < 1e-9, "60 - 100 = -40, got {d}");
}
#[test]
fn compat_corpus_isprint_ascii_visible() {
for c in ['a', 'Z', '0', '9', ' ', '~'] {
assert!(isprint_ascii(c), "{c:?} should be printable");
}
}
#[test]
fn compat_corpus_isprint_ascii_rejects_controls() {
for c in ['\0', '\n', '\r', '\t', '\x1b'] {
assert!(!isprint_ascii(c), "{c:?} should NOT be printable");
}
}
#[test]
fn difftime_same_input_returns_zero() {
assert_eq!(difftime(100, 100), 0.0);
assert_eq!(difftime(0, 0), 0.0);
}
#[test]
fn difftime_positive_when_t2_greater() {
assert_eq!(difftime(100, 60), 40.0);
assert_eq!(difftime(1000, 1), 999.0);
}
#[test]
fn strerror_zero_returns_nonempty() {
let s = strerror(0);
assert!(!s.is_empty(), "strerror(0) must return non-empty string");
}
#[test]
#[cfg(unix)]
fn strerror_known_errno_returns_descriptive() {
let s = strerror(libc::EACCES);
assert!(!s.is_empty());
assert_ne!(s, " ", "should have meaningful content");
}
#[test]
fn strerror_is_deterministic() {
let a = strerror(2);
let b = strerror(2);
assert_eq!(a, b, "strerror must be pure");
}
#[test]
fn isprint_ascii_del_is_not_printable() {
assert!(!isprint_ascii('\x7f'), "DEL (0x7f) is control");
}
#[test]
fn isprint_ascii_non_ascii_returns_false() {
assert!(!isprint_ascii('\u{0080}'), "U+0080 is non-ASCII control");
assert!(!isprint_ascii('é'), "é (U+00E9) is non-ASCII");
assert!(!isprint_ascii('日'), "日 (CJK) is non-ASCII");
}
#[test]
#[cfg(unix)]
fn zopenmax_returns_positive_bounded() {
let max = zopenmax();
assert!(max > 0, "zopenmax must be positive, got {}", max);
assert!(max < 100_000, "zopenmax suspiciously large: {}", max);
}
#[test]
fn strstr_finds_substring() {
assert_eq!(strstr("haystack", "tack"), Some(4));
assert_eq!(strstr("abc", "abc"), Some(0), "full match at start");
assert_eq!(strstr("abc", ""), Some(0), "empty needle matches at 0");
}
#[test]
fn strstr_missing_returns_none() {
assert_eq!(strstr("haystack", "xyz"), None);
assert_eq!(strstr("", "needle"), None);
}
#[test]
fn gettimeofday_returns_positive_seconds() {
let (sec, usec) = gettimeofday();
assert!(sec > 0, "current epoch sec must be positive");
assert!(usec >= 0 && usec < 1_000_000, "usec in [0, 1M)");
}
#[test]
fn strtoul_empty_returns_zero_zero() {
let (v, n) = strtoul("", 10);
assert_eq!(v, 0);
assert_eq!(n, 0);
}
#[test]
fn strtoul_base_10_parses_digits() {
let (v, n) = strtoul("123", 10);
assert_eq!(v, 123);
assert_eq!(n, 3);
}
#[test]
fn strtoul_non_digit_prefix_returns_zero() {
let (v, n) = strtoul("abc", 10);
assert_eq!(v, 0);
assert_eq!(n, 0);
}
#[test]
fn strtoul_base_16_parses_hex() {
let (v, n) = strtoul("ff", 16);
assert_eq!(v, 0xff);
assert_eq!(n, 2);
}
#[test]
fn zgettime_returns_zero_on_success() {
let _g = crate::test_util::global_state_lock();
let mut ts: timespec = unsafe { std::mem::zeroed() };
let r = zgettime(&mut ts);
assert_eq!(r, 0, "zgettime success → 0");
}
#[test]
fn zgettime_populates_positive_secs() {
let _g = crate::test_util::global_state_lock();
let mut ts: timespec = unsafe { std::mem::zeroed() };
zgettime(&mut ts);
assert!(ts.tv_sec > 0, "secs must be > 0 after gettime");
}
#[test]
fn zgettime_monotonic_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let mut ts: timespec = unsafe { std::mem::zeroed() };
let _: i32 = zgettime_monotonic_if_available(&mut ts);
}
#[test]
fn zgetdir_none_returns_option_string_type() {
let _g = crate::test_util::global_state_lock();
let _: Option<String> = zgetdir(None);
}
#[test]
fn zchdir_nonexistent_returns_minus_one_pin() {
let _g = crate::test_util::global_state_lock();
let r = zchdir("/__nonexistent_zshrs_xyz_compat__");
assert_eq!(r, -1, "nonexistent dir → -1");
}
#[test]
fn u9_wcwidth_ascii_letter_returns_one() {
assert_eq!(u9_wcwidth('a'), 1, "ASCII letter width = 1");
}
#[test]
fn u9_wcwidth_returns_i32_type() {
let _: i32 = u9_wcwidth('a');
}
#[test]
fn u9_iswprint_ascii_letter_returns_true() {
assert!(u9_iswprint('a'), "ASCII letter is printable");
}
#[test]
fn u9_iswprint_nul_returns_false() {
assert!(!u9_iswprint('\0'), "NUL is NOT printable");
}
#[test]
fn zpathmax_returns_i64_type() {
let _: i64 = zpathmax("");
}
#[test]
fn zpathmax_is_pure_for_root() {
let first = zpathmax("/");
for _ in 0..3 {
assert_eq!(zpathmax("/"), first, "zpathmax must be pure");
}
}
#[test]
fn difftime_returns_f64_type() {
let _: f64 = difftime(0, 0);
}
#[test]
fn difftime_identity_returns_zero() {
for t in [0i64, 100, 1_000_000, i32::MAX as i64] {
assert_eq!(difftime(t, t), 0.0, "difftime({}, {}) must equal 0", t, t);
}
}
#[test]
fn difftime_antisymmetric() {
assert_eq!(
difftime(100, 50),
-difftime(50, 100),
"difftime is antisymmetric"
);
}
#[test]
fn strerror_returns_string_type() {
let _: String = strerror(0);
}
#[test]
fn strerror_zero_returns_non_empty() {
assert!(!strerror(0).is_empty(), "strerror(0) must be non-empty");
}
#[test]
fn zopenmax_returns_i64_type() {
let _: i64 = zopenmax();
}
#[test]
fn zopenmax_returns_positive() {
let n = zopenmax();
assert!(n > 0, "zopenmax must be positive; got {}", n);
}
#[test]
fn zgetcwd_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _: String = zgetcwd();
}
#[test]
fn output64_zero_returns_zero_digit() {
assert_eq!(output64(0), "0", "0 → \"0\"");
}
#[test]
fn strstr_substring_returns_position() {
assert_eq!(strstr("hello", "ll"), Some(2), "\"ll\" in \"hello\" at 2");
}
#[test]
fn strstr_not_found_returns_none() {
assert_eq!(strstr("abc", "xyz"), None, "no match → None");
}
#[test]
fn gettimeofday_returns_i64_pair_type() {
let _: (i64, i64) = gettimeofday();
}
#[test]
fn strtoul_basic_parse_returns_value_and_count() {
let (v, n) = strtoul("42", 10);
assert_eq!(v, 42, "value parses to 42");
assert_eq!(n, 2, "consumed 2 bytes");
}
}