#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Mutex, OnceLock};
use crate::ported::builtins::sched::zleactive;
use crate::ported::params::{createparam, paramtab, setiparam, setloopvar, setsparam};
use crate::ported::signals_h::{queue_signals, unqueue_signals};
use crate::ported::string::{dupstring, ztrdup};
use crate::ported::zsh_h::{eprog, features, funcstack, funcwrap, isset, module, param, paramdef, EMULATE_KSH, EMULATION, KSHARRAYS, PARAMDEF, PM_LOCAL, PM_NAMEREF, PM_READONLY, PM_UNSET, VIMODE};
use crate::ported::ztype_h::INAMESPC;
pub use crate::ported::options::emulation;
pub use crate::ported::params::locallevel;
use crate::zsh_h::{PM_ARRAY, PM_SCALAR, PM_SPECIAL};
#[allow(unused_variables)]
pub fn edcharsetfn(pm: *mut param, x: *mut libc::c_char) { }
pub fn matchgetfn(pm: *mut param) -> Vec<String> {
let zsh_match: Vec<String> = paramtab()
.read()
.ok()
.and_then(|t| t.get("match").and_then(|p| p.u_arr.clone()))
.unwrap_or_default();
if !pm.is_null() {
unsafe {
(*pm).u_arr = None;
} }
if !zsh_match.is_empty() {
if isset(KSHARRAYS) {
let match_str: String = paramtab()
.read()
.ok()
.and_then(|t| t.get("MATCH").and_then(|p| p.u_str.clone()))
.unwrap_or_default();
let mut ap: Vec<String> = Vec::with_capacity(zsh_match.len() + 1);
ap.push(ztrdup(&match_str)); for s in &zsh_match {
ap.push(ztrdup(s));
}
if !pm.is_null() {
unsafe {
(*pm).u_arr = Some(ap.clone());
}
}
ap
} else {
let dup: Vec<String> = zsh_match
.iter()
.map(|s| ztrdup(s))
.collect();
if !pm.is_null() {
unsafe {
(*pm).u_arr = Some(dup.clone());
}
}
dup }
} else if isset(KSHARRAYS) {
let match_str: String = paramtab()
.read()
.ok()
.and_then(|t| t.get("MATCH").and_then(|p| p.u_str.clone()))
.unwrap_or_default();
let one = vec![ztrdup(&match_str)];
if !pm.is_null() {
unsafe {
(*pm).u_arr = Some(one.clone());
}
}
one } else {
if !pm.is_null() {
unsafe {
(*pm).u_arr = None;
}
}
Vec::new() }
}
pub static sh_unsetval: [u8; 2] = [0, 0];
pub static sh_name: Mutex<String> = Mutex::new(String::new());
pub static sh_subscript: Mutex<String> = Mutex::new(String::new());
pub static sh_edchar: Mutex<String> = Mutex::new(String::new());
pub static sh_edmode: Mutex<[u8; 2]> = Mutex::new([0, 0]);
#[allow(unused_variables)]
pub fn ksh93_wrapper(prog: *const eprog, w: *const funcwrap, name: *mut libc::c_char) -> i32 {
let mut f: *const funcstack;
let mut pm: *mut param;
let mut num: i64 = if (*funcstack.lock().unwrap()) != 0 {
paramtab()
.read()
.ok()
.and_then(|t| {
t.get(".sh.level")
.and_then(|p| p.u_str.as_ref().and_then(|s| s.parse::<i64>().ok()))
})
.unwrap_or(0)
} else {
0
};
if !EMULATION(EMULATE_KSH) {
return 1; }
if num == 0 {
let _ = f; if let Ok(stack) = crate::ported::modules::parameter::FUNCSTACK.lock() {
num = stack.len() as i64;
}
} else {
num += 1; }
queue_signals(); locallevel.fetch_add(1, Ordering::SeqCst);
pm = createparam(".sh.command", LOCAL_NAMEREF as i32)
.map(Box::into_raw)
.unwrap_or(std::ptr::null_mut());
if !pm.is_null() {
unsafe {
(*pm).level = locallevel.load(Ordering::Relaxed);
} setloopvar(".sh.command", "ZSH_DEBUG_CMD"); unsafe {
(*pm).node.flags |= PM_READONLY as i32;
} }
if zleactive.load(Ordering::Relaxed) != 0 {
pm = createparam(".sh.edcol", LOCAL_NAMEREF as i32) .map(Box::into_raw)
.unwrap_or(std::ptr::null_mut());
if !pm.is_null() {
unsafe {
(*pm).level = locallevel.load(Ordering::Relaxed);
} setloopvar(".sh.edcol", "CURSOR"); unsafe {
(*pm).node.flags |= (PM_NAMEREF | PM_READONLY) as i32;
} }
}
if zleactive.load(Ordering::Relaxed) != 0 {
pm = createparam(".sh.edtext", LOCAL_NAMEREF as i32) .map(Box::into_raw)
.unwrap_or(std::ptr::null_mut());
if !pm.is_null() {
unsafe {
(*pm).level = locallevel.load(Ordering::Relaxed);
} setloopvar(".sh.edtext", "BUFFER"); unsafe {
(*pm).node.flags |= PM_READONLY as i32;
} }
}
pm = createparam(
".sh.fun", (PM_LOCAL | PM_UNSET) as i32,
)
.map(Box::into_raw)
.unwrap_or(std::ptr::null_mut());
if !pm.is_null() {
unsafe {
(*pm).level = locallevel.load(Ordering::Relaxed);
} let name_str: String = if name.is_null() {
String::new()
} else {
unsafe {
std::ffi::CStr::from_ptr(name)
.to_string_lossy()
.into_owned()
}
};
setsparam(
".sh.fun", &ztrdup(&name_str),
);
unsafe {
(*pm).node.flags |= PM_READONLY as i32;
} }
pm = createparam(
".sh.level", (PM_LOCAL | PM_UNSET) as i32,
)
.map(Box::into_raw)
.unwrap_or(std::ptr::null_mut());
if !pm.is_null() {
unsafe {
(*pm).level = locallevel.load(Ordering::Relaxed);
} setiparam(".sh.level", num); }
if zleactive.load(Ordering::Relaxed) != 0 {
let curkmap = curkeymapname.lock().unwrap().clone();
if !curkmap.is_empty() && isset(VIMODE) && curkmap == "main" {
let mut em = sh_edmode.lock().unwrap();
em[0] = 0o33;
em[1] = 0;
} else {
let mut em = sh_edmode.lock().unwrap();
em[0] = 0;
em[1] = 0;
}
let edch_unset = sh_edchar.lock().unwrap().is_empty();
if edch_unset {
let keys: String = paramtab()
.read()
.ok()
.and_then(|t| t.get("KEYS").and_then(|p| p.u_str.clone()))
.unwrap_or_default();
*sh_edchar.lock().unwrap() = dupstring(&keys);
}
let varedarg_val = varedarg.lock().unwrap().clone();
if !varedarg_val.is_empty() {
*sh_name.lock().unwrap() = dupstring(&varedarg_val);
let nm = sh_name.lock().unwrap().clone();
let _ = INAMESPC;
let ie_off = {
let mut k = 0;
for &b in nm.as_bytes() {
match b {
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' => k += 1,
_ => break,
}
}
k
};
if ie_off < nm.len() {
let head = &nm[..ie_off];
let tail = &nm[ie_off + 1..]; *sh_name.lock().unwrap() = head.to_string();
*sh_subscript.lock().unwrap() = dupstring(tail);
let mut sub = sh_subscript.lock().unwrap();
if sub.ends_with(']') {
sub.pop();
}
} else {
sh_subscript.lock().unwrap().clear();
}
pm = createparam(
".sh.value", LOCAL_NAMEREF as i32,
)
.map(Box::into_raw)
.unwrap_or(std::ptr::null_mut());
if !pm.is_null() {
unsafe {
(*pm).level = locallevel.load(Ordering::Relaxed);
} setloopvar(".sh.value", "BUFFER");
unsafe {
(*pm).node.flags |= PM_READONLY as i32;
} }
} else {
sh_name.lock().unwrap().clear();
sh_subscript.lock().unwrap().clear();
}
} else {
sh_edchar.lock().unwrap().clear();
sh_name.lock().unwrap().clear();
sh_subscript.lock().unwrap().clear();
let mut em = sh_edmode.lock().unwrap();
em[0] = 0;
em[1] = 0;
}
locallevel.fetch_sub(1, Ordering::SeqCst); unqueue_signals();
1 }
#[allow(dead_code)]
const LOCAL_NAMEREF: u32 = PM_LOCAL | PM_UNSET | PM_NAMEREF;
#[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) }
pub fn boot_(m: *const module) -> i32 {
let _ = m;
0
}
pub fn cleanup_(m: *const module) -> i32 {
let mut p: usize;
let partab: [paramdef; 9] = [
PARAMDEF(".sh.edchar", (PM_SCALAR | PM_SPECIAL) as i32, 0, 0), PARAMDEF(
".sh.edmode",
(PM_SCALAR | PM_READONLY | PM_SPECIAL) as i32,
0,
0,
), PARAMDEF(".sh.file", (PM_NAMEREF | PM_READONLY) as i32, 0, 0), PARAMDEF(".sh.lineno", (PM_NAMEREF | PM_READONLY) as i32, 0, 0), PARAMDEF(".sh.match", (PM_ARRAY | PM_READONLY) as i32, 0, 0), PARAMDEF(
".sh.name",
(PM_SCALAR | PM_READONLY | PM_SPECIAL) as i32,
0,
0,
), PARAMDEF(
".sh.subscript",
(PM_SCALAR | PM_READONLY | PM_SPECIAL) as i32,
0,
0,
), PARAMDEF(".sh.subshell", (PM_NAMEREF | PM_READONLY) as i32, 0, 0), PARAMDEF(".sh.version", (PM_NAMEREF | PM_READONLY) as i32, 0, 0), ];
p = 0;
while p < partab.len() {
let entry = &partab[p];
if (entry.flags as u32 & PM_NAMEREF) != 0 {
if let Ok(mut t) = paramtab().write() {
if let Some(pm) = t.get_mut(&entry.name) {
pm.node.flags &= !(PM_NAMEREF as i32); }
}
}
p += 1;
}
setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 {
0
}
static MODULE_FEATURES: OnceLock<Mutex<features>> = OnceLock::new();
fn featuresarray(_m: *const module, _f: &Mutex<features>) -> Vec<String> {
vec![
"b:nameref".to_string(),
"p:.sh.edchar".to_string(),
"p:.sh.edmode".to_string(),
"p:.sh.file".to_string(),
"p:.sh.lineno".to_string(),
"p:.sh.match".to_string(),
"p:.sh.name".to_string(),
"p:.sh.subscript".to_string(),
"p:.sh.subshell".to_string(),
"p:.sh.version".to_string(),
]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 10]);
}
0
}
fn setfeatureenables(_m: *const module, _f: &Mutex<features>, _e: Option<&[i32]>) -> i32 {
0
}
pub static curkeymapname: Mutex<String> = Mutex::new(String::new());
pub static varedarg: Mutex<String> = Mutex::new(String::new());
static funcstack: Mutex<usize> = Mutex::new(0);
#[allow(dead_code)]
const _: AtomicI64 = AtomicI64::new(0);
fn module_features() -> &'static Mutex<features> {
MODULE_FEATURES.get_or_init(|| {
Mutex::new(features {
bn_list: None,
bn_size: 1, cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 9, n_abstract: 0,
})
})
}
#[cfg(test)]
mod tests {
use crate::zsh_h::hashnode;
use super::*;
#[test]
fn ksh93_wrapper_returns_one_when_not_emulate_ksh() {
let _g = crate::test_util::global_state_lock();
emulation.store(0, Ordering::SeqCst);
let rc = ksh93_wrapper(std::ptr::null(), std::ptr::null(), std::ptr::null_mut());
assert_eq!(rc, 1);
}
#[test]
fn ksh93_wrapper_runs_full_body_under_emulate_ksh() {
let _g = crate::test_util::global_state_lock();
let saved = emulation.load(Ordering::SeqCst);
emulation.store(EMULATE_KSH, Ordering::SeqCst);
let rc = ksh93_wrapper(std::ptr::null(), std::ptr::null(), std::ptr::null_mut());
assert_eq!(rc, 1);
assert_eq!(locallevel.load(Ordering::SeqCst), 0);
emulation.store(saved, Ordering::SeqCst);
}
#[test]
fn matchgetfn_empty_returns_empty() {
let _g = crate::test_util::global_state_lock();
let v = matchgetfn(std::ptr::null_mut());
assert!(v.is_empty());
}
#[test]
fn edcharsetfn_noop() {
let _g = crate::test_util::global_state_lock();
edcharsetfn(std::ptr::null_mut(), std::ptr::null_mut());
}
#[test]
fn module_loaders_return_zero() {
let _g = crate::test_util::global_state_lock();
let m: *const module = std::ptr::null();
assert_eq!(setup_(m), 0);
let mut features = Vec::new();
assert_eq!(features_(m, &mut features), 0);
let mut enables: Option<Vec<i32>> = None;
assert_eq!(enables_(m, &mut enables), 0);
assert_eq!(boot_(m), 0);
assert_eq!(cleanup_(m), 0);
assert_eq!(finish_(m), 0);
}
#[test]
fn statics_default_to_unsetval() {
let _g = crate::test_util::global_state_lock();
sh_name.lock().unwrap().clear();
sh_subscript.lock().unwrap().clear();
sh_edchar.lock().unwrap().clear();
*sh_edmode.lock().unwrap() = [0, 0];
assert!(sh_name.lock().unwrap().is_empty());
assert!(sh_subscript.lock().unwrap().is_empty());
assert!(sh_edchar.lock().unwrap().is_empty());
assert_eq!(*sh_edmode.lock().unwrap(), [0u8, 0u8]);
assert_eq!(sh_unsetval, [0u8, 0u8]);
}
#[test]
fn local_nameref_matches_c_define() {
let _g = crate::test_util::global_state_lock();
assert_eq!(LOCAL_NAMEREF, PM_LOCAL | PM_UNSET | PM_NAMEREF);
}
#[test]
fn matchgetfn_with_param_no_array_returns_empty() {
let _g = crate::test_util::global_state_lock();
let mut pm = param {
node: hashnode {
next: None,
nam: "match".to_string(),
flags: 0,
},
u_data: 0,
u_arr: None,
u_str: None,
u_val: 0,
u_dval: 0.0,
u_hash: None,
gsu_s: None,
gsu_i: None,
gsu_f: None,
gsu_a: None,
gsu_h: None,
base: 0,
width: 0,
env: None,
ename: None,
old: None,
level: 0,
};
let v = matchgetfn(&mut pm as *mut _);
assert!(
v.is_empty(),
"param without array data must produce empty result"
);
}
#[test]
fn local_nameref_has_exactly_three_component_flags() {
let _g = crate::test_util::global_state_lock();
let expected = PM_LOCAL | PM_UNSET | PM_NAMEREF;
assert_eq!(LOCAL_NAMEREF, expected);
assert_eq!(LOCAL_NAMEREF & !PM_LOCAL, PM_UNSET | PM_NAMEREF);
assert_eq!(LOCAL_NAMEREF & !PM_UNSET, PM_LOCAL | PM_NAMEREF);
assert_eq!(LOCAL_NAMEREF & !PM_NAMEREF, PM_LOCAL | PM_UNSET);
}
#[test]
fn sh_unsetval_is_two_zero_bytes() {
let _g = crate::test_util::global_state_lock();
assert_eq!(
sh_unsetval,
[0u8, 0u8],
"sh_unsetval must be the C-canonical [0,0] sentinel"
);
}
#[test]
fn matchgetfn_null_pointer_is_safe() {
let _g = crate::test_util::global_state_lock();
let _ = matchgetfn(std::ptr::null_mut());
}
#[test]
fn edcharsetfn_double_null_is_safe() {
let _g = crate::test_util::global_state_lock();
edcharsetfn(std::ptr::null_mut(), std::ptr::null_mut());
edcharsetfn(std::ptr::null_mut(), std::ptr::null_mut());
}
#[test]
fn ksh93_corpus_matchgetfn_null_returns_empty() {
let _g = crate::test_util::global_state_lock();
let r = matchgetfn(std::ptr::null_mut());
assert!(r.is_empty(), "null pm → empty vec, got {r:?}");
}
#[test]
fn ksh93_corpus_lifecycle_returns_zero() {
let _g = crate::test_util::global_state_lock();
let m: *const module = std::ptr::null();
assert_eq!(setup_(m), 0);
assert_eq!(boot_(m), 0);
assert_eq!(cleanup_(m), 0);
assert_eq!(finish_(m), 0);
}
#[test]
fn ksh93_corpus_edcharsetfn_repeated_null_no_panic() {
let _g = crate::test_util::global_state_lock();
for _ in 0..100 {
edcharsetfn(std::ptr::null_mut(), std::ptr::null_mut());
}
}
}