use crate::ported::exec::ShellExecutor;
use crate::ported::math::{mnumber, MN_INTEGER, MN_FLOAT};
use crate::ported::params::{setiparam, setsparam, setiparam_no_convert};
use crate::ported::utils::{isident, metafy, unmeta, zwarnnam, zclose, movefd};
use crate::ported::zsh_h::{OPT_ISSET, OPT_ARG};
const SYSREAD_BUFSIZE: usize = 8192;
pub fn getposint(instr: &str, nam: &str) -> i32 { let (ret, eptr) = crate::ported::utils::zstrtol(instr, 10);
let ret = ret as i32;
if !eptr.is_empty() || ret < 0 {
zwarnnam(nam, &format!("integer expected: {}", instr)); return -1; }
ret }
#[allow(unused_variables)]
pub fn bin_sysread(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
let mut infd: i32 = 0; let mut outfd: i32 = -1; let mut bufsize: usize = SYSREAD_BUFSIZE; let mut outvar: Option<String> = None; let mut countvar: Option<String> = None;
if OPT_ISSET(ops, b'i') { infd = getposint(OPT_ARG(ops, b'i').unwrap_or(""), nam); if infd < 0 { return 1; } }
if OPT_ISSET(ops, b'o') { outfd = getposint(OPT_ARG(ops, b'o').unwrap_or(""), nam); if outfd < 0 { return 1; } }
if OPT_ISSET(ops, b's') { let v = getposint(OPT_ARG(ops, b's').unwrap_or(""), nam); if v < 0 { return 1; } bufsize = v as usize;
}
if OPT_ISSET(ops, b'c') { let cv = OPT_ARG(ops, b'c').unwrap_or("").to_string(); if !isident(&cv) { zwarnnam(nam, &format!("not an identifier: {}", cv)); return 1; }
countvar = Some(cv);
}
if !args.is_empty() { let ov = args[0].clone(); if !isident(&ov) { zwarnnam(nam, &format!("not an identifier: {}", ov)); return 1; }
outvar = Some(ov);
}
let timeout_arg: Option<&str> = if OPT_ISSET(ops, b't') { OPT_ARG(ops, b't')
} else { None };
let mut inbuf = vec![0u8; bufsize];
if let Some(t_str) = timeout_arg {
let to_mn = match crate::ported::math::matheval(t_str) {
Ok(m) => m,
Err(_) => return 1, };
let to_int: i32 = if to_mn.type_ == MN_FLOAT {
(1000.0 * to_mn.d) as i32 } else {
(1000 * to_mn.l) as i32 };
let mut ret;
loop {
let mut pfd = libc::pollfd { fd: infd,
events: libc::POLLIN,
revents: 0,
};
ret = unsafe { libc::poll(&mut pfd, 1, to_int) };
if ret >= 0 {
break;
}
let eno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
if eno != libc::EINTR {
break; }
}
if ret <= 0 {
return if ret != 0 { 2 } else { 4 };
}
}
let mut count: isize;
loop {
count = unsafe {
libc::read(infd, inbuf.as_mut_ptr() as *mut libc::c_void, bufsize)
};
if count >= 0 { break; }
let eno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
if eno != libc::EINTR { break; } }
if let Some(ref cv) = countvar {
crate::ported::params::setiparam(cv, count as i64); }
if count < 0 {
return 2;
}
let count = count as usize;
if outfd >= 0 { if count == 0 { return 5; } let mut p = 0usize;
let mut remaining = count;
while remaining > 0 { let ret = unsafe {
libc::write(outfd,
inbuf[p..].as_ptr() as *const libc::c_void,
remaining)
};
if ret < 0 { let eno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
if eno == libc::EINTR { continue;
}
if let Some(ref ov) = outvar {
let buf_remaining = String::from_utf8_lossy(&inbuf[p..p+remaining]);
let m = metafy(&buf_remaining);
crate::ported::params::setsparam(ov, &m); }
if let Some(ref cv) = countvar {
crate::ported::params::setiparam(cv, remaining as i64); }
return 3; }
p += ret as usize; remaining -= ret as usize; }
return 0; }
let target = outvar.unwrap_or_else(|| "REPLY".to_string()); let buf_str = String::from_utf8_lossy(&inbuf[..count]);
let m = metafy(&buf_str);
crate::ported::params::setsparam(&target, &m); if count != 0 { 0 } else { 5 } }
pub fn bin_syswrite(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut outfd: i32 = 1; let mut countvar: Option<String> = None;
if OPT_ISSET(ops, b'o') { outfd = getposint(OPT_ARG(ops, b'o').unwrap_or(""), nam); if outfd < 0 { return 1; } }
if OPT_ISSET(ops, b'c') { let cv = OPT_ARG(ops, b'c').unwrap_or("").to_string(); if !isident(&cv) { zwarnnam(nam, &format!("not an identifier: {}", cv)); return 1; }
countvar = Some(cv);
}
let data = match args.first() { Some(d) => d.clone(),
None => return 1,
};
let unmeta = self::unmeta(&data);
let bytes = unmeta.as_bytes();
let mut totcount: usize = 0; let mut len = bytes.len();
let mut p = 0usize;
while len > 0 { let count = unsafe {
libc::write(outfd,
bytes[p..].as_ptr() as *const libc::c_void,
len)
};
if count < 0 { let eno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
if eno != libc::EINTR { if let Some(ref cv) = countvar { crate::ported::params::setiparam(cv, totcount as i64); }
return 2; }
continue;
}
p += count as usize; totcount += count as usize; len -= count as usize; }
if let Some(ref cv) = countvar {
crate::ported::params::setiparam(cv, totcount as i64); }
0 }
pub fn bin_sysopen(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let read_flag = OPT_ISSET(ops, b'r'); let write_flag = OPT_ISSET(ops, b'w'); let append_flag = OPT_ISSET(ops, b'a');
let append_flag_bit = if append_flag { libc::O_APPEND } else { 0 };
let mut flags = libc::O_NOCTTY | append_flag_bit | if append_flag || write_flag {
if read_flag { libc::O_RDWR } else { libc::O_WRONLY }
} else {
libc::O_RDONLY
};
let mut perms: u32 = 0o666;
let mut explicit: i32 = -1;
if !OPT_ISSET(ops, b'u') {
zwarnnam(nam, "file descriptor not specified"); return 1; }
let fdvar = OPT_ARG(ops, b'u').unwrap_or("").to_string(); let path = match args.first() {
Some(p) => p.clone(),
None => return 1,
};
let o_arg: Option<&str> = if OPT_ISSET(ops, b'o') { OPT_ARG(ops, b'o') } else { None };
let m_arg: Option<&str> = if OPT_ISSET(ops, b'm') { OPT_ARG(ops, b'm') } else { None };
if fdvar.len() == 1 && fdvar.chars().next().unwrap().is_ascii_digit() {
explicit = fdvar.parse().unwrap(); } else if !isident(&fdvar) { zwarnnam(nam, &format!("not an identifier: {}", fdvar)); return 1; }
if let Some(opts) = o_arg {
for tok in opts.split(',') { let mut name: &str = tok;
if name.len() >= 2 && name[..2].eq_ignore_ascii_case("O_") {
name = &name[2..];
}
#[cfg(unix)]
{
const OPENOPTS: &[(&str, i32)] = &[
("cloexec", libc::O_CLOEXEC), ("nofollow", libc::O_NOFOLLOW), ("sync", libc::O_SYNC), #[cfg(target_os = "linux")]
("noatime", libc::O_NOATIME), ("nonblock", libc::O_NONBLOCK), ("excl", libc::O_EXCL | libc::O_CREAT), ("creat", libc::O_CREAT), ("create", libc::O_CREAT), ("truncate", libc::O_TRUNC), ("trunc", libc::O_TRUNC), ];
let mut found: Option<i32> = None;
for (n, oflag) in OPENOPTS.iter().rev() { if n.eq_ignore_ascii_case(name) {
found = Some(*oflag);
break;
}
}
let oflag = match found {
Some(f) => f,
None => {
zwarnnam(nam, &format!("unsupported option: {}\n", tok)); return 1; }
};
flags |= oflag; }
}
}
if let Some(m) = m_arg {
let mode_str: &str = m;
let mut ptr = 0;
let bytes = mode_str.as_bytes();
while ptr < bytes.len() && (b'0'..=b'7').contains(&bytes[ptr]) {
ptr += 1;
}
if ptr < bytes.len() || ptr < 3 {
zwarnnam(nam, &format!("invalid mode {}", mode_str)); return 1; }
let (v, _) = crate::ported::utils::zstrtol(mode_str, 8);
perms = v as u32;
}
let path_c = match std::ffi::CString::new(path.as_bytes()) {
Ok(s) => s,
Err(_) => return 1,
};
let fd = unsafe {
if (flags & libc::O_CREAT) != 0 { libc::open(path_c.as_ptr(), flags, perms as libc::c_uint) } else {
libc::open(path_c.as_ptr(), flags) }
};
if fd == -1 { let e = std::io::Error::last_os_error();
zwarnnam(nam, &format!("can't open file {}: {}", path, e)); return 2; }
let moved_fd: i32 = if explicit > -1 {
crate::ported::utils::redup(fd, explicit) } else {
movefd(fd) };
if moved_fd == -1 { zwarnnam(nam, &format!("can't open file {}", path)); return 2; }
if (flags & libc::O_CLOEXEC) != 0 && fd != moved_fd { unsafe { libc::fcntl(moved_fd, libc::F_SETFD, libc::FD_CLOEXEC); } }
if explicit == -1 {
crate::ported::params::setiparam(&fdvar, moved_fd as i64); }
0 }
pub fn bin_sysseek(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut w: i32 = libc::SEEK_SET; let mut fd: i32 = 0;
if OPT_ISSET(ops, b'u') { fd = getposint(OPT_ARG(ops, b'u').unwrap_or(""), nam); if fd < 0 { return 1; } }
if OPT_ISSET(ops, b'w') { let whence = OPT_ARG(ops, b'w').unwrap_or(""); if whence.eq_ignore_ascii_case("current") || whence == "1" { w = libc::SEEK_CUR; } else if whence.eq_ignore_ascii_case("end") || whence == "2" { w = libc::SEEK_END; } else if !whence.eq_ignore_ascii_case("start") && whence != "0" { zwarnnam(nam, &format!("unknown argument to -w: {}", whence)); return 1; }
}
let pos_str = match args.first() {
Some(s) => s.clone(),
None => return 1,
};
let pos = match crate::ported::math::mathevali(&pos_str) { Ok(v) => v,
Err(_) => return 1,
};
if unsafe { libc::lseek(fd, pos as libc::off_t, w) } == -1 { 2
} else {
0
}
}
#[allow(unused_variables)]
pub fn math_systell(name: &str, argc: i32, argv: &[mnumber], id: i32) -> mnumber { let fd: i32 = if argv[0].type_ == MN_INTEGER {
argv[0].l as i32
} else {
argv[0].d as i32
};
let mut ret = mnumber {
type_: MN_INTEGER, l: 0, d: 0.0,
};
if fd < 0 {
crate::ported::utils::zwarn("file descriptor out of range");
return ret;
}
ret.l = unsafe { libc::lseek(fd, 0, libc::SEEK_CUR) } as i64;
ret }
pub fn bin_syserror(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut num: i32 = 0;
let mut errvar: Option<String> = None;
let mut pfx: String = String::new();
if OPT_ISSET(ops, b'e') { let ev = OPT_ARG(ops, b'e').unwrap_or("").to_string(); if !isident(&ev) { zwarnnam(nam, &format!("not an identifier: {}", ev)); return 1; }
errvar = Some(ev);
}
if OPT_ISSET(ops, b'p') { pfx = OPT_ARG(ops, b'p').unwrap_or("").to_string(); }
if args.is_empty() { num = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
} else {
let arg = &args[0];
let bytes = arg.as_bytes();
let mut ptr = 0usize;
while ptr < bytes.len() && bytes[ptr].is_ascii_digit() {
ptr += 1;
}
if ptr == bytes.len() && ptr > 0 { num = arg.parse::<i32>().unwrap_or(0); } else { let mut found = false;
for (idx, (ename, _)) in SYS_ERRNAMES.iter().enumerate() {
if *ename == arg { num = (idx as i32) + 1; found = true;
break; }
}
if !found { return 2; }
}
}
let msg = std::io::Error::from_raw_os_error(num).to_string();
if let Some(ev) = errvar {
let str_out = format!("{}{}", pfx, msg); crate::ported::params::setsparam(&ev, &str_out); } else {
eprintln!("{}{}", pfx, msg); }
0 }
pub fn bin_zsystem_flock(nam: &str, args: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let mut cloexec: bool = true; let mut unlock: bool = false;
let mut readlock: bool = false;
let mut timeout: f64 = -1.0; let mut timeout_interval: i64 = 1_000_000;
let mut fdvar: Option<String> = None;
let mut i = 0usize;
while i < args.len() && args[i].starts_with('-') {
let arg = &args[i];
i += 1;
let optptr = &arg[1..];
if optptr.is_empty() || optptr == "-" { break;
}
let chars: Vec<char> = optptr.chars().collect();
let mut idx = 0usize;
while idx < chars.len() {
let opt = chars[idx];
match opt {
'e' => { cloexec = false; }
'f' => { let rest: String = chars[idx + 1..].iter().collect();
let fdvar_str = if !rest.is_empty() {
idx = chars.len(); rest
} else if i < args.len() {
let v = args[i].clone(); i += 1;
v
} else {
zwarnnam(nam, &format!(
"flock: option {} requires a variable name", opt));
return 1;
};
if !isident(&fdvar_str) { zwarnnam(nam, &format!(
"flock: option {} requires a variable name", opt));
return 1; }
fdvar = Some(fdvar_str);
break;
}
'r' => readlock = true, 't' => { let rest: String = chars[idx + 1..].iter().collect();
let optarg = if !rest.is_empty() {
idx = chars.len();
rest
} else if i < args.len() {
let v = args[i].clone();
i += 1;
v
} else {
zwarnnam(nam, &format!(
"flock: option {} requires a numeric timeout", opt));
return 1;
};
let tp = match crate::ported::math::matheval(&optarg) {
Ok(m) => m,
Err(_) => return 1,
};
timeout = if (tp.type_ & MN_FLOAT) != 0 { tp.d
} else {
tp.l as f64
};
if timeout > 1073741823.0 {
zwarnnam(nam, &format!("flock: invalid timeout value: '{}'", optarg));
return 1;
}
break;
}
'i' => { let rest: String = chars[idx + 1..].iter().collect();
let optarg = if !rest.is_empty() {
idx = chars.len();
rest
} else if i < args.len() {
let v = args[i].clone();
i += 1;
v
} else {
zwarnnam(nam, &format!(
"flock: option {} requires a numeric retry interval", opt));
return 1;
};
let mut tp = match crate::ported::math::matheval(&optarg) {
Ok(m) => m,
Err(_) => return 1,
};
if (tp.type_ & MN_FLOAT) == 0 { tp.type_ = MN_FLOAT;
tp.d = tp.l as f64;
}
tp.d = (tp.d * 1e6).ceil(); if tp.d < 1.0 || tp.d > 0.999 * (i64::MAX as f64) { zwarnnam(nam, &format!("flock: invalid interval value: '{}'", optarg));
return 1; }
timeout_interval = tp.d as i64; break;
}
'u' => unlock = true, _ => {
zwarnnam(nam, &format!("flock: unknown option: {}", opt)); return 1; }
}
idx += 1;
}
}
if i >= args.len() {
zwarnnam(nam, "flock: not enough arguments");
return 1;
}
if i + 1 < args.len() { zwarnnam(nam, "flock: too many arguments");
return 1;
}
let path = &args[i];
if unlock {
let flock_fd: i32 = match crate::ported::math::mathevali(path) {
Ok(v) => v as i32,
Err(_) => return 1,
};
if crate::ported::utils::zcloselockfd(flock_fd) < 0 { zwarnnam(nam, &format!(
"flock: file descriptor {} not in use for locking", flock_fd));
return 1;
}
return 0; }
let flags = if readlock {
libc::O_RDONLY | libc::O_NOCTTY
} else {
libc::O_RDWR | libc::O_NOCTTY
};
let path_unmeta = self::unmeta(path);
let path_c = match std::ffi::CString::new(path_unmeta) {
Ok(s) => s,
Err(_) => return 1,
};
let mut flock_fd = unsafe { libc::open(path_c.as_ptr(), flags) }; if flock_fd < 0 {
let e = std::io::Error::last_os_error();
zwarnnam(nam, &format!("failed to open {} for writing: {}", path, e));
return 1;
}
flock_fd = movefd(flock_fd); if flock_fd == -1 { return 1; }
if cloexec {
let fdflags = unsafe { libc::fcntl(flock_fd, libc::F_GETFD, 0) };
if fdflags != -1 {
unsafe { libc::fcntl(flock_fd, libc::F_SETFD, fdflags | libc::FD_CLOEXEC); }
}
}
crate::ported::utils::addlockfd(flock_fd, cloexec);
let lock_type: libc::c_short = if readlock {
libc::F_RDLCK as libc::c_short
} else {
libc::F_WRLCK as libc::c_short
};
#[allow(clippy::unnecessary_cast)]
let lck = libc::flock {
l_type: lock_type, l_whence: libc::SEEK_SET as libc::c_short, l_start: 0, l_len: 0, l_pid: 0,
};
if timeout > 0.0 { let deadline = std::time::Instant::now()
+ std::time::Duration::from_secs_f64(timeout);
loop {
let r = unsafe { libc::fcntl(flock_fd, libc::F_SETLK, &lck) };
if r >= 0 { break; }
let eno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
if eno != libc::EINTR && eno != libc::EACCES && eno != libc::EAGAIN {
zclose(flock_fd); let e = std::io::Error::last_os_error();
zwarnnam(nam, &format!("failed to lock file {}: {}", path, e));
return 1;
}
let now = std::time::Instant::now();
if now >= deadline {
zclose(flock_fd); return 2; }
let remaining = deadline - now;
let remaining_us = remaining.as_micros() as i64;
let interval = remaining_us.min(timeout_interval);
std::thread::sleep(std::time::Duration::from_micros(interval as u64));
}
} else {
let cmd = if timeout == 0.0 { libc::F_SETLK } else { libc::F_SETLKW };
loop {
let r = unsafe { libc::fcntl(flock_fd, cmd, &lck) };
if r >= 0 { break; }
let eno = std::io::Error::last_os_error().raw_os_error().unwrap_or(0);
if eno == libc::EINTR { continue; } zclose(flock_fd); let e = std::io::Error::last_os_error();
zwarnnam(nam, &format!("failed to lock file {}: {}", path, e));
return 1;
}
}
if let Some(ref var) = fdvar {
crate::ported::params::setiparam(var, flock_fd as i64); }
0 }
pub fn bin_zsystem_supports(nam: &str, args: &[String], _ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
if args.is_empty() {
zwarnnam(nam, "supports: not enough arguments");
return 255;
}
if args.len() > 1 {
zwarnnam(nam, "supports: too many arguments");
return 255;
}
if args[0] == "supports" { return 0; } #[cfg(unix)]
if args[0] == "flock" { return 0; } 1 }
pub fn bin_zsystem(nam: &str, args: &[String], ops: &crate::ported::zsh_h::options, func: i32) -> i32 {
if args.is_empty() {
zwarnnam(nam, "subcommand expected");
return 1;
}
if args[0] == "flock" {
return bin_zsystem_flock(nam, &args[1..], ops, func); }
if args[0] == "supports" {
return bin_zsystem_supports(nam, &args[1..], ops, func); }
zwarnnam(nam, &format!("unknown subcommand: {}", args[0])); 1 }
pub fn errnosgetfn() -> Vec<String> { SYS_ERRNAMES.iter().map(|(n, _)| n.to_string()).collect() }
pub fn fillpmsysparams(name: &str) -> Option<String> { let num: i32 = match name {
"pid" => unsafe { libc::getpid() }, "ppid" => unsafe { libc::getppid() }, "procsubstpid" => 0,
_ => return None, };
Some(format!("{}", num)) }
pub fn getpmsysparams(name: &str) -> Option<String> { fillpmsysparams(name) }
pub fn scanpmsysparams() -> Vec<(String, String)> { let mut out = Vec::new();
for n in ["pid", "ppid", "procsubstpid"] { if let Some(v) = fillpmsysparams(n) {
out.push((n.to_string(), v));
}
}
out
}
use crate::ported::zsh_h::module;
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 { 0
}
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
*features = featuresarray(m, module_features());
0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 { 0
}
pub fn cleanup_(m: *const module) -> i32 {
setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 { 0
}
#[cfg(target_os = "linux")]
pub static SYS_ERRNAMES: &[(&str, i32)] = &[
("EPERM", 1), ("ENOENT", 2), ("ESRCH", 3), ("EINTR", 4), ("EIO", 5),
("ENXIO", 6), ("E2BIG", 7), ("ENOEXEC", 8), ("EBADF", 9), ("ECHILD", 10),
("EAGAIN", 11), ("ENOMEM", 12), ("EACCES", 13), ("EFAULT", 14),
("ENOTBLK", 15), ("EBUSY", 16), ("EEXIST", 17), ("EXDEV", 18),
("ENODEV", 19), ("ENOTDIR", 20), ("EISDIR", 21), ("EINVAL", 22),
("ENFILE", 23), ("EMFILE", 24), ("ENOTTY", 25), ("ETXTBSY", 26),
("EFBIG", 27), ("ENOSPC", 28), ("ESPIPE", 29), ("EROFS", 30),
("EMLINK", 31), ("EPIPE", 32), ("EDOM", 33), ("ERANGE", 34),
("EDEADLK", 35), ("ENAMETOOLONG", 36), ("ENOLCK", 37), ("ENOSYS", 38),
("ENOTEMPTY", 39), ("ELOOP", 40),
];
#[cfg(target_os = "macos")]
pub static SYS_ERRNAMES: &[(&str, i32)] = &[
("EPERM", 1), ("ENOENT", 2), ("ESRCH", 3), ("EINTR", 4), ("EIO", 5),
("ENXIO", 6), ("E2BIG", 7), ("ENOEXEC", 8), ("EBADF", 9), ("ECHILD", 10),
("EDEADLK", 11), ("ENOMEM", 12), ("EACCES", 13), ("EFAULT", 14),
("ENOTBLK", 15), ("EBUSY", 16), ("EEXIST", 17), ("EXDEV", 18),
("ENODEV", 19), ("ENOTDIR", 20), ("EISDIR", 21), ("EINVAL", 22),
("ENFILE", 23), ("EMFILE", 24), ("ENOTTY", 25), ("ETXTBSY", 26),
("EFBIG", 27), ("ENOSPC", 28), ("ESPIPE", 29), ("EROFS", 30),
("EMLINK", 31), ("EPIPE", 32), ("EDOM", 33), ("ERANGE", 34),
("EAGAIN", 35), ("EINPROGRESS", 36), ("EALREADY", 37), ("ENOTSOCK", 38),
("EDESTADDRREQ", 39), ("EMSGSIZE", 40), ("EPROTOTYPE", 41),
("ENOPROTOOPT", 42), ("EPROTONOSUPPORT", 43), ("ESOCKTNOSUPPORT", 44),
("ENOTSUP", 45), ("EPFNOSUPPORT", 46), ("EAFNOSUPPORT", 47),
("EADDRINUSE", 48), ("EADDRNOTAVAIL", 49), ("ENETDOWN", 50),
("ENETUNREACH", 51), ("ENETRESET", 52), ("ECONNABORTED", 53),
("ECONNRESET", 54), ("ENOBUFS", 55), ("EISCONN", 56), ("ENOTCONN", 57),
("ESHUTDOWN", 58), ("ETOOMANYREFS", 59), ("ETIMEDOUT", 60),
("ECONNREFUSED", 61), ("ELOOP", 62), ("ENAMETOOLONG", 63),
("EHOSTDOWN", 64), ("EHOSTUNREACH", 65), ("ENOTEMPTY", 66),
("EPROCLIM", 67), ("EUSERS", 68), ("EDQUOT", 69), ("ESTALE", 70),
("EREMOTE", 71), ("EBADRPC", 72), ("ERPCMISMATCH", 73),
("EPROGUNAVAIL", 74), ("EPROGMISMATCH", 75), ("EPROCUNAVAIL", 76),
("ENOLCK", 77), ("ENOSYS", 78), ("EFTYPE", 79), ("EAUTH", 80),
("ENEEDAUTH", 81), ("EPWROFF", 82), ("EDEVERR", 83), ("EOVERFLOW", 84),
("EBADEXEC", 85), ("EBADARCH", 86), ("ESHLIBVERS", 87),
("EBADMACHO", 88), ("ECANCELED", 89), ("EIDRM", 90), ("ENOMSG", 91),
("EILSEQ", 92), ("ENOATTR", 93), ("EBADMSG", 94), ("EMULTIHOP", 95),
("ENODATA", 96), ("ENOLINK", 97), ("ENOSR", 98), ("ENOSTR", 99),
("EPROTO", 100), ("ETIME", 101), ("EOPNOTSUPP", 102), ("ENOPOLICY", 103),
("ENOTRECOVERABLE", 104), ("EOWNERDEAD", 105), ("EQFULL", 106),
];
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
pub static SYS_ERRNAMES: &[(&str, i32)] = &[
("EPERM", 1), ("ENOENT", 2), ("ESRCH", 3), ("EINTR", 4), ("EIO", 5),
("ENXIO", 6), ("E2BIG", 7), ("ENOEXEC", 8), ("EBADF", 9), ("ECHILD", 10),
("ENOMEM", 12), ("EACCES", 13), ("EFAULT", 14), ("EBUSY", 16),
("EEXIST", 17), ("EXDEV", 18), ("ENODEV", 19), ("ENOTDIR", 20),
("EISDIR", 21), ("EINVAL", 22), ("ENFILE", 23), ("EMFILE", 24),
("ENOTTY", 25), ("EFBIG", 27), ("ENOSPC", 28), ("ESPIPE", 29),
("EROFS", 30), ("EMLINK", 31), ("EPIPE", 32), ("EDOM", 33),
("ERANGE", 34),
];
pub static ERRNO_NAMES: &[(&str, i32)] = SYS_ERRNAMES;
use crate::ported::zsh_h::features as features_t;
use std::sync::{Mutex, OnceLock};
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["b:syserror".to_string(), "b:sysread".to_string(), "b:syswrite".to_string(), "b:sysopen".to_string(), "b:sysseek".to_string(), "b:zsystem".to_string(), "f:systell".to_string(), "p:errnos".to_string(), "p:sysparams".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features_t>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 9]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 6,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 1,
pd_list: None,
pd_size: 2,
n_abstract: 0,
}))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::math::{mnumber, MN_INTEGER};
use std::fs::File;
use std::io::Write as _;
use tempfile::TempDir;
#[test]
fn getposint_basic() {
assert_eq!(getposint("42", "test"), 42);
assert_eq!(getposint("0", "test"), 0);
assert_eq!(getposint("-1", "test"), -1); assert_eq!(getposint("abc", "test"), -1); }
#[test]
fn bin_zsystem_supports_self() {
let ops = empty_ops();
assert_eq!(bin_zsystem_supports("zsystem",
&["supports".to_string()], &ops, 0), 0);
#[cfg(unix)]
assert_eq!(bin_zsystem_supports("zsystem",
&["flock".to_string()], &ops, 0), 0);
assert_eq!(bin_zsystem_supports("zsystem",
&["nosuchfeature".to_string()], &ops, 0), 1);
}
#[test]
fn bin_zsystem_supports_arg_count() {
let ops = empty_ops();
assert_eq!(bin_zsystem_supports("zsystem", &[], &ops, 0), 255);
assert_eq!(bin_zsystem_supports("zsystem",
&["a".to_string(), "b".to_string()], &ops, 0), 255);
}
#[test]
fn bin_zsystem_dispatch() {
let ops = empty_ops();
assert_eq!(bin_zsystem("zsystem",
&["supports".to_string(), "supports".to_string()], &ops, 0), 0);
assert_eq!(bin_zsystem("zsystem",
&["unknown".to_string()], &ops, 0), 1);
assert_eq!(bin_zsystem("zsystem", &[], &ops, 0), 1);
}
#[test]
fn errnosgetfn_returns_table() {
let names = errnosgetfn();
assert!(names.contains(&"EPERM".to_string()));
assert!(names.contains(&"ENOENT".to_string()));
assert!(names.contains(&"EINVAL".to_string()));
}
#[test]
fn fillpmsysparams_keys() {
assert!(fillpmsysparams("pid").is_some());
assert!(fillpmsysparams("ppid").is_some());
assert!(fillpmsysparams("procsubstpid").is_some());
assert!(fillpmsysparams("nonsense").is_none());
}
#[test]
fn getpmsysparams_pid_set() {
assert!(getpmsysparams("pid").is_some());
assert!(getpmsysparams("nonsense").is_none());
}
#[test]
fn scanpmsysparams_three_entries() {
let entries = scanpmsysparams();
let names: Vec<&str> = entries.iter().map(|(n,_)| n.as_str()).collect();
assert!(names.contains(&"pid"));
assert!(names.contains(&"ppid"));
assert!(names.contains(&"procsubstpid"));
}
fn empty_ops() -> crate::ported::zsh_h::options {
use crate::ported::zsh_h::{options, MAX_OPS};
options { ind: [0u8; MAX_OPS], args: Vec::new(),
argscount: 0, argsalloc: 0 }
}
fn ops_with(args: &[(u8, &str)]) -> crate::ported::zsh_h::options {
let mut ops = empty_ops();
for (idx, (opt, val)) in args.iter().enumerate() {
ops.ind[*opt as usize] = (((idx + 1) << 2) | 1) as u8;
ops.args.push(val.to_string());
ops.argscount = (idx + 1) as i32;
ops.argsalloc = (idx + 1) as i32;
}
ops
}
#[test]
fn bin_syserror_to_errvar_with_prefix() {
let ops = ops_with(&[(b'e', "myerr"), (b'p', "PFX:")]);
let r = bin_syserror("syserror",
&["ENOENT".to_string()], &ops, 0);
assert_eq!(r, 0);
let val = crate::ported::params::paramtab().read().ok()
.and_then(|t| t.get("myerr").and_then(|p| p.u_str.clone()))
.unwrap_or_default();
assert!(val.starts_with("PFX:"), "expected PFX: prefix, got {:?}", val);
}
#[test]
fn bin_syserror_unknown_name_returns_2() {
let ops = ops_with(&[(b'e', "myerr2")]);
assert_eq!(bin_syserror("syserror",
&["ENOTAREALERROR".to_string()], &ops, 0), 2);
}
#[test]
#[cfg(unix)]
fn bin_sysopen_writes_fd_to_var() {
let saved_exec = crate::ported::options::opt_state_get("exec").unwrap_or(false);
crate::ported::options::opt_state_set("exec", true);
let dir = TempDir::new().unwrap();
let p = dir.path().join("a.txt");
let ops = ops_with(&[(b'u', "MYFD"), (b'o', "creat")]);
let mut ops = ops;
ops.ind[b'w' as usize] = 1;
let r = bin_sysopen("sysopen",
&[p.to_str().unwrap().to_string()], &ops, 0);
assert_eq!(r, 0);
let fd: i32 = crate::ported::params::paramtab().read().ok()
.and_then(|t| t.get("MYFD").map(|p| p.u_val as i32))
.expect("MYFD not set by sysopen");
assert!(fd >= 10, "movefd should lift fd to 10+, got {}", fd);
unsafe { libc::close(fd); }
crate::ported::options::opt_state_set("exec", saved_exec);
}
#[test]
#[cfg(unix)]
fn bin_sysseek_basic() {
let dir = TempDir::new().unwrap();
let p = dir.path().join("b.txt");
{
let mut f = File::create(&p).unwrap();
f.write_all(b"hello world").unwrap();
}
let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
let fd = unsafe { libc::open(path_c.as_ptr(), libc::O_RDONLY) };
assert!(fd >= 0);
let ops = ops_with(&[(b'u', &fd.to_string()), (b'w', "start")]);
let r = bin_sysseek("sysseek", &["5".to_string()], &ops, 0);
assert_eq!(r, 0);
unsafe { libc::close(fd); }
}
#[test]
fn setiparam_writes_integer_to_paramtab() {
let saved_exec = crate::ported::options::opt_state_get("exec").unwrap_or(false);
crate::ported::options::opt_state_set("exec", true);
let name = "ZSHRS_TEST_SETIPARAM_FD_INT";
let _ = crate::ported::params::setiparam(name, 12345);
let val = crate::ported::params::paramtab().read().ok()
.and_then(|t| t.get(name).map(|p| p.u_val));
crate::ported::options::opt_state_set("exec", saved_exec);
assert_eq!(val, Some(12345),
"setiparam should put the integer in paramtab().get(name).u_val");
}
#[test]
#[cfg(unix)]
fn math_systell_returns_lseek_cur() {
let dir = TempDir::new().unwrap();
let p = dir.path().join("c.txt");
{
let mut f = File::create(&p).unwrap();
f.write_all(b"hello world").unwrap();
}
let path_c = std::ffi::CString::new(p.to_str().unwrap()).unwrap();
let fd = unsafe { libc::open(path_c.as_ptr(), libc::O_RDONLY) };
unsafe { libc::lseek(fd, 7, libc::SEEK_SET); }
let argv = vec![mnumber { l: fd as i64, d: 0.0, type_: MN_INTEGER }];
let r = math_systell("systell", 1, &argv, 0);
assert_eq!(r.type_, MN_INTEGER);
assert_eq!(r.l, 7);
unsafe { libc::close(fd); }
}
}