use crate::ported::builtin::createbuiltintable;
use crate::ported::params::{createparam, createspecialhash, paramtab, unsetparam_pm};
use crate::ported::signals::unqueue_signals;
use crate::ported::utils::{zwarn, zwarnnam};
use crate::ported::zsh_h::{BINF_AUTOALL, CONDF_AUTOALL, HOOKF_ALL, Hookfn, MFF_USERFUNC, MOD_ALIAS, MOD_BUSY, MOD_INIT_B, MOD_INIT_S, MOD_LINKED, MOD_SETUP, MOD_UNLOAD, OPT_ISSET, PM_ARRAY, PM_AUTOLOAD, PM_EFLOAT, PM_FFLOAT, PM_HASHED, PM_INTEGER, PM_NAMEREF, PM_READONLY, PM_REMOVABLE, PM_SCALAR, PM_TIED, PM_TYPE, builtin, conddef, funcwrap, hookdef, mathfunc, options, paramdef, CASMOD_LOWER, CASMOD_UPPER, Param, linknode, linklist, PM_AUTOALL, PRINT_LIST};
use crate::zsh_h::module;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::Mutex;
use crate::ported::hist::casemodify;
use crate::ported::mem::ztrdup;
pub use crate::ported::zsh_h::{BINF_ADDED, CONDF_ADDED, CONDF_INFIX, MFF_ADDED};
pub fn freemodulenode(hn: module) {
}
pub const PRINTMOD_LIST: i32 = 0x0001; pub const PRINTMOD_EXIST: i32 = 0x0002; pub const PRINTMOD_ALIAS: i32 = 0x0004; pub const PRINTMOD_DEPS: i32 = 0x0008; pub const PRINTMOD_FEATURES: i32 = 0x0010; pub const PRINTMOD_LISTALL: i32 = 0x0020; pub const PRINTMOD_AUTO: i32 = 0x0040;
pub fn printmodulenode(hn: &str, m: &module, flags: i32) -> String {
let modname = hn;
let mut out = String::new();
if flags & PRINTMOD_DEPS != 0 {
let deps = match m.deps.as_ref() {
Some(d) if !d.is_empty() => d,
_ => return out,
};
if flags & PRINTMOD_LIST != 0 {
out.push_str("zmodload -d ");
if modname.starts_with('-') {
out.push_str("-- ");
}
out.push_str(modname);
} else {
out.push_str(modname);
out.push(':');
}
for dep in deps.iter() {
out.push(' ');
out.push_str(dep);
}
return out;
}
if flags & PRINTMOD_EXIST != 0 {
if (m.node.flags & MOD_ALIAS) != 0
&& (flags & PRINTMOD_ALIAS == 0 || m.alias.is_none())
{
return out;
}
if m.node.flags & MOD_UNLOAD != 0 {
return out;
}
out.push_str(modname);
return out;
}
if m.node.flags & MOD_ALIAS != 0 {
let alias = m.alias.as_deref().unwrap_or("");
if flags & PRINTMOD_LIST != 0 {
out.push_str("zmodload -A ");
if modname.starts_with('-') {
out.push_str("-- ");
}
out.push_str(modname);
out.push('=');
out.push_str(alias);
} else {
out.push_str(modname);
out.push_str(" -> ");
out.push_str(alias);
}
return out;
}
let loaded = (m.node.flags & MOD_LINKED) != 0 && (m.node.flags & MOD_UNLOAD) == 0;
let auto = flags & PRINTMOD_AUTO != 0;
if loaded || auto {
if flags & PRINTMOD_LIST != 0 {
out.push_str("zmodload ");
if auto {
out.push_str("-Fa ");
} else if flags & PRINTMOD_FEATURES != 0 {
out.push_str("-F ");
}
if modname.starts_with('-') {
out.push_str("-- ");
}
out.push_str(modname);
} else {
out.push_str(modname);
}
}
out
}
pub fn newmoduletable() -> modulestab {
modulestab::new()
}
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 {
0 }
#[allow(unused_variables)]
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 {
1 }
#[allow(unused_variables)]
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 {
1 }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 {
0 }
#[allow(unused_variables)]
pub fn cleanup_(m: *const module) -> i32 {
0 }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 {
0 }
pub fn register_module(table: &mut modulestab, name: &str) -> bool {
if table.modules.contains_key(name) {
return false;
}
table.modules.insert(name.to_string(), module::new(name));
true
}
pub fn addbuiltin(b: &mut builtin) -> i32 {
let tab = createbuiltintable();
if let Some(existing) = tab.get(&b.node.nam) {
if (existing.node.flags & BINF_ADDED as i32) != 0 {
return 1;
} }
b.node.flags |= BINF_ADDED as i32; 0
}
pub fn addbuiltins(nam: &str, binl: &mut [builtin]) -> i32 {
let mut ret = 0; for b in binl.iter_mut() {
if (b.node.flags & BINF_ADDED as i32) != 0 {
continue;
} if addbuiltin(b) != 0 {
zwarnnam(
nam, &format!("name clash when adding builtin `{}'", b.node.nam),
);
ret = 1;
}
}
ret }
pub fn gethookdef(n: &str) -> *mut hookdef {
let mut p = hooktab.load(std::sync::atomic::Ordering::SeqCst); while !p.is_null() {
unsafe {
if (*p).name == n {
return p; }
p = (*p).next; }
}
std::ptr::null_mut() }
pub fn addhookdef(h: *mut hookdef) -> i32 {
unsafe {
if !gethookdef(&(*h).name).is_null() {
return 1; }
(*h).next = hooktab.load(std::sync::atomic::Ordering::SeqCst); hooktab.store(h, std::sync::atomic::Ordering::SeqCst); if (*h).funcs.is_null() {
(*h).funcs = Box::into_raw(Box::new(linklist {
first: None,
last: None,
flags: 0,
}));
}
}
0 }
pub fn addhookdefs(
m: *const module,
mut h: *mut hookdef,
mut size: i32,
) -> i32 {
let mut ret: i32 = 0; while size > 0 {
if addhookdef(h) != 0 {
let nam: String = if m.is_null() {
String::new()
} else {
unsafe { (*m).node.nam.clone() }
};
let hook_name = unsafe { (*h).name.clone() };
zwarnnam(
&nam,
&format!("name clash when adding hook `{}'", hook_name),
);
ret = 1; }
unsafe {
h = h.add(1);
} size -= 1; }
ret }
pub fn deletehookdef(h: *mut hookdef) -> i32 {
let mut p = hooktab.load(std::sync::atomic::Ordering::SeqCst); let mut q: *mut hookdef = std::ptr::null_mut(); while !p.is_null() && p != h {
q = p; unsafe {
p = (*p).next;
} }
if p.is_null() {
return 1; }
unsafe {
if !q.is_null() {
(*q).next = (*p).next; } else {
hooktab.store((*p).next, std::sync::atomic::Ordering::SeqCst); }
if !(*p).funcs.is_null() {
drop(Box::from_raw((*p).funcs));
(*p).funcs = std::ptr::null_mut();
}
}
0 }
pub fn deletehookdefs(
_m: *const module,
mut h: *mut hookdef,
mut size: i32,
) -> i32 {
let mut ret: i32 = 0; while size > 0 {
if deletehookdef(h) != 0 {
ret = 1; }
unsafe {
h = h.add(1);
} size -= 1; }
ret }
pub fn addhookdeffunc(
h: *mut hookdef,
f: Hookfn,
) -> i32 {
unsafe {
if (*h).funcs.is_null() {
(*h).funcs = Box::into_raw(Box::new(linklist {
first: None,
last: None,
flags: 0,
}));
}
let funcs = &mut *(*h).funcs;
let new_node = Box::new(linknode {
next: None,
prev: None,
dat: f as usize,
});
if funcs.first.is_none() {
funcs.first = Some(new_node);
} else {
let mut tail = funcs.first.as_mut().unwrap();
while tail.next.is_some() {
tail = tail.next.as_mut().unwrap();
}
tail.next = Some(new_node);
}
}
0 }
pub fn addhookfunc(
n: &str,
f: Hookfn,
) -> i32 {
let h = gethookdef(n); if !h.is_null() {
return addhookdeffunc(h, f); }
1 }
pub fn deletehookdeffunc(
h: *mut hookdef,
f: Hookfn,
) -> i32 {
unsafe {
if (*h).funcs.is_null() {
return 1;
}
let funcs = &mut *(*h).funcs;
let f_val = f as usize;
let mut prev: &mut Option<Box<linknode>> = &mut funcs.first;
loop {
match prev {
None => return 1, Some(node) if node.dat == f_val => {
let next = node.next.take(); *prev = next;
return 0; }
Some(_) => {
prev = &mut prev.as_mut().unwrap().next;
}
}
}
}
}
pub fn deletehookfunc(
n: &str,
f: Hookfn,
) -> i32 {
let h = gethookdef(n); if !h.is_null() {
return deletehookdeffunc(h, f); }
1 }
pub fn runhookdef(
h: *mut hookdef,
d: *mut std::ffi::c_void,
) -> i32 {
unsafe {
let funcs_ptr = (*h).funcs;
let funcs_empty = funcs_ptr.is_null() || (*funcs_ptr).first.is_none(); if funcs_empty {
if let Some(def) = (*h).def {
return def(h, d); }
return 0; }
if (*h).flags & HOOKF_ALL != 0 {
let mut node = (*funcs_ptr).first.as_ref(); while let Some(n) = node {
let fn_ptr: Hookfn = std::mem::transmute(n.dat); let r = fn_ptr(h, d); if r != 0 {
return r; }
node = n.next.as_ref(); }
if let Some(def) = (*h).def {
return def(h, d); }
return 0; }
let mut tail = (*funcs_ptr).first.as_ref().expect("non-empty");
while let Some(next) = tail.next.as_ref() {
tail = next;
}
let fn_ptr: Hookfn = std::mem::transmute(tail.dat);
fn_ptr(h, d) }
}
pub fn checkaddparam(nam: &str, opt_i: i32) -> i32 {
let pm_clone = {
let tab = paramtab()
.read()
.expect("paramtab poisoned");
tab.get(nam).cloned()
};
let mut pm = match pm_clone {
Some(p) => p,
None => return 0,
};
if pm.level != 0 || (pm.node.flags as u32 & PM_AUTOLOAD) == 0 {
if opt_i == 0 || pm.level != 0 {
zwarn(&format!(
"Can't add module parameter `{}': {}",
nam,
if pm.level != 0 {
"local parameter exists"
} else {
"parameter already exists"
}
));
return 1;
}
return 2;
}
unsetparam_pm(&mut pm, 0, 1);
0
}
pub fn addparamdef(d: &mut paramdef) -> i32 {
if checkaddparam(&d.name, 0) != 0 {
return 1; }
let pm_opt: Option<Param> = if d.getnfn.is_some() {
createspecialhash(&d.name, d.flags) } else {
match createparam(&d.name, d.flags) {
Some(p) => Some(p),
None => {
let tab = paramtab().read().ok();
tab.and_then(|t| {
t.get(&d.name).map(|p| {
let mut clone = p.clone();
clone.level = 0;
Box::new(*clone)
})
})
}
}
};
let mut pm = match pm_opt {
Some(p) => p,
None => return 1,
};
pm.level = 0;
if d.var != 0 { }
if d.var != 0 || d.gsu != 0 {
let t = PM_TYPE(pm.node.flags as u32); let pmflags = pm.node.flags as u32;
if t == PM_SCALAR || t == PM_NAMEREF {
if t == PM_SCALAR && (pmflags & PM_TIED) != 0 {
let lower = casemodify(
&pm.node.nam,
CASMOD_LOWER,
);
pm.ename = Some(ztrdup(&lower)); }
let _ = d.gsu; } else if t == PM_INTEGER {
let _ = d.gsu; } else if t == PM_FFLOAT || t == PM_EFLOAT {
let _ = d.gsu; } else if t == PM_ARRAY {
if (pmflags & PM_TIED) != 0 {
let upper = casemodify(
&pm.node.nam,
CASMOD_UPPER,
);
pm.ename = Some(ztrdup(&upper)); }
let _ = d.gsu; } else if t == PM_HASHED {
let _ = d.gsu; } else {
unsetparam_pm(&mut pm, 0, 1); return 1; }
}
d.pm = Some(pm); 0 }
pub fn deleteparamdef(d: &mut paramdef) -> i32 {
let mut pm: Param = {
let tab = paramtab().read();
match tab {
Ok(t) => match t.get(&d.name) {
Some(p) => p.clone(),
None => return 1, },
Err(_) => return 1,
}
};
if let Some(expected) = d.pm.as_ref() {
if !std::ptr::eq(pm.as_ref(), expected.as_ref()) {
let mut searchpm = pm.old.clone(); let mut found = false;
while let Some(s) = searchpm {
if std::ptr::eq(s.as_ref(), expected.as_ref()) {
found = true; break;
}
searchpm = s.old.clone(); }
if !found {
return 1; }
}
}
pm.node.flags = (pm.node.flags & !(PM_READONLY as i32)) | (PM_REMOVABLE as i32); unsetparam_pm(&mut pm, 0, 1); d.pm = None; 0 }
impl modulestab {
pub fn new() -> Self {
let mut table = Self::default();
table.register_builtin_modules();
table
}
fn register_builtin_modules(&mut self) {
let builtin_modules = [
(
"zsh/complete",
&[
"compctl",
"compcall",
"comparguments",
"compdescribe",
"compfiles",
"compgroups",
"compquote",
"comptags",
"comptry",
"compvalues",
][..],
),
("zsh/complist", &["complist"][..]),
("zsh/computil", &["compadd", "compset"][..]),
("zsh/datetime", &["output_strftime"][..]),
(
"zsh/files",
&[
"mkdir", "rmdir", "ln", "mv", "cp", "rm", "chmod", "chown", "sync",
][..],
),
("zsh/langinfo", &[][..]),
("zsh/mapfile", &[][..]),
("zsh/mathfunc", &[][..]),
("zsh/nearcolor", &[][..]),
("zsh/net/socket", &["zsocket"][..]),
("zsh/net/tcp", &["ztcp"][..]),
("zsh/parameter", &[][..]),
(
"zsh/pcre",
&["pcre_compile", "pcre_match", "pcre_study"][..],
),
("zsh/regex", &[][..]),
("zsh/sched", &["sched"][..]),
("zsh/stat", &["zstat"][..]),
(
"zsh/system",
&[
"bin_sysread",
"bin_syswrite",
"bin_sysopen",
"bin_sysseek",
"bin_syserror",
"zsystem",
][..],
),
("zsh/termcap", &["echotc"][..]),
("zsh/terminfo", &["echoti"][..]),
("zsh/watch", &["log"][..]),
("zsh/zftp", &["zftp"][..]),
("zsh/zleparameter", &[][..]),
("zsh/zprof", &["zprof"][..]),
("zsh/zpty", &["zpty"][..]),
("zsh/zselect", &["zselect"][..]),
(
"zsh/zutil",
&["zstyle", "zformat", "zparseopts", "zregexparse"][..],
),
(
"zsh/attr",
&["zgetattr", "zsetattr", "zdelattr", "zlistattr"][..],
),
("zsh/cap", &["cap", "getcap", "setcap"][..]),
("zsh/clone", &["clone"][..]),
("zsh/curses", &["zcurses"][..]),
("zsh/db/gdbm", &["ztie", "zuntie", "zgdbmpath"][..]),
("zsh/param/private", &["private"][..]),
];
for (name, _builtins) in &builtin_modules {
let module = module::new(name);
self.modules.insert(name.to_string(), module);
}
}
pub fn load_module(&mut self, name: &str) -> bool {
if modname_ok(name) == 0 {
return false; }
crate::ported::signals::queue_signals(); let exists = self.modules.contains_key(name);
if !exists {
unqueue_signals(); return false; }
if let Some(m) = self.modules.get(name) {
if (m.node.flags & MOD_SETUP) != 0 {
unqueue_signals(); return true; }
}
if let Some(m) = self.modules.get_mut(name) {
if (m.node.flags & MOD_UNLOAD) != 0 {
m.node.flags &= !MOD_UNLOAD; } else if (m.node.flags & MOD_LINKED) != 0 && (m.node.flags & MOD_INIT_B) != 0 {
unqueue_signals(); return true; }
if (m.node.flags & MOD_BUSY) != 0 {
unqueue_signals(); return false; }
m.node.flags |= MOD_BUSY; m.node.flags &= !MOD_BUSY; m.node.flags |= MOD_LINKED; m.node.flags |= MOD_INIT_S; m.node.flags |= MOD_SETUP; m.node.flags |= MOD_INIT_B; m.node.flags &= !MOD_SETUP; }
unqueue_signals(); true }
pub fn unload_module(&mut self, name: &str) -> bool {
let _ = MOD_ALIAS;
if let Some(m) = self.modules.get_mut(name) {
m.node.flags &= !(MOD_INIT_B | MOD_INIT_S);
let _ = MOD_LINKED;
m.node.flags |= MOD_UNLOAD; true } else {
false }
}
pub fn is_loaded(&self, name: &str) -> bool {
self.modules
.get(name)
.map(|m| m.is_loaded())
.unwrap_or(false)
}
pub fn list_loaded(&self) -> Vec<&str> {
self.modules
.iter()
.filter(|(_, m)| m.is_loaded())
.map(|(name, _)| name.as_str())
.collect()
}
pub fn list_all(&self) -> Vec<(&str, i32)> {
self.modules
.iter()
.map(|(name, m)| (name.as_str(), m.node.flags))
.collect()
}
pub fn addbuiltin(&mut self, _name: &str, _module: &str) { }
pub fn deletebuiltin(&mut self, _name: &str, _module: &str) { }
pub fn add_autobin(&mut self, name: &str, module: &str, flags: i32) -> i32 {
let mut node_flags: i32 = 0; if (flags & FEAT_AUTOALL as i32) != 0 {
node_flags |= BINF_AUTOALL as i32; }
let _ = node_flags; let prior = self
.autoload_builtins
.insert(name.to_string(), module.to_string());
if prior.is_some() {
if (flags & FEAT_IGNORE as i32) == 0 {
return 1; }
}
0 }
pub fn del_autobin(&mut self, name: &str, flags: i32) -> i32 {
let bn = createbuiltintable().get(name);
if bn.is_none() {
if !self.autoload_builtins.contains_key(name) {
if (flags & FEAT_IGNORE as i32) == 0 {
return 2; }
return 0;
}
self.autoload_builtins.remove(name); return 0; }
if (flags & FEAT_IGNORE as i32) == 0 {
return 3; }
0 }
pub fn setbuiltins(&mut self, module: &str, names: &[&str], e: Option<&[i32]>) -> i32 {
let mut ret: i32 = 0; for (n, name) in names.iter().enumerate() {
let enable = e
.map(|arr| arr.get(n).copied().unwrap_or(0)) .unwrap_or(1);
let already_added = self.added_builtins.contains_key(*name); if enable != 0 {
if already_added {
continue;
} self.addbuiltin(name, module);
self.added_builtins.insert(name.to_string(), BINF_ADDED); } else {
if !already_added {
continue;
} self.added_builtins.remove(*name); }
let _ = ret;
}
ret }
pub fn addconddef(&mut self, _name: &str, _module: &str) { }
pub fn deleteconddef(&mut self, _name: &str, _module: &str) {
}
pub fn getconddef(&self, name: &str) -> Option<&str> {
self.autoload_conditions.get(name).map(|s| s.as_str())
}
pub fn add_autocond(&mut self, name: &str, module: &str, flags: i32) -> i32 {
let mut cflags: i32 = if (flags & FEAT_INFIX) != 0 {
CONDF_INFIX
} else {
0
};
if (flags & FEAT_AUTOALL) != 0 {
cflags |= CONDF_AUTOALL; }
let _ = cflags; let prior = self
.autoload_conditions
.insert(name.to_string(), module.to_string());
if prior.is_some() {
if (flags & FEAT_IGNORE) == 0 {
return 1; }
}
0 }
pub fn del_autocond(&mut self, name: &str, flags: i32) -> i32 {
if self.autoload_conditions.contains_key(name) {
self.autoload_conditions.remove(name); return 0; }
if (flags & FEAT_IGNORE as i32) == 0 {
return 2; }
0 }
pub fn setparamdefs(&mut self, module: &str, names: &[&str], e: Option<&[i32]>) -> i32 {
let mut ret: i32 = 0; for (n, name) in names.iter().enumerate() {
let enable = e
.map(|arr| arr.get(n).copied().unwrap_or(0)) .unwrap_or(1);
let already = self.autoload_params.contains_key(*name); if enable != 0 {
if already {
continue;
}
self.autoload_params
.insert(name.to_string(), module.to_string());
} else {
if !already {
continue;
}
self.autoload_params.remove(*name);
}
let _ = ret;
}
ret }
pub fn add_autoparam(&mut self, name: &str, module: &str, flags: i32) -> i32 {
let _ret: i32;
crate::ported::signals::queue_signals(); let exists = self.autoload_params.contains_key(name); if exists {
unqueue_signals(); if (flags & FEAT_IGNORE) != 0 {
return 0; }
return -1; }
self.autoload_params
.insert(name.to_string(), module.to_string()); let _ = PM_AUTOLOAD; if (flags & FEAT_AUTOALL) != 0 {
let _ = PM_AUTOALL; }
unqueue_signals(); 0 }
pub fn del_autoparam(&mut self, name: &str, flags: i32) -> i32 {
let pm_flags = paramtab()
.read()
.ok()
.and_then(|t| t.get(name).map(|p| p.node.flags));
match pm_flags {
None => {
if !self.autoload_params.contains_key(name) {
if (flags & FEAT_IGNORE as i32) == 0 {
return 2; }
return 0;
}
self.autoload_params.remove(name);
0 }
Some(f) if (f as u32 & PM_AUTOLOAD) == 0 => {
if (flags & FEAT_IGNORE as i32) == 0 {
return 3; }
0 }
Some(_) => {
if let Ok(mut t) = paramtab().write() {
t.remove(name); }
self.autoload_params.remove(name);
0 }
}
}
pub fn enable_feature(&mut self, module: &str, _name: &str) -> bool {
self.modules.contains_key(module)
}
pub fn disable_feature(&mut self, module: &str, _name: &str) -> bool {
self.modules.contains_key(module)
}
pub fn list_features(&self, _module: &str) -> Vec<String> {
Vec::new()
}
pub fn module_linked(&self, name: &str) -> bool {
self.modules.contains_key(name)
}
pub fn resolve_autoload_builtin(&self, name: &str) -> Option<&str> {
self.autoload_builtins.get(name).map(|s| s.as_str())
}
pub fn resolve_autoload_param(&self, name: &str) -> Option<&str> {
self.autoload_params.get(name).map(|s| s.as_str())
}
pub fn ensurefeature(&mut self, module: &str, feature: &str) -> bool {
if !self.is_loaded(module) {
self.load_module(module);
}
self.is_loaded(module)
}
}
pub trait ModuleLifecycle {
fn setup(&mut self) -> i32 {
0
}
fn boot(&mut self) -> i32 {
0
}
fn cleanup(&mut self) -> i32 {
0
}
fn finish(&mut self) -> i32 {
0
}
}
pub fn getmathfunc(table: &mut modulestab, name: &str, autol: i32) -> Option<String> {
if let Some(module) = table.autoload_mathfuncs.get(name).cloned() {
if autol != 0 {
let _ = ensurefeature(table, &module, "f:", Some(name));
return table.autoload_mathfuncs.get(name).cloned();
}
return Some(module); }
None }
pub fn add_automathfunc(table: &mut modulestab, module: &str, fnam: &str, flags: i32) -> i32 {
if table.autoload_mathfuncs.contains_key(fnam) {
if (flags & FEAT_IGNORE) == 0 {
return 1; }
} else {
table
.autoload_mathfuncs
.insert(fnam.to_string(), module.to_string());
}
0 }
pub fn del_automathfunc(table: &mut modulestab, _modnam: &str, fnam: &str, flags: i32) -> i32 {
if !table.autoload_mathfuncs.contains_key(fnam) {
if (flags & FEAT_IGNORE) == 0 {
return 2; }
} else {
table.autoload_mathfuncs.remove(fnam);
}
0 }
pub fn load_and_bind(_fn_path: &str) -> usize {
0 }
#[allow(unused_variables)]
pub fn hpux_dlsym(handle: usize, name: &str) -> usize {
0 }
pub fn try_load_module(table: &modulestab, name: &str) -> i32 {
if table.modules.contains_key(name) {
1
} else {
0
}
}
pub fn do_load_module(table: &mut modulestab, name: &str, silent: i32) -> i32 {
let ret = try_load_module(table, name);
if ret == 0 && silent == 0 {
zwarn(&format!("failed to load module: {}", name));
}
ret }
pub fn find_module(table: &mut modulestab, name: &str, flags: i32) -> Option<String> {
let mut cur_name = name.to_string();
let mut depth = 0;
loop {
if depth > 64 {
return None;
} depth += 1;
match table.modules.get(&cur_name) {
Some(m) => {
if (flags & FINDMOD_ALIASP) != 0
&& (m.node.flags & MOD_ALIAS) != 0
{
if let Some(target) = m.alias.clone() {
cur_name = target;
continue;
}
return None;
}
return Some(cur_name);
}
None => {
if (flags & FINDMOD_CREATE) == 0 {
return None;
}
table
.modules
.insert(cur_name.clone(), module::new(&cur_name));
return Some(cur_name);
}
}
}
}
pub fn delete_module(table: &mut modulestab, name: &str) -> i32 {
table.modules.remove(name); 0
}
pub fn module_loaded(table: &modulestab, name: &str) -> i32 {
if table.modules.contains_key(name) {
1 } else {
0
}
}
#[allow(unused_variables)]
pub fn dyn_setup_module(m: *const module) -> i32 {
0 }
#[allow(unused_variables)]
pub fn dyn_features_module(
m: *const module,
features: &mut Vec<String>,
) -> i32 {
0 }
#[allow(unused_variables)]
pub fn dyn_enables_module(
m: *const module,
enables: &mut Option<Vec<i32>>,
) -> i32 {
0 }
#[allow(unused_variables)]
pub fn dyn_boot_module(m: *const module) -> i32 {
0 }
#[allow(unused_variables)]
pub fn dyn_cleanup_module(m: *const module) -> i32 {
0 }
#[allow(unused_variables)]
pub fn dyn_finish_module(m: *const module) -> i32 {
0 }
#[allow(unused_variables)]
pub fn module_func(m: &module, name: &str) -> usize {
0 }
pub fn setup_module(_table: &mut modulestab, _name: &str) -> i32 {
0 }
pub fn features_module(_table: &mut modulestab, _name: &str, _features: &mut Vec<String>) -> i32 {
0 }
pub fn enables_module(
_table: &mut modulestab,
_name: &str,
_enables: &mut Option<Vec<i32>>,
) -> i32 {
0 }
pub fn boot_module(_table: &mut modulestab, _name: &str) -> i32 {
0 }
pub fn cleanup_module(_table: &mut modulestab, _name: &str) -> i32 {
0 }
pub fn finish_module(_table: &mut modulestab, _name: &str) -> i32 {
0 }
pub fn do_module_features(m: &mut modulestab, enablesarr: &str, flags: i32) -> i32 {
let mut features: Vec<String> = Vec::new(); let mut ret: i32 = 0;
if features_module(m, enablesarr, &mut features) == 0 {
let mut enables: Option<Vec<i32>> = None;
if enables_module(m, enablesarr, &mut enables) != 0 {
if (flags & FEAT_IGNORE) == 0 {
zwarn(&format!(
"error getting enabled features for module `{}'", enablesarr,
));
}
return 1; }
if (flags & FEAT_CHECKAUTO) != 0 {
let autoloads: Vec<String> = match m.modules.get(enablesarr) {
Some(m) => m
.autoloads
.as_ref()
.map(|al| al.iter().cloned().collect())
.unwrap_or_default(),
None => return ret,
};
for al in &autoloads {
let found = features.iter().any(|f| f == al);
if !found {
if (flags & FEAT_IGNORE) == 0 {
zwarn(&format!(
"module `{}' has no such feature: `{}': autoload cancelled", enablesarr, al,
));
}
let arg = vec![al.clone()];
autofeatures(m, "", Some(enablesarr), &arg, 0, FEAT_IGNORE | FEAT_REMOVE);
}
}
}
}
ret }
pub fn do_boot_module(m: &mut modulestab, enablesarr: &str, silent: i32) -> i32 {
let flags = if silent != 0 {
FEAT_IGNORE | FEAT_CHECKAUTO
} else {
FEAT_CHECKAUTO };
let ret = do_module_features(m, enablesarr, flags); if ret == 1 {
return 1; }
if boot_module(m, enablesarr) != 0 {
return 1; }
ret }
pub fn do_cleanup_module(table: &mut modulestab, name: &str) -> i32 {
if table.modules.contains_key(name) {
cleanup_module(table, name) } else {
0
}
}
pub fn modname_ok(p: &str) -> i32 {
let bytes = p.as_bytes();
let mut i: usize = 0;
loop {
while i < bytes.len() {
let b = bytes[i];
if b.is_ascii_alphanumeric() || b == b'_' {
i += 1;
} else {
break;
}
}
if i >= bytes.len() {
return 1; }
if bytes[i] != b'/' {
break;
} i += 1;
}
0 }
pub fn require_module(table: &mut modulestab, modname: &str, _features: Option<&[String]>) -> i32 {
if try_load_module(table, modname) == 0 {
return 1;
}
let needs_load = table
.modules
.get(modname)
.map(|m| {
let flags = m.node.flags;
(flags & crate::ported::zsh_h::MOD_INIT_B) == 0
|| (flags & crate::ported::zsh_h::MOD_UNLOAD) != 0
})
.unwrap_or(true);
if needs_load {
if !table.load_module(modname) {
return 1;
}
}
0
}
pub fn add_dep(table: &mut modulestab, name: &str, from: &str) -> i32 {
let canon = match find_module(table, name, FINDMOD_ALIASP | FINDMOD_CREATE) {
Some(n) => n,
None => return 0,
};
if let Some(m) = table.modules.get_mut(&canon) {
let deps = m
.deps
.get_or_insert_with(crate::ported::linklist::LinkList::new);
if !deps.iter().any(|d| d == from) {
deps.push_back(from.to_string()); }
}
0
}
pub fn autoloadscan(name: &str, optstr: &str, flags: u32, printflags: i32) {
if (flags & BINF_ADDED) != 0 {
return; }
if (printflags & PRINT_LIST) != 0 {
print!("zmodload -ab ");
if optstr.starts_with('-') {
print!("-- "); }
print!("{}", optstr); if name != optstr {
print!(" "); print!("{}", name); }
} else {
print!("{}", name); if name != optstr {
print!(" ("); print!("{}", optstr); print!(")"); }
}
println!(); }
pub fn bin_zmodload(
nam: &str,
args: &[String], ops: &options,
_func: i32,
) -> i32 {
let mut table = MODULESTAB.lock().unwrap();
let table = &mut *table;
let ops_bcpf = OPT_ISSET(ops, b'b') || OPT_ISSET(ops, b'c') || OPT_ISSET(ops, b'p') || OPT_ISSET(ops, b'f');
let ops_au = OPT_ISSET(ops, b'a') || OPT_ISSET(ops, b'u'); let mut ret: i32;
if ops_bcpf && !ops_au {
zwarnnam(nam, "-b, -c, -f, and -p must be combined with -a or -u"); return 1; }
if OPT_ISSET(ops, b'F') && (ops_bcpf || OPT_ISSET(ops, b'u')) {
zwarnnam(nam, "-b, -c, -f, -p and -u cannot be combined with -F"); return 1; }
if OPT_ISSET(ops, b'A') || OPT_ISSET(ops, b'R') {
if ops_bcpf || ops_au || OPT_ISSET(ops, b'd') || (OPT_ISSET(ops, b'R') && OPT_ISSET(ops, b'e'))
{
zwarnnam(nam, "illegal flags combined with -A or -R"); return 1; }
if !OPT_ISSET(ops, b'e') {
return bin_zmodload_alias(table, nam, args, ops); }
}
if OPT_ISSET(ops, b'd') && OPT_ISSET(ops, b'a') {
zwarnnam(nam, "-d cannot be combined with -a"); return 1; }
if OPT_ISSET(ops, b'u') && args.is_empty() {
zwarnnam(nam, "what do you want to unload?"); return 1; }
if OPT_ISSET(ops, b'e')
&& (OPT_ISSET(ops, b'I') || OPT_ISSET(ops, b'L') || (OPT_ISSET(ops, b'a') && !OPT_ISSET(ops, b'F'))
|| OPT_ISSET(ops, b'd') || OPT_ISSET(ops, b'i')
|| OPT_ISSET(ops, b'u'))
{
zwarnnam(nam, "-e cannot be combined with other options"); return 1; }
for fp in [b'l', b'P'] {
if OPT_ISSET(ops, fp) && !OPT_ISSET(ops, b'F') {
zwarnnam(nam, &format!("-{} is only allowed with -F", fp as char)); return 1; }
}
crate::ported::mem::queue_signals(); if OPT_ISSET(ops, b'F') {
ret = bin_zmodload_features(table, nam, args, ops); } else if OPT_ISSET(ops, b'e') {
ret = bin_zmodload_exist(table, nam, args, ops); } else if OPT_ISSET(ops, b'd') {
ret = bin_zmodload_dep(table, nam, args, ops); } else {
let autoopts = (OPT_ISSET(ops, b'b') as i32) + (OPT_ISSET(ops, b'c') as i32)
+ (OPT_ISSET(ops, b'p') as i32)
+ (OPT_ISSET(ops, b'f') as i32);
if autoopts != 0 || OPT_ISSET(ops, b'a') {
if autoopts > 1 {
zwarnnam(nam, "use only one of -b, -c, or -p"); ret = 1; } else {
ret = bin_zmodload_auto(table, nam, args, ops); }
} else {
ret = bin_zmodload_load(table, nam, args, ops); }
}
unqueue_signals(); ret }
pub fn bin_zmodload_alias(
table: &mut modulestab,
nam: &str,
args: &[String],
ops: &options,
) -> i32 {
if args.is_empty() {
if OPT_ISSET(ops, b'R') {
zwarnnam(nam, "no module alias to remove"); return 1; }
for (name, m) in &table.modules {
if (m.node.flags & MOD_ALIAS) != 0 {
if OPT_ISSET(ops, b'L') {
println!("zmodload -A {}={}", name, m.alias.as_deref().unwrap_or(""));
} else {
println!("{} -> {}", name, m.alias.as_deref().unwrap_or(""));
}
}
}
return 0; }
for arg in args {
let (lhs, aliasname): (&str, Option<&str>) = match arg.find('=') {
Some(eq) => (&arg[..eq], Some(&arg[eq + 1..])),
None => (arg.as_str(), None),
};
if modname_ok(lhs) == 0 {
zwarnnam(nam, &format!("invalid module name `{}'", lhs)); return 1; }
if OPT_ISSET(ops, b'R') {
if aliasname.is_some() {
zwarnnam(
nam,
&format!("bad syntax for removing module alias: {}", lhs),
); return 1; }
match table.modules.get(lhs) {
Some(m) => {
if (m.node.flags & MOD_ALIAS) == 0 {
zwarnnam(
nam,
&format!("module is not an alias: {}", lhs),
); return 1; }
table.modules.remove(lhs); }
None => {
zwarnnam(nam, &format!("no such module alias: {}", lhs)); return 1; }
}
} else {
if let Some(target) = aliasname {
if modname_ok(target) == 0 {
zwarnnam(
nam,
&format!("invalid module name `{}'", target),
); return 1; }
let mut mname = target;
let mut depth = 0;
loop {
if depth > 256 {
break;
}
depth += 1;
if mname == lhs {
zwarnnam(
nam,
&format!("module alias would refer to itself: {}", lhs),
); return 1; }
match table.modules.get(mname) {
Some(m) if (m.node.flags & MOD_ALIAS) != 0 => {
mname = m.alias.as_deref().unwrap_or("");
}
_ => break,
}
}
if let Some(m) = table.modules.get_mut(lhs) {
if (m.node.flags & MOD_ALIAS) == 0 {
zwarnnam(
nam,
&format!("module is not an alias: {}", lhs),
); return 1; }
m.alias = Some(target.to_string()); } else {
let mut m = module::new(lhs); m.node.flags = MOD_ALIAS; m.alias = Some(target.to_string()); table.modules.insert(lhs.to_string(), m); }
} else {
match table.modules.get(lhs) {
Some(m) if (m.node.flags & MOD_ALIAS) != 0 => {
if OPT_ISSET(ops, b'L') {
println!("zmodload -A {}={}", lhs, m.alias.as_deref().unwrap_or(""));
} else {
println!("{} -> {}", lhs, m.alias.as_deref().unwrap_or(""));
}
}
Some(_) => {
zwarnnam(
nam,
&format!("module is not an alias: {}", lhs),
); return 1; }
None => {
zwarnnam(
nam,
&format!("no such module alias: {}", lhs),
); return 1; }
}
}
}
}
0 }
pub fn bin_zmodload_exist(
table: &mut modulestab,
_nam: &str,
args: &[String],
_ops: &options,
) -> i32 {
if args.is_empty() {
for (name, _) in &table.modules {
println!("{}", name);
}
return 0; }
let mut ret: i32 = 0;
for arg in args {
if ret != 0 {
break;
}
let canon = match find_module(table, arg, FINDMOD_ALIASP) {
Some(n) => n,
None => {
ret = 1; continue;
}
};
let live = match table.modules.get(&canon) {
Some(m) => {
let bound = m.handle.is_some() || m.linked.is_some();
let unloading = (m.node.flags & MOD_UNLOAD) != 0;
bound && !unloading
}
None => false,
};
if !live {
ret = 1; }
}
ret }
pub fn bin_zmodload_dep(
table: &mut modulestab,
_nam: &str,
args: &[String],
ops: &options,
) -> i32 {
if OPT_ISSET(ops, b'u') {
if args.is_empty() {
return 0;
}
let tnam = &args[0];
let rest = &args[1..];
let canon = match find_module(table, tnam, FINDMOD_ALIASP) {
Some(n) => n,
None => return 0, };
if let Some(m) = table.modules.get_mut(&canon) {
if let Some(deps) = m.deps.as_mut() {
if !rest.is_empty() {
for to_remove in rest {
if let Some(pos) = deps.iter().position(|d| d == to_remove) {
deps.delete_node(pos); }
}
} else {
deps.clear();
}
}
let no_deps_no_handle = m.deps.as_ref().map_or(true, |d| d.is_empty());
if no_deps_no_handle {
table.modules.remove(&canon);
}
}
return 0; }
if args.len() < 2 {
for (name, m) in &table.modules {
if let Some(deps) = m.deps.as_ref() {
if !deps.is_empty() {
let joined: Vec<&str> = deps.iter().map(|s| s.as_str()).collect();
println!("zmodload -d {} {}", name, joined.join(" "));
}
}
}
return 0;
}
let target = &args[0];
for dep in &args[1..] {
add_dep(table, target, dep); }
0
}
pub fn printautoparams(name: &str, module: &str, flags: u32, lon: i32) {
if (flags & PM_AUTOLOAD) != 0 {
if lon != 0 {
println!("zmodload -ap {} {}", module, name);
} else {
println!("{} ({})", name, module);
}
}
}
pub fn bin_zmodload_auto(
table: &mut modulestab,
_nam: &str,
args: &[String],
ops: &options,
) -> i32 {
let fchar: char; let _flags: i32 = if OPT_ISSET(ops, b'i') {
FEAT_IGNORE
} else {
0
};
if OPT_ISSET(ops, b'c') {
fchar = if OPT_ISSET(ops, b'I') {
'C'
} else {
'c'
};
let _ = fchar;
if args.is_empty() {
for (name, module) in &table.autoload_conditions {
println!("{} {}", module, name);
}
return 0;
}
} else if OPT_ISSET(ops, b'p') {
if args.is_empty() {
for (name, module) in &table.autoload_params {
println!("{} {}", module, name);
}
return 0;
}
} else if OPT_ISSET(ops, b'f') {
if args.is_empty() {
for (name, module) in &table.autoload_mathfuncs {
println!("{} {}", module, name);
}
return 0;
}
} else {
if args.is_empty() {
for (name, module) in &table.autoload_builtins {
autoloadscan(
name,
module,
0,
if OPT_ISSET(ops, b'L') {
PRINT_LIST
} else {
0
},
);
}
return 0;
}
}
if args.len() < 2 {
return 1;
}
let modnam = &args[0]; for nm in &args[1..] {
if OPT_ISSET(ops, b'p') {
table.autoload_params.insert(nm.clone(), modnam.clone());
} else if OPT_ISSET(ops, b'f') {
table.autoload_mathfuncs.insert(nm.clone(), modnam.clone());
} else if OPT_ISSET(ops, b'c') {
table.autoload_conditions.insert(nm.clone(), modnam.clone());
} else {
table.autoload_builtins.insert(nm.clone(), modnam.clone());
}
}
0 }
pub fn unload_named_module(table: &mut modulestab, name: &str, _nam: &str, _silent: i32) -> i32 {
if table.modules.remove(name).is_some() {
0
} else {
1
}
}
pub fn bin_zmodload_load(
table: &mut modulestab,
nam: &str,
args: &[String],
ops: &options,
) -> i32 {
let mut ret: i32 = 0;
if OPT_ISSET(ops, b'u') {
for arg in args {
if unload_named_module(
table,
arg,
nam,
OPT_ISSET(ops, b'i') as i32,
) != 0
{
ret = 1;
}
}
return ret; } else if args.is_empty() {
for (name, _m) in &table.modules {
println!("{}", name);
}
return 0; } else {
for arg in args {
let tmpret = require_module(table, arg, None); if tmpret != 0 && ret != 1 {
ret = tmpret;
}
}
ret
}
}
pub fn bin_zmodload_features(
table: &mut modulestab,
nam: &str,
args: &[String],
ops: &options,
) -> i32 {
let modname = args.first(); let rest_args = if args.is_empty() {
&args[..]
} else {
&args[1..]
};
if modname.is_none() {
if OPT_ISSET(ops, b'L') {
if OPT_ISSET(ops, b'P') {
zwarnnam(nam, "-P is only allowed with a module name"); return 1; }
for (name, _m) in &table.modules {
println!("zmodload -F {}", name);
}
return 0; }
zwarnnam(nam, "-F requires a module name"); return 1; }
let modname = modname.unwrap();
let mut feats: Vec<String> = Vec::with_capacity(rest_args.len());
for arg in rest_args {
feats.push(arg.clone());
}
if !feats.is_empty() {
autofeatures(table, nam, Some(modname), &feats, 0, 0);
}
do_module_features(table, modname, FEAT_CHECKAUTO); 0
}
pub fn ensurefeature(
table: &mut modulestab,
modname: &str,
prefix: &str,
feature: Option<&str>,
) -> i32 {
match feature {
None => require_module(table, modname, None), Some(f) => {
let combined = crate::ported::string::dyncat(prefix, f); let arr = vec![combined];
require_module(table, modname, Some(&arr)) }
}
}
pub fn autofeatures(
table: &mut modulestab,
_cmdnam: &str,
module: Option<&str>,
features: &[String],
prefchar: u8,
defflags: i32,
) -> i32 {
let mut ret: i32 = 0;
let _ = defflags;
for feature in features {
let mut s = feature.as_str();
let mut add: bool = true; if let Some(rest) = s.strip_prefix('-') {
add = false;
s = rest;
} else if let Some(rest) = s.strip_prefix('+') {
add = true;
s = rest;
}
let (fchar, fnam): (u8, &str) = if prefchar != 0 {
(prefchar, s) } else {
let bytes = s.as_bytes();
if bytes.len() >= 2 && bytes[1] == b':' {
(bytes[0], &s[2..])
} else {
(b'b', s) }
};
let modname = match module {
Some(m) => m,
None => {
ret = 1;
continue;
}
};
if add {
match fchar {
b'b' => {
table
.autoload_builtins
.insert(fnam.to_string(), modname.to_string());
}
b'c' | b'C' => {
table
.autoload_conditions
.insert(fnam.to_string(), modname.to_string());
}
b'p' => {
table
.autoload_params
.insert(fnam.to_string(), modname.to_string());
}
b'f' => {
table
.autoload_mathfuncs
.insert(fnam.to_string(), modname.to_string());
}
_ => {
ret = 1;
}
}
} else {
match fchar {
b'b' => {
table.autoload_builtins.remove(fnam);
}
b'c' | b'C' => {
table.autoload_conditions.remove(fnam);
}
b'p' => {
table.autoload_params.remove(fnam);
}
b'f' => {
table.autoload_mathfuncs.remove(fnam);
}
_ => {
ret = 1;
}
}
}
}
ret
}
pub static MATHFUNCS: Lazy<Mutex<Vec<mathfunc>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub fn setconddefs(
nam: &str, c: &mut [conddef],
e: Option<&[i32]>,
) -> i32 {
let mut ret = 0; for (i, entry) in c.iter_mut().enumerate() {
let want_add = e.map(|es| es[i] != 0).unwrap_or(true); if want_add {
if (entry.flags & CONDF_ADDED) != 0 {
continue;
} let dup = conddef {
next: None,
name: entry.name.clone(),
flags: entry.flags,
handler: entry.handler,
min: entry.min,
max: entry.max,
condid: entry.condid,
module: entry.module.clone(),
};
if addconddef(dup) != 0 {
zwarnnam(
nam, &format!("name clash when adding condition `{}'", entry.name),
);
ret = 1;
} else {
entry.flags |= CONDF_ADDED; }
} else {
if (entry.flags & CONDF_ADDED) == 0 {
continue;
} if deleteconddef(entry) != 0 {
zwarnnam(
nam, &format!("condition `{}' already deleted", entry.name),
);
ret = 1;
} else {
entry.flags &= !CONDF_ADDED; }
}
}
ret }
pub fn setmathfuncs(
nam: &str, f: &mut [mathfunc],
e: Option<&[i32]>,
) -> i32 {
let mut ret = 0; for (i, entry) in f.iter_mut().enumerate() {
let want_add = e.map(|es| es[i] != 0).unwrap_or(true); if want_add {
if (entry.flags & MFF_ADDED) != 0 {
continue;
} let dup = mathfunc {
next: None,
name: entry.name.clone(),
flags: entry.flags,
nfunc: entry.nfunc,
sfunc: entry.sfunc,
module: entry.module.clone(),
minargs: entry.minargs,
maxargs: entry.maxargs,
funcid: entry.funcid,
};
if addmathfunc(dup) != 0 {
zwarnnam(
nam, &format!("name clash when adding math function `{}'", entry.name),
);
ret = 1;
} else {
entry.flags |= MFF_ADDED; }
} else {
if (entry.flags & MFF_ADDED) == 0 {
continue;
} if deletemathfunc(entry) != 0 {
zwarnnam(
nam, &format!("math function `{}' already deleted", entry.name),
);
ret = 1;
}
}
}
ret }
pub static CONDTAB: Lazy<Mutex<Vec<conddef>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub fn deleteconddef(c: &conddef) -> i32 {
let mut tab = CONDTAB.lock().unwrap();
let infix = c.flags & CONDF_INFIX;
match tab
.iter()
.position(|p| p.name == c.name && (p.flags & CONDF_INFIX) == infix)
{
Some(i) => {
tab.remove(i);
0
} None => -1, }
}
pub fn addconddef(c: conddef) -> i32 {
let infix = c.flags & CONDF_INFIX;
let clash_idx = {
let tab = CONDTAB.lock().unwrap();
tab.iter()
.position(|p| p.name == c.name && (p.flags & CONDF_INFIX) == infix) };
if let Some(i) = clash_idx {
let (autoload, added) = {
let tab = CONDTAB.lock().unwrap();
(tab[i].module.is_some(), (tab[i].flags & CONDF_ADDED) != 0)
};
if !autoload || added {
return 1;
} CONDTAB.lock().unwrap().remove(i); }
CONDTAB.lock().unwrap().insert(0, c); 0
}
pub static WRAPPERS: Lazy<Mutex<Vec<funcwrap>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub fn addmathfunc(f: mathfunc) -> i32 {
if (f.flags & MFF_ADDED) != 0 {
return 1;
} let mut tab = MATHFUNCS.lock().unwrap();
let mut found_idx: Option<usize> = None;
for (i, p) in tab.iter().enumerate() {
if p.name == f.name {
if p.module.is_some() && (p.flags & MFF_USERFUNC) == 0 {
found_idx = Some(i); break;
}
return 1; }
}
if let Some(i) = found_idx {
tab.remove(i);
} tab.insert(0, f); 0
}
pub fn removemathfunc(name: &str) {
let mut tab = MATHFUNCS.lock().unwrap();
if let Some(i) = tab.iter().position(|m| m.name == name) {
tab.remove(i); }
}
pub fn deletemathfunc(f: &mathfunc) -> i32 {
let mut tab = MATHFUNCS.lock().unwrap();
match tab.iter().position(|m| m.name == f.name) {
Some(i) => {
if tab[i].module.is_some() {
tab.remove(i);
}
else {
tab[i].flags &= !MFF_ADDED;
} 0
}
None => -1, }
}
pub fn addwrapper(_m: &str, w: funcwrap) -> i32 {
let mut tab = WRAPPERS.lock().unwrap();
if tab.iter().any(|x| match (x.handler, w.handler) {
(Some(a), Some(b)) => std::ptr::fn_addr_eq(a, b),
(None, None) => true,
_ => false,
}) {
return 1; }
let mut entry = w; entry.flags |= 1; tab.push(entry); 0
}
pub fn deletewrapper(_m: &str, w: &funcwrap) -> i32 {
let mut tab = WRAPPERS.lock().unwrap();
match tab.iter().position(|x| match (x.handler, w.handler) {
(Some(a), Some(b)) => std::ptr::fn_addr_eq(a, b),
(None, None) => true,
_ => false,
}) {
Some(i) => {
tab.remove(i);
0
} None => 1, }
}
pub fn featuresarray(
_m: *const module,
bn: &[builtin], cd: &[conddef], mf: &[mathfunc], pd: &[paramdef], n_abstract: i32, ) -> Vec<String> {
let features_size = bn.len() + cd.len() + mf.len() + pd.len() + n_abstract.max(0) as usize;
let mut features: Vec<String> = Vec::with_capacity(features_size + 1); for b in bn {
features.push(format!("b:{}", b.node.nam)); }
for c in cd {
let prefix = if (c.flags & CONDF_INFIX) != 0 {
"C:"
} else {
"c:"
}; features.push(format!("{}{}", prefix, c.name)); }
for m in mf {
features.push(format!("f:{}", m.name)); }
for p in pd {
features.push(format!("p:{}", p.name)); }
features
}
pub fn getfeatureenables(
_m: *const module,
bn: &[builtin], cd: &[conddef], mf: &[mathfunc], pd: &[paramdef], n_abstract: i32, ) -> Vec<i32> {
let features_size = bn.len() + cd.len() + mf.len() + pd.len() + n_abstract.max(0) as usize;
let mut enables: Vec<i32> = Vec::with_capacity(features_size); for b in bn {
enables.push(if (b.node.flags & BINF_ADDED as i32) != 0 {
1
} else {
0
});
}
for c in cd {
enables.push(if (c.flags & CONDF_ADDED) != 0 { 1 } else { 0 });
}
for m in mf {
enables.push(if (m.flags & MFF_ADDED) != 0 { 1 } else { 0 });
}
for p in pd {
enables.push(if p.pm.is_some() { 1 } else { 0 });
}
for _ in 0..n_abstract.max(0) {
enables.push(0);
}
enables }
pub static hooktab: std::sync::atomic::AtomicPtr<hookdef> = std::sync::atomic::AtomicPtr::new(std::ptr::null_mut());
pub static MODULESTAB: Lazy<Mutex<modulestab>> = Lazy::new(|| Mutex::new(modulestab::new()));
pub const FEATURE_TYPE_BUILTIN: i32 = 0;
pub const FEATURE_TYPE_CONDITION: i32 = 1;
pub const FEATURE_TYPE_PARAMETER: i32 = 2;
pub const FEATURE_TYPE_MATHFUNC: i32 = 3;
pub const FEATURE_TYPE_HOOK: i32 = 4;
#[derive(Debug, Default)]
pub struct modulestab {
pub modules: HashMap<String, module>,
pub autoload_builtins: HashMap<String, String>,
pub autoload_conditions: HashMap<String, String>,
pub autoload_params: HashMap<String, String>,
pub autoload_mathfuncs: HashMap<String, String>,
pub added_builtins: HashMap<String, u32>,
}
pub const FEAT_IGNORE: i32 = 0x0001;
pub const FEAT_INFIX: i32 = 0x0002;
pub const FEAT_AUTOALL: i32 = 0x0004;
pub const FEAT_REMOVE: i32 = 0x0008;
pub const FEAT_CHECKAUTO: i32 = 0x0010;
pub const FINDMOD_ALIASP: i32 = 0x0001;
pub const FINDMOD_CREATE: i32 = 0x0002;
#[cfg(test)]
mod tests {
use crate::ported::zsh_h::hashnode;
use super::*;
#[test]
fn test_module_table_new() {
let _g = crate::test_util::global_state_lock();
let table = modulestab::new();
assert!(table.is_loaded("zsh/complete"));
assert!(table.is_loaded("zsh/datetime"));
assert!(table.is_loaded("zsh/system"));
assert!(!table.is_loaded("nonexistent"));
}
#[test]
fn test_load_unload() {
let _g = crate::test_util::global_state_lock();
let mut table = modulestab::new();
assert!(table.is_loaded("zsh/complete"));
table.unload_module("zsh/complete");
assert!(!table.is_loaded("zsh/complete"));
table.load_module("zsh/complete");
assert!(table.is_loaded("zsh/complete"));
}
#[test]
fn test_list_loaded() {
let _g = crate::test_util::global_state_lock();
let table = modulestab::new();
let loaded = table.list_loaded();
assert!(loaded.len() > 20);
assert!(loaded.contains(&"zsh/complete"));
}
#[test]
fn test_autoload() {
let _g = crate::test_util::global_state_lock();
let mut table = modulestab::new();
table.add_autobin("my_cmd", "zsh/mymodule", 0);
assert_eq!(
table.resolve_autoload_builtin("my_cmd"),
Some("zsh/mymodule")
);
assert_eq!(table.resolve_autoload_builtin("nonexistent"), None);
}
#[test]
fn test_features() {
let _g = crate::test_util::global_state_lock();
let table = modulestab::new();
let features = table.list_features("zsh/complete");
assert!(features.is_empty());
assert!(table.module_linked("zsh/complete"));
}
#[test]
fn test_module_linked() {
let _g = crate::test_util::global_state_lock();
let table = modulestab::new();
assert!(table.module_linked("zsh/complete"));
assert!(table.module_linked("zsh/stat"));
assert!(!table.module_linked("zsh/nonexistent"));
}
#[test]
fn test_printmodulenode() {
let _g = crate::test_util::global_state_lock();
let module = module::new("zsh/test");
let output = printmodulenode("zsh/test", &module, 0);
assert_eq!(output, "zsh/test");
let listed = printmodulenode("zsh/test", &module, PRINTMOD_LIST);
assert_eq!(listed, "zmodload zsh/test");
}
fn mk_mf(name: &str, autoload: bool) -> mathfunc {
mathfunc {
next: None,
name: name.to_string(),
flags: 0,
nfunc: None,
sfunc: None,
module: if autoload {
Some("zsh/test".to_string())
} else {
None
},
minargs: 0,
maxargs: 0,
funcid: 0,
}
}
#[test]
fn addmathfunc_clash_returns_one_when_already_added() {
let _g = crate::test_util::global_state_lock();
let f1 = mk_mf("zshrs_test_clash_a", false);
let f2 = mk_mf("zshrs_test_clash_a", false);
assert_eq!(addmathfunc(f1), 0);
assert_eq!(addmathfunc(f2), 1, "second add should clash");
removemathfunc("zshrs_test_clash_a");
}
#[test]
fn addmathfunc_autoload_replace_succeeds() {
let _g = crate::test_util::global_state_lock();
let auto = mk_mf("zshrs_test_replace", true);
let real = mk_mf("zshrs_test_replace", false);
assert_eq!(addmathfunc(auto), 0);
assert_eq!(
addmathfunc(real),
0,
"autoloadable entry must be replaceable"
);
let tab = MATHFUNCS.lock().unwrap();
let entry = tab.iter().find(|m| m.name == "zshrs_test_replace").unwrap();
assert!(
entry.module.is_none(),
"after replace, module should be None"
);
drop(tab);
removemathfunc("zshrs_test_replace");
}
#[test]
fn removemathfunc_returns_unit_and_drops() {
let _g = crate::test_util::global_state_lock();
let f = mk_mf("zshrs_test_remove", false);
assert_eq!(addmathfunc(f), 0);
assert!(MATHFUNCS
.lock()
.unwrap()
.iter()
.any(|m| m.name == "zshrs_test_remove"));
removemathfunc("zshrs_test_remove");
assert!(!MATHFUNCS
.lock()
.unwrap()
.iter()
.any(|m| m.name == "zshrs_test_remove"));
}
#[test]
fn deletemathfunc_returns_minus_one_on_miss() {
let _g = crate::test_util::global_state_lock();
let probe = mk_mf("zshrs_test_never_added_xyz", false);
assert_eq!(deletemathfunc(&probe), -1);
}
#[test]
fn deletemathfunc_clears_added_flag_for_userfunc() {
let _g = crate::test_util::global_state_lock();
let mut f = mk_mf("zshrs_test_clear_flag", false);
f.flags = MFF_ADDED;
assert_eq!(
addmathfunc(f),
1,
"MFF_ADDED set → addmathfunc clashes at c:1318"
);
MATHFUNCS
.lock()
.unwrap()
.insert(0, mk_mf("zshrs_test_clear_flag2", false));
let mut f2 = mk_mf("zshrs_test_clear_flag2", false);
f2.flags = MFF_ADDED;
assert_eq!(deletemathfunc(&f2), 0);
let tab = MATHFUNCS.lock().unwrap();
let entry = tab.iter().find(|m| m.name == "zshrs_test_clear_flag2");
if let Some(e) = entry {
assert_eq!(e.flags & MFF_ADDED, 0);
}
drop(tab);
removemathfunc("zshrs_test_clear_flag2");
}
fn mk_cd(name: &str, infix: bool, autoload: bool) -> conddef {
conddef {
next: None,
name: name.to_string(),
flags: if infix {
CONDF_INFIX
} else {
0
},
handler: None,
min: 0,
max: 0,
condid: 0,
module: if autoload {
Some("zsh/cond".to_string())
} else {
None
},
}
}
#[test]
fn addconddef_clash_returns_one() {
let _g = crate::test_util::global_state_lock();
let c1 = mk_cd("zshrs_test_cond_clash", false, false);
let c2 = mk_cd("zshrs_test_cond_clash", false, false);
assert_eq!(addconddef(c1), 0);
assert_eq!(addconddef(c2), 1);
let probe = mk_cd("zshrs_test_cond_clash", false, false);
assert_eq!(deleteconddef(&probe), 0);
}
#[test]
fn deleteconddef_returns_minus_one_on_miss() {
let _g = crate::test_util::global_state_lock();
let probe = mk_cd("zshrs_test_cond_never_added", false, false);
assert_eq!(deleteconddef(&probe), -1);
}
#[test]
fn addconddef_distinguishes_infix_from_prefix() {
let _g = crate::test_util::global_state_lock();
let prefix = mk_cd("zshrs_test_cond_dual", false, false);
let infix = mk_cd("zshrs_test_cond_dual", true, false);
assert_eq!(addconddef(prefix), 0);
assert_eq!(
addconddef(infix),
0,
"infix variant must not clash with prefix variant"
);
let _ = deleteconddef(&mk_cd("zshrs_test_cond_dual", false, false));
let _ = deleteconddef(&mk_cd("zshrs_test_cond_dual", true, false));
}
#[test]
fn setconddefs_bulk_add_then_bulk_delete_via_e_array() {
let _g = crate::test_util::global_state_lock();
let mut entries = vec![
mk_cd("zshrs_test_bulk_a", false, false),
mk_cd("zshrs_test_bulk_b", false, false),
];
let add_selectors = [1, 1];
assert_eq!(setconddefs("test", &mut entries, Some(&add_selectors)), 0);
assert_ne!(entries[0].flags & CONDF_ADDED, 0);
assert_ne!(entries[1].flags & CONDF_ADDED, 0);
let del_selectors = [0, 0];
assert_eq!(setconddefs("test", &mut entries, Some(&del_selectors)), 0);
assert_eq!(entries[0].flags & CONDF_ADDED, 0);
assert_eq!(entries[1].flags & CONDF_ADDED, 0);
}
fn mk_b(nam: &str) -> builtin {
builtin {
node: hashnode {
next: None,
nam: nam.to_string(),
flags: 0,
},
handlerfunc: None,
minargs: 0,
maxargs: 0,
funcid: 0,
optstr: None,
defopts: None,
}
}
#[test]
fn addbuiltin_clash_against_existing_builtintab_entry() {
let _g = crate::test_util::global_state_lock();
let _ = createbuiltintable();
let mut b = mk_b("echo");
let r = addbuiltin(&mut b);
assert!(r == 0 || r == 1);
if r == 0 {
assert_ne!(b.node.flags & BINF_ADDED as i32, 0);
}
}
#[test]
fn addbuiltins_skips_already_added_entries() {
let _g = crate::test_util::global_state_lock();
let mut b1 = mk_b("zshrs_test_already_added_1");
b1.node.flags = BINF_ADDED as i32;
let mut b2 = mk_b("zshrs_test_already_added_2");
b2.node.flags = BINF_ADDED as i32;
let mut binl = vec![b1, b2];
assert_eq!(addbuiltins("test", &mut binl), 0);
}
fn mk_w() -> funcwrap {
funcwrap {
next: None,
flags: 0,
handler: Some(|_prog, _w, _name| 0),
module: None,
}
}
#[test]
fn addwrapper_then_deletewrapper_round_trip() {
let _g = crate::test_util::global_state_lock();
let w = mk_w();
assert_eq!(addwrapper("zsh/test", w), 0);
let probe = mk_w();
let r = deletewrapper("zsh/test", &probe);
assert!(r == 0 || r == 1);
}
#[test]
fn deletewrapper_returns_one_when_not_found() {
let _g = crate::test_util::global_state_lock();
let snapshot: Vec<_> = WRAPPERS.lock().unwrap().drain(..).collect();
let probe = mk_w();
assert_eq!(deletewrapper("zsh/test", &probe), 1);
WRAPPERS.lock().unwrap().extend(snapshot);
}
static H1_CALLS: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
static H2_CALLS: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
static H1_RETVAL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
fn h1(_h: *mut hookdef, _d: *mut std::ffi::c_void) -> i32 {
H1_CALLS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
H1_RETVAL.load(std::sync::atomic::Ordering::SeqCst)
}
fn h2(_h: *mut hookdef, _d: *mut std::ffi::c_void) -> i32 {
H2_CALLS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
0
}
fn leak_hookdef(name: &str, flags: i32) -> *mut hookdef {
Box::into_raw(Box::new(hookdef {
next: std::ptr::null_mut(),
name: name.to_string(),
def: None,
flags,
funcs: std::ptr::null_mut(),
}))
}
#[test]
fn gethookdef_returns_null_then_registered_ptr() {
let _g = crate::test_util::global_state_lock();
let h = leak_hookdef("zshrs_test_get_hook", HOOKF_ALL);
unsafe {
assert!(gethookdef(&(*h).name).is_null());
}
assert_eq!(addhookdef(h), 0);
unsafe {
assert_eq!(gethookdef(&(*h).name), h);
}
assert_eq!(deletehookdef(h), 0);
unsafe {
drop(Box::from_raw(h));
}
}
#[test]
fn addhookdef_rejects_duplicate_name() {
let _g = crate::test_util::global_state_lock();
let h1 = leak_hookdef("zshrs_test_dup_hook", HOOKF_ALL);
let h2 = leak_hookdef("zshrs_test_dup_hook", HOOKF_ALL);
assert_eq!(addhookdef(h1), 0);
assert_eq!(addhookdef(h2), 1, "duplicate name must return 1");
assert_eq!(deletehookdef(h1), 0);
unsafe {
drop(Box::from_raw(h1));
drop(Box::from_raw(h2));
}
}
#[test]
fn deletehookdef_returns_one_on_miss_zero_on_hit() {
let _g = crate::test_util::global_state_lock();
let h = leak_hookdef("zshrs_test_del_hook", HOOKF_ALL);
assert_eq!(deletehookdef(h), 1, "not in chain → 1");
assert_eq!(addhookdef(h), 0);
assert_eq!(deletehookdef(h), 0, "spliced out → 0");
assert_eq!(deletehookdef(h), 1, "second delete misses → 1");
unsafe {
drop(Box::from_raw(h));
}
}
#[test]
fn runhookdef_dispatches_all_under_hookf_all_short_circuits_on_nonzero() {
let _g = crate::test_util::global_state_lock();
H1_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H2_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H1_RETVAL.store(0, std::sync::atomic::Ordering::SeqCst);
let h = leak_hookdef("zshrs_test_runall", HOOKF_ALL);
assert_eq!(addhookdef(h), 0);
assert_eq!(addhookdeffunc(h, h1), 0);
assert_eq!(addhookdeffunc(h, h2), 0);
assert_eq!(runhookdef(h, std::ptr::null_mut()), 0);
assert_eq!(H1_CALLS.load(std::sync::atomic::Ordering::SeqCst), 1);
assert_eq!(H2_CALLS.load(std::sync::atomic::Ordering::SeqCst), 1);
H1_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H2_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H1_RETVAL.store(7, std::sync::atomic::Ordering::SeqCst);
assert_eq!(runhookdef(h, std::ptr::null_mut()), 7);
assert_eq!(H1_CALLS.load(std::sync::atomic::Ordering::SeqCst), 1);
assert_eq!(
H2_CALLS.load(std::sync::atomic::Ordering::SeqCst),
0,
"h2 must not fire after h1 returns nonzero"
);
assert_eq!(deletehookdef(h), 0);
unsafe {
drop(Box::from_raw(h));
}
}
#[test]
fn runhookdef_calls_only_last_when_hookf_all_clear() {
let _g = crate::test_util::global_state_lock();
H1_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H2_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H1_RETVAL.store(0, std::sync::atomic::Ordering::SeqCst);
let h = leak_hookdef("zshrs_test_runlast", 0); assert_eq!(addhookdef(h), 0);
assert_eq!(addhookdeffunc(h, h1), 0);
assert_eq!(addhookdeffunc(h, h2), 0);
runhookdef(h, std::ptr::null_mut());
assert_eq!(
H1_CALLS.load(std::sync::atomic::Ordering::SeqCst),
0,
"h1 must not fire when HOOKF_ALL is clear"
);
assert_eq!(
H2_CALLS.load(std::sync::atomic::Ordering::SeqCst),
1,
"only the last-registered handler fires"
);
assert_eq!(deletehookdef(h), 0);
unsafe {
drop(Box::from_raw(h));
}
}
#[test]
fn deletehookdeffunc_removes_only_target_hookfn() {
let _g = crate::test_util::global_state_lock();
H1_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H2_CALLS.store(0, std::sync::atomic::Ordering::SeqCst);
H1_RETVAL.store(0, std::sync::atomic::Ordering::SeqCst);
let h = leak_hookdef("zshrs_test_del_fn", HOOKF_ALL);
assert_eq!(addhookdef(h), 0);
addhookdeffunc(h, h1);
addhookdeffunc(h, h2);
assert_eq!(deletehookdeffunc(h, h1), 0);
assert_eq!(deletehookdeffunc(h, h1), 1);
runhookdef(h, std::ptr::null_mut());
assert_eq!(H1_CALLS.load(std::sync::atomic::Ordering::SeqCst), 0);
assert_eq!(H2_CALLS.load(std::sync::atomic::Ordering::SeqCst), 1);
assert_eq!(deletehookdef(h), 0);
unsafe {
drop(Box::from_raw(h));
}
}
#[test]
fn addhookfunc_and_deletehookfunc_return_one_when_no_hookdef() {
let _g = crate::test_util::global_state_lock();
assert_eq!(addhookfunc("zshrs_test_no_such_hook", h1), 1);
assert_eq!(deletehookfunc("zshrs_test_no_such_hook", h1), 1);
}
#[test]
fn addhookdefs_registers_contiguous_array() {
let _g = crate::test_util::global_state_lock();
let arr: Box<[hookdef; 2]> = Box::new([
hookdef {
next: std::ptr::null_mut(),
name: "zshrs_test_arr_0".into(),
def: None,
flags: HOOKF_ALL,
funcs: std::ptr::null_mut(),
},
hookdef {
next: std::ptr::null_mut(),
name: "zshrs_test_arr_1".into(),
def: None,
flags: HOOKF_ALL,
funcs: std::ptr::null_mut(),
},
]);
let base: *mut hookdef =
Box::into_raw(arr) as *mut hookdef;
assert_eq!(addhookdefs(std::ptr::null(), base, 2), 0);
assert_eq!(gethookdef("zshrs_test_arr_0"), base);
unsafe {
assert_eq!(gethookdef("zshrs_test_arr_1"), base.add(1));
}
unsafe {
assert_eq!(deletehookdef(base), 0);
assert_eq!(deletehookdef(base.add(1)), 0);
drop(Box::from_raw(
base as *mut [hookdef; 2],
));
}
}
}
#[cfg(test)]
mod modname_tests {
use super::*;
#[test]
fn modname_ok_accepts_canonical_zsh_module_paths() {
let _g = crate::test_util::global_state_lock();
assert_eq!(modname_ok("zsh"), 1);
assert_eq!(modname_ok("zsh/datetime"), 1);
assert_eq!(modname_ok("zsh/zle"), 1);
assert_eq!(modname_ok("foo_bar"), 1);
assert_eq!(modname_ok("foo123"), 1);
}
#[test]
fn modname_ok_rejects_special_chars() {
let _g = crate::test_util::global_state_lock();
assert_eq!(modname_ok("zsh space"), 0);
assert_eq!(modname_ok("zsh-bad"), 0, "hyphen is not IIDENT");
assert_eq!(modname_ok("zsh.foo"), 0, "dot is not IIDENT");
assert_eq!(modname_ok("$foo"), 0);
}
#[test]
fn modname_ok_treats_empty_as_trivially_ok() {
let _g = crate::test_util::global_state_lock();
assert_eq!(modname_ok(""), 1);
}
#[test]
fn del_autobin_unknown_name_returns_2_or_zero_per_feat_ignore() {
let _g = crate::test_util::global_state_lock();
let mut t = modulestab::new();
assert_eq!(t.del_autobin("definitely_not_a_builtin", 0), 2);
assert_eq!(t.del_autobin("definitely_not_a_builtin", FEAT_IGNORE), 0);
}
#[test]
fn del_autobin_registered_builtin_returns_3_or_zero_per_feat_ignore() {
let _g = crate::test_util::global_state_lock();
let mut t = modulestab::new();
assert_eq!(t.del_autobin("echo", 0), 3);
assert_eq!(t.del_autobin("echo", FEAT_IGNORE), 0);
}
#[test]
fn del_autobin_autoload_only_entry_removed() {
let _g = crate::test_util::global_state_lock();
let mut t = modulestab::new();
t.autoload_builtins
.insert("zshrs_test_autobin_x".to_string(), "mymod".to_string());
assert_eq!(t.del_autobin("zshrs_test_autobin_x", 0), 0);
assert!(
!t.autoload_builtins.contains_key("zshrs_test_autobin_x"),
"successful del must remove ledger entry"
);
assert_eq!(t.del_autobin("zshrs_test_autobin_x", 0), 2);
assert_eq!(t.del_autobin("zshrs_test_autobin_x", FEAT_IGNORE), 0);
}
#[test]
fn del_autocond_autoload_entry_removed_or_not_found() {
let _g = crate::test_util::global_state_lock();
let mut t = modulestab::new();
assert_eq!(t.del_autocond("zshrs_test_cond_x", 0), 2);
assert_eq!(t.del_autocond("zshrs_test_cond_x", FEAT_IGNORE), 0);
t.autoload_conditions
.insert("zshrs_test_cond_x".to_string(), "mymod".to_string());
assert_eq!(t.del_autocond("zshrs_test_cond_x", 0), 0);
assert!(!t.autoload_conditions.contains_key("zshrs_test_cond_x"));
}
#[test]
fn del_autoparam_unknown_name_returns_2() {
let _g = crate::test_util::global_state_lock();
let mut t = modulestab::new();
assert_eq!(t.del_autoparam("zshrs_test_param_x_unknown", 0), 2);
assert_eq!(
t.del_autoparam("zshrs_test_param_x_unknown", FEAT_IGNORE),
0
);
}
#[test]
fn module_corpus_register_new_returns_true() {
let _g = crate::test_util::global_state_lock();
let mut t = newmoduletable();
assert!(register_module(&mut t, "myfresh.module"));
}
#[test]
fn module_corpus_register_duplicate_returns_false() {
let _g = crate::test_util::global_state_lock();
let mut t = newmoduletable();
assert!(register_module(&mut t, "dup.module"));
assert!(!register_module(&mut t, "dup.module"),
"second registration of same name = false");
}
#[test]
fn module_corpus_new_table_has_builtin_modules() {
let _g = crate::test_util::global_state_lock();
let t = newmoduletable();
assert!(t.modules.len() >= 1, "fresh modulestab has built-ins, got {}", t.modules.len());
}
#[test]
fn module_corpus_register_empty_name_does_not_panic() {
let _g = crate::test_util::global_state_lock();
let mut t = newmoduletable();
let _ = register_module(&mut t, "");
}
#[test]
fn module_corpus_setup_callback_default_returns_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(setup_(std::ptr::null()), 0);
}
#[test]
fn module_corpus_boot_callback_default_returns_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(boot_(std::ptr::null()), 0);
}
#[test]
fn module_corpus_cleanup_callback_default_returns_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(cleanup_(std::ptr::null()), 0);
}
#[test]
fn module_corpus_finish_callback_default_returns_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(finish_(std::ptr::null()), 0);
}
}