use std::collections::HashMap;
use std::sync::Mutex;
use once_cell::sync::Lazy;
use crate::ported::utils::zwarnnam;
use crate::ported::zsh_h::mathfunc;
use crate::zsh_h::module;
use crate::ported::zsh_h::OPT_ISSET;
pub fn freemodulenode(hn: module) {
}
pub fn printmodulenode(hn: &str, m: &module) -> String {
let state = if (m.node.flags & crate::ported::zsh_h::MOD_ALIAS) != 0 {
"alias"
} else if (m.node.flags & crate::ported::zsh_h::MOD_UNLOAD) != 0 {
"unloaded"
} else if (m.node.flags & crate::ported::zsh_h::MOD_LINKED) != 0 {
"loaded"
} else {
"autoloaded"
};
format!("{} ({})", hn, state)
}
pub fn newmoduletable() -> modulestab {
modulestab::new()
}
#[allow(unused_variables)]
pub fn setup_(m: *const crate::ported::zsh_h::module) -> i32 { 0 }
#[allow(unused_variables)]
pub fn features_(m: *const crate::ported::zsh_h::module, features: &mut Vec<String>) -> i32 { 1 }
#[allow(unused_variables)]
pub fn enables_(m: *const crate::ported::zsh_h::module, enables: &mut Option<Vec<i32>>) -> i32 { 1 }
#[allow(unused_variables)]
pub fn boot_(m: *const crate::ported::zsh_h::module) -> i32 { 0 }
#[allow(unused_variables)]
pub fn cleanup_(m: *const crate::ported::zsh_h::module) -> i32 { 0 }
#[allow(unused_variables)]
pub fn finish_(m: *const crate::ported::zsh_h::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 crate::ported::zsh_h::builtin) -> i32 { use crate::ported::zsh_h::BINF_ADDED;
let tab = crate::ported::builtin::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 [crate::ported::zsh_h::builtin]) -> i32 { use crate::ported::zsh_h::BINF_ADDED;
let mut ret = 0; for b in binl.iter_mut() { if (b.node.flags & BINF_ADDED as i32) != 0 { continue; } if addbuiltin(b) != 0 { crate::ported::utils::zwarnnam(nam, &format!("name clash when adding builtin `{}'", b.node.nam));
ret = 1;
}
}
ret }
pub fn addhookdeffunc(table: &mut modulestab, h: &mut crate::ported::zsh_h::hookdef, fn_name: &str) -> i32 { table.hooks.entry(h.name.clone()).or_default().push(fn_name.to_string());
let _ = h.funcs; 0 }
pub fn addhookfunc(hook: &str, func: &str) { if let Ok(mut tab) = HOOKTAB.lock() {
tab.entry(hook.to_string())
.or_default()
.push(func.to_string());
}
}
pub fn deletehookdeffunc(table: &mut modulestab, h: &mut crate::ported::zsh_h::hookdef, fn_name: &str) -> i32 { if let Some(funcs) = table.hooks.get_mut(&h.name) {
if let Some(pos) = funcs.iter().position(|n| n == fn_name) {
funcs.remove(pos); let _ = h.funcs;
return 0; }
}
let _ = h.funcs;
1 }
pub fn deletehookfunc(hook: &str, func: &str) { if let Ok(mut tab) = HOOKTAB.lock() {
if let Some(v) = tab.get_mut(hook) {
v.retain(|f| f != func);
}
}
}
#[allow(unused_variables)]
pub fn checkaddparam(nam: &str, opt_i: i32) -> i32 { let _ = nam;
let _ = opt_i;
0
}
pub fn addparamdef(d: &mut crate::ported::zsh_h::paramdef) -> i32 { use crate::ported::zsh_h::{
PM_ARRAY, PM_EFLOAT, PM_FFLOAT, PM_HASHED, PM_INTEGER, PM_NAMEREF,
PM_SCALAR, PM_TIED, PM_TYPE,
};
if checkaddparam(&d.name, 0) != 0 { return 1; }
let pm_opt: Option<crate::ported::zsh_h::Param> = if d.getnfn.is_some() { crate::ported::params::createspecialhash(&d.name, d.flags) } else { match crate::ported::params::createparam(&d.name, d.flags) { Some(p) => Some(p),
None => {
let tab = crate::ported::params::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 = crate::ported::hist::casemodify(
&pm.node.nam,
crate::ported::zsh_h::CASMOD_LOWER,
);
pm.ename = Some(crate::ported::mem::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 = crate::ported::hist::casemodify(
&pm.node.nam,
crate::ported::zsh_h::CASMOD_UPPER,
);
pm.ename = Some(crate::ported::mem::ztrdup(&upper)); }
let _ = d.gsu; } else if t == PM_HASHED { let _ = d.gsu; } else { crate::ported::params::unsetparam_pm(&mut pm, 0, 1); return 1; }
}
d.pm = Some(pm); 0 }
pub fn deleteparamdef(d: &mut crate::ported::zsh_h::paramdef) -> i32 { use crate::ported::zsh_h::{PM_READONLY, PM_REMOVABLE};
let mut pm: crate::ported::zsh_h::Param = {
let tab = crate::ported::params::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); crate::ported::params::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 { use crate::ported::zsh_h::{
MOD_BUSY, MOD_INIT_B, MOD_INIT_S, MOD_LINKED,
MOD_SETUP, MOD_UNLOAD,
};
if modname_ok(name) == 0 { return false; }
crate::ported::signals::queue_signals(); let exists = self.modules.contains_key(name);
if !exists { crate::ported::signals::unqueue_signals(); return false; }
if let Some(m) = self.modules.get(name) {
if (m.node.flags & MOD_SETUP) != 0 { crate::ported::signals::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 { crate::ported::signals::unqueue_signals(); return true; }
if (m.node.flags & MOD_BUSY) != 0 { crate::ported::signals::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; }
crate::ported::signals::unqueue_signals(); true }
pub fn unload_module(&mut self, name: &str) -> bool { use crate::ported::zsh_h::{
MOD_ALIAS, MOD_INIT_B, MOD_INIT_S, MOD_LINKED, MOD_UNLOAD,
};
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 { use crate::ported::zsh_h::BINF_AUTOALL;
use crate::ported::module::{FEAT_AUTOALL, FEAT_IGNORE};
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 { use crate::ported::module::FEAT_IGNORE;
let bn = crate::ported::builtin::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 { use crate::ported::zsh_h::BINF_ADDED;
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 { use crate::ported::module::{FEAT_AUTOALL, FEAT_IGNORE, FEAT_INFIX};
use crate::ported::zsh_h::{CONDF_AUTOALL, CONDF_INFIX};
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) {
self.autoload_conditions.remove(name);
}
pub fn addhookdef(&mut self, h: &str) { self.hooks.entry(h.to_string()).or_default();
}
pub fn addhookdefs(&mut self, names: &[&str]) {
for name in names {
self.addhookdef(name);
}
}
pub fn deletehookdef(&mut self, name: &str) { self.hooks.remove(name);
}
pub fn deletehookdefs(&mut self, names: &[&str]) {
for name in names {
self.deletehookdef(name);
}
}
pub fn addhookfunc(&mut self, n: &str, f: &str) {
self.hooks
.entry(n.to_string())
.or_default()
.push(f.to_string());
}
pub fn deletehookfunc(&mut self, n: &str, f: &str) {
if let Some(funcs) = self.hooks.get_mut(n) {
funcs.retain(|f| f != f);
}
}
pub fn gethookdef(&self, n: &str) -> Option<&Vec<String>> {
self.hooks.get(n)
}
pub fn runhookdef(&self, name: &str) -> Vec<String> { self.hooks.get(name).cloned().unwrap_or_default()
}
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 { use crate::ported::module::FEAT_AUTOALL;
let _ret: i32;
crate::ported::signals::queue_signals(); let exists = self.autoload_params.contains_key(name); if exists {
crate::ported::signals::unqueue_signals(); if (flags & crate::ported::module::FEAT_IGNORE) != 0 {
return 0; }
return -1; }
self.autoload_params.insert(name.to_string(), module.to_string()); let _ = crate::ported::zsh_h::PM_AUTOLOAD; if (flags & FEAT_AUTOALL) != 0 { let _ = crate::ported::zsh_h::PM_AUTOALL; }
crate::ported::signals::unqueue_signals(); 0 }
pub fn del_autoparam(&mut self, name: &str, flags: i32) -> i32 { use crate::ported::module::FEAT_IGNORE;
use crate::ported::zsh_h::PM_AUTOLOAD;
let pm_flags = crate::ported::params::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) = crate::ported::params::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 { crate::ported::utils::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 & crate::ported::zsh_h::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 crate::ported::zsh_h::module) -> i32 { 0 }
#[allow(unused_variables)]
pub fn dyn_features_module(m: *const crate::ported::zsh_h::module, features: &mut Vec<String>) -> i32 { 0 }
#[allow(unused_variables)]
pub fn dyn_enables_module(m: *const crate::ported::zsh_h::module, enables: &mut Option<Vec<i32>>) -> i32 { 0 }
#[allow(unused_variables)]
pub fn dyn_boot_module(m: *const crate::ported::zsh_h::module) -> i32 { 0 }
#[allow(unused_variables)]
pub fn dyn_cleanup_module(m: *const crate::ported::zsh_h::module) -> i32 { 0 }
#[allow(unused_variables)]
pub fn dyn_finish_module(m: *const crate::ported::zsh_h::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 { crate::ported::utils::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 { crate::ported::utils::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;
}
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 & crate::ported::zsh_h::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: &crate::ported::zsh_h::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); }
}
crate::ported::mem::unqueue_signals(); ret }
pub fn bin_zmodload_alias(table: &mut modulestab, nam: &str, args: &[String], ops: &crate::ported::zsh_h::options) -> i32 {
if args.is_empty() {
if crate::ported::zsh_h::OPT_ISSET(ops, b'R') { crate::ported::utils::zwarnnam(nam, "no module alias to remove"); return 1; }
for (name, m) in &table.modules {
if (m.node.flags & crate::ported::zsh_h::MOD_ALIAS) != 0 {
if crate::ported::zsh_h::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 { crate::ported::utils::zwarnnam(nam, &format!("invalid module name `{}'", lhs)); return 1; }
if crate::ported::zsh_h::OPT_ISSET(ops, b'R') { if aliasname.is_some() { crate::ported::utils::zwarnnam(nam,
&format!("bad syntax for removing module alias: {}", lhs)); return 1; }
match table.modules.get(lhs) {
Some(m) => {
if (m.node.flags & crate::ported::zsh_h::MOD_ALIAS) == 0 { crate::ported::utils::zwarnnam(nam,
&format!("module is not an alias: {}", lhs)); return 1; }
table.modules.remove(lhs); }
None => {
crate::ported::utils::zwarnnam(nam,
&format!("no such module alias: {}", lhs)); return 1; }
}
} else {
if let Some(target) = aliasname { if modname_ok(target) == 0 { crate::ported::utils::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 { crate::ported::utils::zwarnnam(nam,
&format!("module alias would refer to itself: {}", lhs)); return 1; }
match table.modules.get(mname) {
Some(m) if (m.node.flags & crate::ported::zsh_h::MOD_ALIAS) != 0 => {
mname = m.alias.as_deref().unwrap_or("");
}
_ => break,
}
}
if let Some(m) = table.modules.get_mut(lhs) {
if (m.node.flags & crate::ported::zsh_h::MOD_ALIAS) == 0 { crate::ported::utils::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 = crate::ported::zsh_h::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 & crate::ported::zsh_h::MOD_ALIAS) != 0 => {
if crate::ported::zsh_h::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(_) => {
crate::ported::utils::zwarnnam(nam,
&format!("module is not an alias: {}", lhs)); return 1; }
None => {
crate::ported::utils::zwarnnam(nam,
&format!("no such module alias: {}", lhs)); return 1; }
}
}
}
}
0 }
pub fn bin_zmodload_exist(table: &mut modulestab, _nam: &str, args: &[String], _ops: &crate::ported::zsh_h::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; }
if find_module(table, arg, FINDMOD_ALIASP).is_none() { ret = 1; }
}
ret }
pub fn bin_zmodload_dep(table: &mut modulestab, _nam: &str, args: &[String], ops: &crate::ported::zsh_h::options) -> i32 { if crate::ported::zsh_h::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 & crate::ported::zsh_h::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: &crate::ported::zsh_h::options) -> i32 { let fchar: char; let _flags: i32 = if crate::ported::zsh_h::OPT_ISSET(ops, b'i') { FEAT_IGNORE } else { 0 };
if crate::ported::zsh_h::OPT_ISSET(ops, b'c') {
fchar = if crate::ported::zsh_h::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 crate::ported::zsh_h::OPT_ISSET(ops, b'p') { if args.is_empty() {
for (name, module) in &table.autoload_params {
println!("{} {}", module, name);
}
return 0;
}
} else if crate::ported::zsh_h::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 crate::ported::zsh_h::OPT_ISSET(ops, b'L') {
crate::ported::zsh_h::PRINT_LIST
} else { 0 });
}
return 0;
}
}
if args.len() < 2 { return 1; }
let modnam = &args[0]; for nm in &args[1..] {
if crate::ported::zsh_h::OPT_ISSET(ops, b'p') {
table.autoload_params.insert(nm.clone(), modnam.clone());
} else if crate::ported::zsh_h::OPT_ISSET(ops, b'f') {
table.autoload_mathfuncs.insert(nm.clone(), modnam.clone());
} else if crate::ported::zsh_h::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: &crate::ported::zsh_h::options) -> i32 { let mut ret: i32 = 0;
if crate::ported::zsh_h::OPT_ISSET(ops, b'u') { for arg in args {
if unload_named_module(table, arg, nam, crate::ported::zsh_h::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: &crate::ported::zsh_h::options) -> i32 { let modname = args.first(); let rest_args = if args.is_empty() { &args[..] } else { &args[1..] };
if modname.is_none() {
if crate::ported::zsh_h::OPT_ISSET(ops, b'L') { if crate::ported::zsh_h::OPT_ISSET(ops, b'P') { crate::ported::utils::zwarnnam(nam, "-P is only allowed with a module name"); return 1; }
for (name, _m) in &table.modules {
println!("zmodload -F {}", name);
}
return 0; }
crate::ported::utils::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 [crate::ported::zsh_h::conddef],
e: Option<&[i32]>) -> i32 {
use crate::ported::zsh_h::CONDF_ADDED;
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 = crate::ported::zsh_h::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 { crate::ported::utils::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 { crate::ported::utils::zwarnnam(nam, &format!("condition `{}' already deleted", entry.name));
ret = 1;
} else {
entry.flags &= !CONDF_ADDED; }
}
}
ret }
pub fn setmathfuncs(nam: &str, f: &mut [crate::ported::zsh_h::mathfunc],
e: Option<&[i32]>) -> i32 {
use crate::ported::zsh_h::MFF_ADDED;
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 = crate::ported::zsh_h::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 { crate::ported::utils::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 { crate::ported::utils::zwarnnam(nam, &format!("math function `{}' already deleted", entry.name));
ret = 1;
}
}
}
ret }
pub static CONDTAB: Lazy<Mutex<Vec<crate::ported::zsh_h::conddef>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub fn deleteconddef(c: &crate::ported::zsh_h::conddef) -> i32 { use crate::ported::zsh_h::CONDF_INFIX;
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: crate::ported::zsh_h::conddef) -> i32 { use crate::ported::zsh_h::{CONDF_INFIX, CONDF_ADDED};
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<crate::ported::zsh_h::funcwrap>>> = Lazy::new(|| Mutex::new(Vec::new()));
pub fn addmathfunc(f: crate::ported::zsh_h::mathfunc) -> i32 { use crate::ported::zsh_h::{MFF_ADDED, MFF_USERFUNC};
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: &crate::ported::zsh_h::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 &= !crate::ported::zsh_h::MFF_ADDED; } 0
}
None => -1, }
}
pub fn addwrapper(_m: &str, w: crate::ported::zsh_h::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: &crate::ported::zsh_h::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 crate::ported::zsh_h::module,
bn: &[crate::ported::zsh_h::builtin], cd: &[crate::ported::zsh_h::conddef], mf: &[crate::ported::zsh_h::mathfunc], pd: &[crate::ported::zsh_h::paramdef], n_abstract: i32, ) -> Vec<String> {
use crate::ported::zsh_h::CONDF_INFIX;
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 crate::ported::zsh_h::module,
bn: &[crate::ported::zsh_h::builtin], cd: &[crate::ported::zsh_h::conddef], mf: &[crate::ported::zsh_h::mathfunc], pd: &[crate::ported::zsh_h::paramdef], n_abstract: i32, ) -> Vec<i32> {
use crate::ported::zsh_h::{BINF_ADDED, CONDF_ADDED, MFF_ADDED};
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: Lazy<Mutex<HashMap<String, Vec<String>>>> = Lazy::new(|| Mutex::new(HashMap::new()));
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 hooks: HashMap<String, Vec<String>>,
pub added_builtins: HashMap<String, u32>,
}
pub use crate::ported::zsh_h::{BINF_ADDED, CONDF_INFIX, CONDF_ADDED, MFF_ADDED};
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 super::*;
#[test]
fn test_module_table_new() {
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 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 table = modulestab::new();
let loaded = table.list_loaded();
assert!(loaded.len() > 20);
assert!(loaded.contains(&"zsh/complete"));
}
#[test]
fn test_hooks() {
let mut table = modulestab::new();
table.addhookdef("chpwd");
table.addhookfunc("chpwd", "my_chpwd_handler");
let funcs = table.runhookdef("chpwd");
assert_eq!(funcs, vec!["my_chpwd_handler"]);
table.deletehookfunc("chpwd", "my_chpwd_handler");
let funcs = table.runhookdef("chpwd");
assert!(funcs.is_empty());
}
#[test]
fn test_autoload() {
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 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 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 module = module::new("zsh/test");
let output = printmodulenode("zsh/test", &module);
assert!(output.contains("zsh/test"));
assert!(output.contains("loaded"));
}
fn mk_mf(name: &str, autoload: bool) -> crate::ported::zsh_h::mathfunc {
crate::ported::zsh_h::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 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 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 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 probe = mk_mf("zshrs_test_never_added_xyz", false);
assert_eq!(deletemathfunc(&probe), -1);
}
#[test]
fn deletemathfunc_clears_added_flag_for_userfunc() {
let mut f = mk_mf("zshrs_test_clear_flag", false);
f.flags = crate::ported::zsh_h::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 = crate::ported::zsh_h::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 & crate::ported::zsh_h::MFF_ADDED, 0);
}
drop(tab);
removemathfunc("zshrs_test_clear_flag2");
}
fn mk_cd(name: &str, infix: bool, autoload: bool) -> crate::ported::zsh_h::conddef {
crate::ported::zsh_h::conddef {
next: None, name: name.to_string(),
flags: if infix { crate::ported::zsh_h::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 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 probe = mk_cd("zshrs_test_cond_never_added", false, false);
assert_eq!(deleteconddef(&probe), -1);
}
#[test]
fn addconddef_distinguishes_infix_from_prefix() {
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 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 & crate::ported::zsh_h::CONDF_ADDED, 0);
assert_ne!(entries[1].flags & crate::ported::zsh_h::CONDF_ADDED, 0);
let del_selectors = [0, 0];
assert_eq!(setconddefs("test", &mut entries, Some(&del_selectors)), 0);
assert_eq!(entries[0].flags & crate::ported::zsh_h::CONDF_ADDED, 0);
assert_eq!(entries[1].flags & crate::ported::zsh_h::CONDF_ADDED, 0);
}
fn mk_b(nam: &str) -> crate::ported::zsh_h::builtin {
crate::ported::zsh_h::builtin {
node: crate::ported::zsh_h::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 _ = crate::ported::builtin::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 & crate::ported::zsh_h::BINF_ADDED as i32, 0);
}
}
#[test]
fn addbuiltins_skips_already_added_entries() {
let mut b1 = mk_b("zshrs_test_already_added_1");
b1.node.flags = crate::ported::zsh_h::BINF_ADDED as i32;
let mut b2 = mk_b("zshrs_test_already_added_2");
b2.node.flags = crate::ported::zsh_h::BINF_ADDED as i32;
let mut binl = vec![b1, b2];
assert_eq!(addbuiltins("test", &mut binl), 0);
}
fn mk_w() -> crate::ported::zsh_h::funcwrap {
crate::ported::zsh_h::funcwrap {
next: None, flags: 0,
handler: Some(|_prog, _w, _name| 0),
module: None,
}
}
#[test]
fn addwrapper_then_deletewrapper_round_trip() {
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 snapshot: Vec<_> = WRAPPERS.lock().unwrap().drain(..).collect();
let probe = mk_w();
assert_eq!(deletewrapper("zsh/test", &probe), 1);
WRAPPERS.lock().unwrap().extend(snapshot);
}
}
#[cfg(test)]
mod modname_tests {
use super::*;
#[test]
fn modname_ok_accepts_canonical_zsh_module_paths() {
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() {
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() {
assert_eq!(modname_ok(""), 1);
}
}