use std::os::unix::fs::MetadataExt;
pub const PATH_MAX: usize = libc::PATH_MAX as usize;
pub const ZSH_INITIAL_OPEN_MAX: usize = 64;
pub const OPEN_MAX: usize = ZSH_INITIAL_OPEN_MAX;
pub const DIGBUFSIZE: usize = (((std::mem::size_of::<i64>() * 8) - 1) * 30103 / 100000) + 3;
pub const BDIGBUFSIZE: usize = (std::mem::size_of::<i64>() * 8) + 4;
#[cfg(unix)]
pub const VDISABLEVAL: u8 = libc::_POSIX_VDISABLE; #[cfg(not(unix))]
pub const VDISABLEVAL: u8 = 0;
#[allow(non_camel_case_types)]
pub type timespec = libc::timespec;
pub const DEFAULT_WORDCHARS: &str = "*?_-.[]~=/&;!#$%^(){}<>";
pub const DEFAULT_TIMEFMT: &str = "%J %U user %S system %P cpu %*E total";
pub const S_IFMT: u32 = 0o170_000;
pub const S_IFBLK: u32 = libc::S_IFBLK as u32;
pub const S_IFCHR: u32 = libc::S_IFCHR as u32;
pub const S_IFDIR: u32 = libc::S_IFDIR as u32;
pub const S_IFIFO: u32 = libc::S_IFIFO as u32;
pub const S_IFLNK: u32 = libc::S_IFLNK as u32;
pub const S_IFREG: u32 = libc::S_IFREG as u32;
pub const S_IFSOCK: u32 = libc::S_IFSOCK as u32;
pub const S_ISUID: u32 = 0o4000; pub const S_ISGID: u32 = 0o2000; pub const S_ISVTX: u32 = 0o1000; pub const S_IRUSR: u32 = 0o0400; pub const S_IWUSR: u32 = 0o0200; pub const S_IXUSR: u32 = 0o0100; pub const S_IRGRP: u32 = 0o0040; pub const S_IWGRP: u32 = 0o0020; pub const S_IXGRP: u32 = 0o0010; pub const S_IROTH: u32 = 0o0004; pub const S_IWOTH: u32 = 0o0002; pub const S_IXOTH: u32 = 0o0001;
pub const S_IRWXU: u32 = S_IRUSR | S_IWUSR | S_IXUSR; pub const S_IRWXG: u32 = S_IRGRP | S_IWGRP | S_IXGRP; pub const S_IRWXO: u32 = S_IROTH | S_IWOTH | S_IXOTH; pub const S_IRUGO: u32 = S_IRUSR | S_IRGRP | S_IROTH; pub const S_IWUGO: u32 = S_IWUSR | S_IWGRP | S_IWOTH; pub const S_IXUGO: u32 = S_IXUSR | S_IXGRP | S_IXOTH;
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISBLK(m: u32) -> bool {
(m & S_IFMT) == S_IFBLK
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISCHR(m: u32) -> bool {
(m & S_IFMT) == S_IFCHR
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISDIR(m: u32) -> bool {
(m & S_IFMT) == S_IFDIR
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISDOOR(_m: u32) -> bool {
false
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISFIFO(m: u32) -> bool {
(m & S_IFMT) == S_IFIFO
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISLNK(m: u32) -> bool {
(m & S_IFMT) == S_IFLNK
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISMPB(_m: u32) -> bool {
false
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISMPC(_m: u32) -> bool {
false
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISNWK(_m: u32) -> bool {
false
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISOFD(_m: u32) -> bool {
false
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISOFL(_m: u32) -> bool {
false
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISREG(m: u32) -> bool {
(m & S_IFMT) == S_IFREG
}
#[inline]
#[allow(non_snake_case)]
pub const fn S_ISSOCK(m: u32) -> bool {
(m & S_IFMT) == S_IFSOCK
}
#[inline]
#[allow(non_snake_case)]
pub const fn WIFEXITED(x: i32) -> bool {
(x & 0o377) == 0
}
#[inline]
#[allow(non_snake_case)]
pub const fn WEXITSTATUS(x: i32) -> i32 {
(x >> 8) & 0o377
}
#[inline]
#[allow(non_snake_case)]
pub const fn WIFSIGNALED(x: i32) -> bool {
let lo = x & 0o377;
lo != 0 && lo != 0o177
}
#[inline]
#[allow(non_snake_case)]
pub const fn WTERMSIG(x: i32) -> i32 {
x & 0o177
}
#[inline]
#[allow(non_snake_case)]
pub const fn WCOREDUMP(x: i32) -> bool {
(x & 0o200) != 0
}
#[inline]
#[allow(non_snake_case)]
pub const fn WIFSTOPPED(x: i32) -> bool {
(x & 0o377) == 0o177
}
#[inline]
#[allow(non_snake_case)]
pub const fn WSTOPSIG(x: i32) -> i32 {
(x >> 8) & 0o377
}
pub const F_OK: i32 = 0;
pub const X_OK: i32 = 1;
pub const W_OK: i32 = 2;
pub const R_OK: i32 = 4;
#[cfg(unix)]
pub const O_NOCTTY: i32 = libc::O_NOCTTY; #[cfg(not(unix))]
pub const O_NOCTTY: i32 = 0;
#[inline]
#[allow(non_snake_case)]
pub const fn IS_DIRSEP(c: char) -> bool {
c == '/'
}
#[inline]
#[allow(non_snake_case)]
pub fn GET_ST_ATIME_NSEC(st: &std::fs::Metadata) -> u32 {
st.accessed()
.ok()
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.subsec_nanos())
.unwrap_or(0)
}
#[inline]
#[allow(non_snake_case)]
pub fn GET_ST_MTIME_NSEC(st: &std::fs::Metadata) -> u32 {
st.modified()
.ok()
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.subsec_nanos())
.unwrap_or(0)
}
#[inline]
#[allow(non_snake_case)]
pub fn GET_ST_CTIME_NSEC(st: &std::fs::Metadata) -> u32 {
#[cfg(unix)]
{
st.ctime_nsec() as u32
}
#[cfg(not(unix))]
{
let _ = st;
0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn digbufsize_i64_is_21() {
let _g = crate::test_util::global_state_lock();
assert_eq!(DIGBUFSIZE, 21);
}
#[test]
fn bdigbufsize_i64_is_68() {
let _g = crate::test_util::global_state_lock();
assert_eq!(BDIGBUFSIZE, 68);
}
#[test]
fn access_modes_correct() {
let _g = crate::test_util::global_state_lock();
assert_eq!(F_OK, 0);
assert_eq!(X_OK, 1);
assert_eq!(W_OK, 2);
assert_eq!(R_OK, 4);
}
#[test]
fn permission_bits_match_posix() {
let _g = crate::test_util::global_state_lock();
assert_eq!(S_ISUID, 0o4000);
assert_eq!(S_ISGID, 0o2000);
assert_eq!(S_ISVTX, 0o1000);
assert_eq!(S_IRUSR, 0o0400);
assert_eq!(S_IWUSR, 0o0200);
assert_eq!(S_IXUSR, 0o0100);
assert_eq!(S_IRGRP, 0o0040);
assert_eq!(S_IRWXU, 0o0700);
assert_eq!(S_IRWXG, 0o0070);
assert_eq!(S_IRWXO, 0o0007);
assert_eq!(S_IRUGO, 0o0444);
assert_eq!(S_IWUGO, 0o0222);
assert_eq!(S_IXUGO, 0o0111);
}
#[test]
fn stat_predicates_dispatch() {
let _g = crate::test_util::global_state_lock();
let dir_mode = libc::S_IFDIR as u32 | 0o755;
let file_mode = libc::S_IFREG as u32 | 0o644;
let link_mode = libc::S_IFLNK as u32 | 0o777;
assert!(S_ISDIR(dir_mode));
assert!(!S_ISDIR(file_mode));
assert!(S_ISREG(file_mode));
assert!(!S_ISREG(dir_mode));
assert!(S_ISLNK(link_mode));
assert!(!S_ISLNK(file_mode));
assert!(!S_ISDOOR(dir_mode));
assert!(!S_ISMPB(dir_mode));
assert!(!S_ISMPC(dir_mode));
assert!(!S_ISNWK(dir_mode));
assert!(!S_ISOFD(dir_mode));
assert!(!S_ISOFL(dir_mode));
}
#[test]
fn wait_decoders_normal_exit() {
let _g = crate::test_util::global_state_lock();
let status = 42 << 8;
assert!(WIFEXITED(status));
assert!(!WIFSIGNALED(status));
assert!(!WIFSTOPPED(status));
assert_eq!(WEXITSTATUS(status), 42);
}
#[test]
fn wait_decoders_killed_by_signal() {
let _g = crate::test_util::global_state_lock();
let status = 15;
assert!(!WIFEXITED(status));
assert!(WIFSIGNALED(status));
assert!(!WIFSTOPPED(status));
assert_eq!(WTERMSIG(status), 15);
assert!(!WCOREDUMP(status));
}
#[test]
fn wait_decoders_killed_with_core() {
let _g = crate::test_util::global_state_lock();
let status = 11 | 0o200;
assert!(WIFSIGNALED(status));
assert_eq!(WTERMSIG(status), 11);
assert!(WCOREDUMP(status));
}
#[test]
fn wait_decoders_stopped() {
let _g = crate::test_util::global_state_lock();
let status = (20 << 8) | 0o177;
assert!(!WIFEXITED(status));
assert!(!WIFSIGNALED(status));
assert!(WIFSTOPPED(status));
assert_eq!(WSTOPSIG(status), 20);
}
#[test]
fn is_dirsep_basic() {
let _g = crate::test_util::global_state_lock();
assert!(IS_DIRSEP('/'));
assert!(!IS_DIRSEP('\\'));
assert!(!IS_DIRSEP('a'));
}
#[test]
fn default_wordchars_contents() {
let _g = crate::test_util::global_state_lock();
assert_eq!(DEFAULT_WORDCHARS, "*?_-.[]~=/&;!#$%^(){}<>");
}
#[test]
fn default_timefmt_contents() {
let _g = crate::test_util::global_state_lock();
assert_eq!(DEFAULT_TIMEFMT, "%J %U user %S system %P cpu %*E total");
}
#[test]
fn zsh_initial_open_max_is_64() {
let _g = crate::test_util::global_state_lock();
assert_eq!(ZSH_INITIAL_OPEN_MAX, 64);
}
#[test]
fn get_st_atime_nsec_in_valid_range() {
let _g = crate::test_util::global_state_lock();
let dir = tempfile::tempdir().unwrap();
let p = dir.path().join("atime_test");
std::fs::write(&p, b"x").unwrap();
let md = std::fs::metadata(&p).unwrap();
let nsec = GET_ST_ATIME_NSEC(&md);
assert!(nsec < 1_000_000_000, "nsec must be < 1B, got {}", nsec);
}
#[test]
fn get_st_mtime_nsec_in_valid_range() {
let _g = crate::test_util::global_state_lock();
let dir = tempfile::tempdir().unwrap();
let p = dir.path().join("mtime_test");
std::fs::write(&p, b"x").unwrap();
let md = std::fs::metadata(&p).unwrap();
let nsec = GET_ST_MTIME_NSEC(&md);
assert!(nsec < 1_000_000_000, "nsec must be < 1B");
}
#[test]
#[cfg(unix)]
fn get_st_ctime_nsec_in_valid_range() {
let _g = crate::test_util::global_state_lock();
let dir = tempfile::tempdir().unwrap();
let p = dir.path().join("ctime_test");
std::fs::write(&p, b"x").unwrap();
let md = std::fs::metadata(&p).unwrap();
let nsec = GET_ST_CTIME_NSEC(&md);
assert!(nsec < 1_000_000_000, "nsec must be < 1B");
}
#[test]
fn get_st_atime_nsec_is_deterministic() {
let _g = crate::test_util::global_state_lock();
let dir = tempfile::tempdir().unwrap();
let p = dir.path().join("det_test");
std::fs::write(&p, b"x").unwrap();
let md = std::fs::metadata(&p).unwrap();
let first = GET_ST_ATIME_NSEC(&md);
for _ in 0..5 {
assert_eq!(GET_ST_ATIME_NSEC(&md), first);
}
}
#[test]
fn default_wordchars_includes_canonical_chars() {
for c in ['_', '-', '.', '~', '/', '&', ';', '!', '#', '$', '%', '^'] {
assert!(
DEFAULT_WORDCHARS.contains(c),
"DEFAULT_WORDCHARS missing canonical char {:?}",
c
);
}
}
#[test]
fn default_wordchars_no_whitespace() {
for c in DEFAULT_WORDCHARS.chars() {
assert!(
!c.is_whitespace(),
"DEFAULT_WORDCHARS must not contain whitespace, found {:?}",
c
);
}
}
#[test]
fn default_timefmt_contains_required_specifiers() {
for spec in ["%J", "%U", "%S", "%P", "%*E"] {
assert!(
DEFAULT_TIMEFMT.contains(spec),
"DEFAULT_TIMEFMT missing required spec {}",
spec
);
}
}
#[test]
fn s_ifmt_covers_all_file_type_bits() {
for &bits in &[
S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFREG, S_IFSOCK,
] {
assert_eq!(
bits & S_IFMT,
bits,
"S_IF* bits {:o} must be subset of S_IFMT {:o}",
bits,
S_IFMT
);
}
}
#[test]
fn wait_decoders_mutually_exclusive() {
for status in [42 << 8, 15, 11 | 0o200, (20 << 8) | 0o177] {
let exited = WIFEXITED(status);
let signaled = WIFSIGNALED(status);
let stopped = WIFSTOPPED(status);
let count = (exited as i32) + (signaled as i32) + (stopped as i32);
assert_eq!(
count, 1,
"exactly one of W{{IFEXITED/IFSIGNALED/IFSTOPPED}} must be true for status {:o}",
status
);
}
}
#[test]
fn wexitstatus_always_in_byte_range() {
for code in [0i32, 1, 42, 127, 200, 255] {
let status = code << 8;
assert_eq!(WEXITSTATUS(status), code & 0xff);
}
}
#[test]
fn wtermsig_low_seven_bits() {
for sig in [1i32, 2, 9, 11, 15, 31, 63, 127] {
let status = sig;
assert_eq!(WTERMSIG(status), sig);
}
}
#[test]
fn wifexited_zero_status_is_true() {
assert!(WIFEXITED(0), "exit 0 must satisfy WIFEXITED");
assert_eq!(WEXITSTATUS(0), 0);
}
#[test]
fn wait_signal_and_exit_not_simultaneously_encoded() {
for sig in [1, 11, 15] {
assert!(WIFSIGNALED(sig));
assert!(!WIFEXITED(sig));
}
for code in [0, 42, 200] {
assert!(WIFEXITED(code << 8));
assert!(!WIFSIGNALED(code << 8));
}
}
#[test]
fn is_dirsep_const_fn_compiles() {
const SLASH_IS_SEP: bool = IS_DIRSEP('/');
const BACK_IS_NOT_SEP: bool = IS_DIRSEP('\\');
assert!(SLASH_IS_SEP);
assert!(!BACK_IS_NOT_SEP);
}
#[test]
fn access_constants_pairwise_distinct() {
let codes = [F_OK, X_OK, W_OK, R_OK];
let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
assert_eq!(unique.len(), codes.len(), "all access codes must differ");
}
#[test]
fn access_all_perms_combine_to_seven() {
assert_eq!(X_OK | W_OK | R_OK, 7, "canonical R|W|X = 7");
}
#[test]
fn permission_bits_are_single_bits() {
for &b in &[
S_ISUID, S_ISGID, S_ISVTX, S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP,
S_IROTH, S_IWOTH, S_IXOTH,
] {
assert!(b.is_power_of_two(), "{:o} must be a single bit", b);
}
}
#[test]
fn aggregate_masks_match_constituents() {
assert_eq!(S_IRWXU, S_IRUSR | S_IWUSR | S_IXUSR, "c:718");
assert_eq!(S_IRWXG, S_IRGRP | S_IWGRP | S_IXGRP, "c:721");
assert_eq!(S_IRWXO, S_IROTH | S_IWOTH | S_IXOTH, "c:724");
assert_eq!(S_IRUGO, S_IRUSR | S_IRGRP | S_IROTH, "c:727");
assert_eq!(S_IWUGO, S_IWUSR | S_IWGRP | S_IWOTH, "c:730");
assert_eq!(S_IXUGO, S_IXUSR | S_IXGRP | S_IXOTH, "c:733");
}
#[test]
fn path_max_positive_non_zero() {
assert!(PATH_MAX > 0, "PATH_MAX must be > 0");
}
#[test]
fn path_max_at_least_256() {
assert!(PATH_MAX >= 256, "PATH_MAX must be ≥ 256 POSIX min");
}
#[test]
fn zsh_initial_open_max_canonical_64() {
assert_eq!(ZSH_INITIAL_OPEN_MAX, 64, "canonical zsh default");
}
#[test]
fn open_max_at_least_initial() {
assert!(
OPEN_MAX >= ZSH_INITIAL_OPEN_MAX,
"OPEN_MAX {} must be ≥ ZSH_INITIAL_OPEN_MAX {}",
OPEN_MAX,
ZSH_INITIAL_OPEN_MAX
);
}
#[test]
fn digbufsize_positive() {
assert!(DIGBUFSIZE > 0, "DIGBUFSIZE must hold at least 1 byte");
}
#[test]
fn bdigbufsize_positive() {
assert!(BDIGBUFSIZE > 0, "BDIGBUFSIZE must hold at least 1 byte");
}
#[test]
fn bdigbufsize_greater_than_digbufsize() {
assert!(
BDIGBUFSIZE > DIGBUFSIZE,
"binary {} must need more bytes than decimal {}",
BDIGBUFSIZE,
DIGBUFSIZE
);
}
#[test]
fn s_ifmt_is_nonzero() {
assert!(S_IFMT > 0);
}
#[test]
fn is_dirsep_nul_returns_false() {
assert!(!IS_DIRSEP('\0'));
}
#[test]
fn is_dirsep_only_matches_slash_full_ascii() {
for b in 0u8..128 {
let c = b as char;
let r = IS_DIRSEP(c);
if c == '/' {
assert!(r, "'/' must match");
} else {
assert!(!r, "{:?} must NOT match", c);
}
}
}
#[test]
fn s_is_stub_predicates_always_false() {
for mode in [0u32, 0xffffffff, S_IFDIR, S_IFREG, S_IFLNK, 0o170_000] {
assert!(!S_ISDOOR(mode), "S_ISDOOR({:o}) must be false stub", mode);
assert!(!S_ISMPB(mode), "S_ISMPB({:o}) must be false stub", mode);
assert!(!S_ISMPC(mode), "S_ISMPC({:o}) must be false stub", mode);
assert!(!S_ISNWK(mode), "S_ISNWK({:o}) must be false stub", mode);
assert!(!S_ISOFD(mode), "S_ISOFD({:o}) must be false stub", mode);
assert!(!S_ISOFL(mode), "S_ISOFL({:o}) must be false stub", mode);
}
}
#[test]
fn permission_bits_pairwise_distinct() {
let bits = [
S_ISUID, S_ISGID, S_ISVTX, S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP,
S_IROTH, S_IWOTH, S_IXOTH,
];
let unique: std::collections::HashSet<_> = bits.iter().copied().collect();
assert_eq!(
unique.len(),
bits.len(),
"permission bits must be pairwise distinct"
);
}
#[test]
fn permission_bits_all_single_bits() {
for v in [
S_ISUID, S_ISGID, S_ISVTX, S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IWGRP, S_IXGRP,
S_IROTH, S_IWOTH, S_IXOTH,
] {
assert!(
v.is_power_of_two(),
"permission bit 0o{:o} must be a single bit",
v
);
}
}
#[test]
fn setid_sticky_bits_verbatim() {
assert_eq!(S_ISUID, 0o4000);
assert_eq!(S_ISGID, 0o2000);
assert_eq!(S_ISVTX, 0o1000);
}
#[test]
fn access_flag_values_exact() {
assert_eq!(F_OK, 0);
assert_eq!(X_OK, 1);
assert_eq!(W_OK, 2);
assert_eq!(R_OK, 4);
}
#[test]
fn s_irwx_aggregates_equal_seven_in_nibble() {
assert_eq!(S_IRWXU, 0o0700);
assert_eq!(S_IRWXG, 0o0070);
assert_eq!(S_IRWXO, 0o0007);
}
#[test]
fn default_wordchars_exact_value() {
assert_eq!(DEFAULT_WORDCHARS, "*?_-.[]~=/&;!#$%^(){}<>");
}
#[test]
fn default_timefmt_exact_value() {
assert_eq!(DEFAULT_TIMEFMT, "%J %U user %S system %P cpu %*E total");
}
#[test]
fn s_ifmt_is_octal_170000() {
assert_eq!(S_IFMT, 0o170_000);
}
#[test]
fn file_type_bits_within_s_ifmt_mask() {
for ft in [
S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFREG, S_IFSOCK,
] {
assert_eq!(
ft & !S_IFMT,
0,
"file-type 0o{:o} must be within S_IFMT mask",
ft
);
}
}
#[test]
fn file_type_bits_pairwise_distinct() {
let bits = [
S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFREG, S_IFSOCK,
];
let unique: std::collections::HashSet<_> = bits.iter().copied().collect();
assert_eq!(
unique.len(),
bits.len(),
"file-type constants must be pairwise distinct"
);
}
#[test]
fn zsh_initial_open_max_exact_64() {
assert_eq!(ZSH_INITIAL_OPEN_MAX, 64);
}
}