#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use std::sync::Mutex;
use std::sync::OnceLock;
use std::sync::atomic::{AtomicI32, Ordering};
use crate::ported::utils::{unmetafy, zerrnam, zwarnnam};
use crate::ported::zsh_h::{module, options};
use std::ffi::CString;
use std::os::unix::io::RawFd;
pub static coprocin: AtomicI32 = AtomicI32::new(-1);
pub static coprocout: AtomicI32 = AtomicI32::new(-1);
pub static mypgrp: AtomicI32 = AtomicI32::new(0);
pub static lastpid: AtomicI32 = AtomicI32::new(0);
pub static ttystrname: Mutex<String> = Mutex::new(String::new());
#[cfg(unix)]
#[allow(unused_variables)]
pub fn bin_clone(nam: &str, args: &[String], ops: &options, func: i32) -> i32 {
let ttyfd: RawFd;
let pid: libc::pid_t;
let cttyfd: RawFd;
let arg0_in: &str = match args.first() {
Some(a) => a.as_str(),
None => {
zwarnnam(nam, "terminal required");
return 1;
}
};
let mut arg0_bytes = arg0_in.as_bytes().to_vec();
unmetafy(&mut arg0_bytes);
let arg0: String = String::from_utf8_lossy(&arg0_bytes).into_owned();
let tty_c = match CString::new(arg0.clone()) {
Ok(c) => c,
Err(_) => {
zwarnnam(nam, &format!("{}: invalid tty path", arg0));
return 1;
}
};
ttyfd = unsafe { libc::open(tty_c.as_ptr(), libc::O_RDWR | libc::O_NOCTTY) };
if ttyfd < 0 { zwarnnam(nam, &format!("{}: {}", arg0, std::io::Error::last_os_error())); return 1; }
pid = unsafe { libc::fork() };
if pid == 0 { if let Some(tab) = crate::ported::jobs::JOBTAB.get() {
if let Ok(mut jobs) = tab.lock() {
jobs.clear();
}
}
let mypid = unsafe { libc::getpid() };
if unsafe { libc::setsid() } != mypid {
zwarnnam(
nam,
&format!("failed to create new session: {}", std::io::Error::last_os_error()), );
}
unsafe {
libc::dup2(ttyfd, 0); libc::dup2(ttyfd, 1); libc::dup2(ttyfd, 2); }
if ttyfd > 2 {
unsafe { libc::close(ttyfd) };
}
unsafe { libc::close(coprocin.load(Ordering::Relaxed)) }; unsafe { libc::close(coprocout.load(Ordering::Relaxed)) }; cttyfd = unsafe { libc::open(tty_c.as_ptr(), libc::O_RDWR) };
if cttyfd == -1 { zwarnnam(nam, &format!("{}", std::io::Error::last_os_error())); } else { #[cfg(any(target_os = "linux", target_os = "macos"))]
unsafe {
libc::ioctl(cttyfd, libc::TIOCSCTTY as _, 0);
}
unsafe { libc::close(cttyfd) }; }
let dev_tty = b"/dev/tty\0".as_ptr() as *const libc::c_char;
let cttyfd2 = unsafe { libc::open(dev_tty, libc::O_RDWR) };
if cttyfd2 == -1 { zwarnnam( nam,
&format!("could not make {} my controlling tty, job control disabled", arg0),
);
} else { unsafe { libc::close(cttyfd2) }; }
mypgrp.store(0, Ordering::Relaxed); crate::ported::init::init_io(None); let tty_name = ttystrname.lock().unwrap().clone();
crate::ported::params::setsparam("TTY", &tty_name); } else { unsafe { libc::close(ttyfd) }; }
if pid < 0 { zerrnam(nam, &format!("fork failed: {}", std::io::Error::last_os_error())); return 1; }
lastpid.store(pid as i32, Ordering::Relaxed); 0 }
#[cfg(not(unix))]
#[allow(unused_variables)]
pub fn bin_clone(nam: &str, args: &[String], ops: &options, func: i32) -> i32 {
zwarnnam(nam, "not available on this host");
1
}
#[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(test)]
mod tests {
use super::*;
use crate::ported::zsh_h::MAX_OPS;
fn empty_ops() -> options {
options { ind: [0u8; MAX_OPS], args: Vec::new(), argscount: 0, argsalloc: 0 }
}
#[test]
#[cfg(unix)]
fn bin_clone_no_args_returns_one() {
let ops = empty_ops();
assert_eq!(bin_clone("clone", &[], &ops, 0), 1);
}
#[test]
#[cfg(unix)]
fn bin_clone_invalid_tty_returns_one() {
let ops = empty_ops();
let rc = bin_clone("clone", &["/nonexistent/tty".to_string()], &ops, 0);
assert_eq!(rc, 1);
}
#[test]
fn module_loaders_return_zero() {
let mut features: Vec<String> = Vec::new();
let mut enables: Option<Vec<i32>> = None;
let m: *const module = std::ptr::null();
assert_eq!(setup_(m), 0);
assert_eq!(features_(m, &mut features), 0);
assert_eq!(features, vec!["b:clone"]);
assert_eq!(enables_(m, &mut enables), 0);
assert!(enables.is_some());
assert_eq!(boot_(m), 0);
assert_eq!(cleanup_(m), 0);
assert_eq!(finish_(m), 0);
}
}
use crate::ported::zsh_h::features as features_t;
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 1,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
}))
}
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["b:clone".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; 1]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}