#![allow(non_snake_case, non_upper_case_globals, dead_code)]
use std::sync::atomic::{AtomicI32, Ordering};
use std::sync::{Mutex, OnceLock};
use crate::ported::zsh_h::{
Bnull, Inbrace, Outbrace, QT_BACKSLASH, QT_DOLLARS, QT_DOUBLE, QT_SINGLE, Stringg,
};
#[allow(unused_imports)]
#[allow(unused_imports)]
use crate::ported::zle::zle_main::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_misc::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_hist::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_move::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_word::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_params::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_vi::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_utils::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_refresh::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_tricky::*;
#[allow(unused_imports)]
use crate::ported::zle::textobjects::*;
#[allow(unused_imports)]
use crate::ported::zle::deltochar::*;
use crate::ported::zle::comp_h::{
Aminfo, Cexpl, Cmatch, Cmgroup, CGF_MATSORT, CGF_NOSORT, CGF_NUMSORT, CGF_REVSORT,
CGF_UNIQALL, CGF_UNIQCON, CMF_DELETE, CMF_DISPLINE, CMF_FMULT, CMF_MULT, CMF_NOLIST,
CMF_PACKED, CMF_PARBR, CMF_PARNEST, CMF_ROWS,
};
use crate::ported::zle::complete::{COMPIPREFIX, COMPPREFIX, COMPSUFFIX};
use crate::ported::zle::zle_tricky::{MENUCMP, USEMENU};
use crate::ported::zle::complete::COMPLIST;
use crate::ported::zle::zle_tricky::{USEGLOB, WOULDINSTAB};
use crate::ported::zsh_h::{Dnull, Equals, Hat, Inbrack, Inpar, Outpar, Pound, Qstring, Quest, Snull, Star, Tilde};
use crate::ported::zle::comp_h::{CAF_ALL, CAF_MATSORT, CAF_NOSORT, CAF_NUMSORT, CAF_QUOTE, CAF_REVSORT, CAF_UNIQALL, CAF_UNIQCON};
pub static WB: AtomicI32 = AtomicI32::new(0); pub static WE: AtomicI32 = AtomicI32::new(0); pub static ZLEMETACS: AtomicI32 = AtomicI32::new(0); pub static ZLEMETALL: AtomicI32 = AtomicI32::new(0); pub static ADDEDX: AtomicI32 = AtomicI32::new(0);
pub static ZLEMETALINE: OnceLock<Mutex<String>> = OnceLock::new(); pub static ZLELINE: OnceLock<Mutex<String>> = OnceLock::new(); pub static ZLECS: AtomicI32 = AtomicI32::new(0); pub static ZLELL: AtomicI32 = AtomicI32::new(0); pub static INWHAT: AtomicI32 = AtomicI32::new(0); pub static ZMULT: AtomicI32 = AtomicI32::new(1); pub static compfunc: OnceLock<Mutex<Option<String>>> = OnceLock::new(); pub static comppatmatch: OnceLock<Mutex<Option<String>>> = OnceLock::new();
pub static compqstack: OnceLock<Mutex<String>> = OnceLock::new();
#[doc(hidden)]
pub use crate::ported::zle::zle_tricky::{NBRBEG as _NBRBEG, NBREND as _NBREND};
use crate::zsh_h::{isset, BASHAUTOLIST, NUMERICGLOBSORT, RCQUOTES, SORTIT_IGNORING_BACKSLASHES, SORTIT_NUMERICALLY};
pub static useexact: AtomicI32 = AtomicI32::new(0); pub static useline: AtomicI32 = AtomicI32::new(0); pub static uselist: AtomicI32 = AtomicI32::new(0); pub static forcelist: AtomicI32 = AtomicI32::new(0); pub static startauto: AtomicI32 = AtomicI32::new(0);
pub static iforcemenu: AtomicI32 = AtomicI32::new(0);
pub static dolastprompt: AtomicI32 = AtomicI32::new(0);
pub static oldlist: AtomicI32 = AtomicI32::new(0); pub static oldins: AtomicI32 = AtomicI32::new(0);
pub static origlpre: AtomicI32 = AtomicI32::new(0); pub static origlsuf: AtomicI32 = AtomicI32::new(0); pub static lenchanged: AtomicI32 = AtomicI32::new(0);
pub static movetoend: AtomicI32 = AtomicI32::new(0);
pub static insmnum: AtomicI32 = AtomicI32::new(0); pub static insspace: AtomicI32 = AtomicI32::new(0);
pub static menuacc: AtomicI32 = AtomicI32::new(0);
pub static hasunqu: AtomicI32 = AtomicI32::new(0); pub static useqbr: AtomicI32 = AtomicI32::new(0); pub static brpcs: AtomicI32 = AtomicI32::new(0); pub static brscs: AtomicI32 = AtomicI32::new(0);
pub static ispar: AtomicI32 = AtomicI32::new(0); pub static linwhat: AtomicI32 = AtomicI32::new(0);
pub static parpre: OnceLock<Mutex<String>> = OnceLock::new();
pub static parflags: AtomicI32 = AtomicI32::new(0);
pub static mflags: AtomicI32 = AtomicI32::new(0);
pub static parq: AtomicI32 = AtomicI32::new(0); pub static eparq: AtomicI32 = AtomicI32::new(0);
pub static ipre: OnceLock<Mutex<String>> = OnceLock::new(); pub static ripre: OnceLock<Mutex<String>> = OnceLock::new(); pub static isuf: OnceLock<Mutex<String>> = OnceLock::new();
pub static matches: OnceLock<Mutex<Vec<Cmatch>>> = OnceLock::new(); pub static fmatches: OnceLock<Mutex<Vec<Cmatch>>> = OnceLock::new();
pub static amatches: OnceLock<Mutex<Vec<Cmgroup>>> = OnceLock::new(); pub static pmatches: OnceLock<Mutex<Vec<Cmgroup>>> = OnceLock::new(); pub static lastmatches: OnceLock<Mutex<Vec<Cmgroup>>> = OnceLock::new(); pub static lmatches: OnceLock<Mutex<Option<Cmgroup>>> = OnceLock::new(); pub static lastlmatches: OnceLock<Mutex<Option<Cmgroup>>> = OnceLock::new();
pub static hasoldlist: AtomicI32 = AtomicI32::new(0); pub static hasperm: AtomicI32 = AtomicI32::new(0); pub static hasallmatch: AtomicI32 = AtomicI32::new(0);
pub static newmatches: AtomicI32 = AtomicI32::new(0);
pub static permmnum: AtomicI32 = AtomicI32::new(0); pub static permgnum: AtomicI32 = AtomicI32::new(0); pub static lastpermmnum: AtomicI32 = AtomicI32::new(0); pub static lastpermgnum: AtomicI32 = AtomicI32::new(0);
pub static nmatches: AtomicI32 = AtomicI32::new(0); pub static smatches: AtomicI32 = AtomicI32::new(0);
pub static diffmatches: AtomicI32 = AtomicI32::new(0);
pub static nmessages: AtomicI32 = AtomicI32::new(0);
pub static onlyexpl: AtomicI32 = AtomicI32::new(0);
pub static listdat: OnceLock<Mutex<crate::ported::zle::comp_h::Cldata>> =
OnceLock::new();
pub static ispattern: AtomicI32 = AtomicI32::new(0); pub static haspattern: AtomicI32 = AtomicI32::new(0);
pub static hasmatched: AtomicI32 = AtomicI32::new(0); pub static hasunmatched: AtomicI32 = AtomicI32::new(0);
pub static mgroup: OnceLock<Mutex<Option<Cmgroup>>> = OnceLock::new();
pub static mnum: AtomicI32 = AtomicI32::new(0);
pub static unambig_mnum: AtomicI32 = AtomicI32::new(0);
pub static maxmlen: AtomicI32 = AtomicI32::new(0); pub static minmlen: AtomicI32 = AtomicI32::new(0);
pub static expls: OnceLock<Mutex<Vec<Cexpl>>> = OnceLock::new();
pub static curexpl: OnceLock<Mutex<Option<Cexpl>>> = OnceLock::new();
pub static matchers: OnceLock<Mutex<Vec<String>>> = OnceLock::new();
pub static ainfo: OnceLock<Mutex<Option<Aminfo>>> = OnceLock::new(); pub static fainfo: OnceLock<Mutex<Option<Aminfo>>> = OnceLock::new();
pub static allccs: OnceLock<Mutex<Vec<String>>> = OnceLock::new();
pub static fromcomp: AtomicI32 = AtomicI32::new(0);
pub static lastend: AtomicI32 = AtomicI32::new(0);
pub static OLDMENUCMP: AtomicI32 = AtomicI32::new(0);
pub static PARWB: AtomicI32 = AtomicI32::new(0); pub static PARWE: AtomicI32 = AtomicI32::new(0); pub static PAROFFS: AtomicI32 = AtomicI32::new(0);
pub static MATCHORDER: AtomicI32 = AtomicI32::new(0);
pub fn rembslash(s: &str) -> String { let mut result = String::with_capacity(s.len()); let mut chars = s.chars().peekable(); while let Some(c) = chars.next() {
if c == '\\' { if let Some(nxt) = chars.next() { result.push(nxt);
}
} else {
result.push(c); }
}
result }
pub fn remsquote(s: &mut String) -> i32 { let rcquotes = isset(RCQUOTES); let qa: usize = if rcquotes { 1 } else { 3 };
let bytes = s.as_bytes(); let mut t = Vec::<u8>::with_capacity(bytes.len());
let mut ret: i32 = 0;
let mut i = 0;
while i < bytes.len() { let matched = if qa == 1 { i + 1 < bytes.len() && bytes[i] == b'\'' && bytes[i + 1] == b'\''
} else {
i + 3 < bytes.len() && bytes[i] == b'\''
&& bytes[i + 1] == b'\\'
&& bytes[i + 2] == b'\''
&& bytes[i + 3] == b'\''
};
if matched {
ret += qa as i32; t.push(b'\''); i += qa + 1; } else {
t.push(bytes[i]); i += 1;
}
}
*s = String::from_utf8(t).unwrap_or_default(); ret }
pub fn ctokenize(p: &str) -> String { let bytes = p.as_bytes(); let mut out = Vec::<u8>::with_capacity(bytes.len());
let mut bslash = false; let mut prev_idx: Option<usize> = None;
let mut i = 0;
while i < bytes.len() {
let b = bytes[i]; if b == b'\\' { bslash = true;
out.push(b);
prev_idx = Some(out.len() - 1);
} else {
if b == b'$' || b == b'{' || b == b'}' { if bslash { if let Some(pi) = prev_idx { out.truncate(pi);
let mut buf = [0u8; 4];
out.extend_from_slice(Bnull.encode_utf8(&mut buf).as_bytes());
}
out.push(b);
} else {
let tok = if b == b'$' { Stringg } else if b == b'{' { Inbrace } else { Outbrace }; let mut buf = [0u8; 4];
out.extend_from_slice(tok.encode_utf8(&mut buf).as_bytes());
}
} else {
out.push(b);
}
bslash = false; prev_idx = Some(out.len().saturating_sub(1));
}
i += 1;
}
String::from_utf8(out).unwrap_or_default() }
pub fn comp_str(untok: bool) -> (String, i32, i32) { let mut p = COMPPREFIX.get_or_init(|| Mutex::new(String::new())) .lock().unwrap().clone();
let mut s = COMPSUFFIX.get_or_init(|| Mutex::new(String::new())) .lock().unwrap().clone();
let ip = COMPIPREFIX.get_or_init(|| Mutex::new(String::new())) .lock().unwrap().clone();
if !untok { p = ctokenize(&p); p = p.chars().filter(|&c| c != Bnull).collect(); s = ctokenize(&s); s = s.chars().filter(|&c| c != Bnull).collect(); }
let lp = p.len() as i32; let lip = ip.len() as i32; let mut str = String::with_capacity(ip.len() + p.len() + s.len() + 1); str.push_str(&ip); str.push_str(&p); str.push_str(&s); (str, lip, lp) }
pub fn comp_quoting_string(stype: i32) -> &'static str { match stype { x if x == QT_SINGLE => "'", x if x == QT_DOUBLE => "\"", x if x == QT_DOLLARS => "$'", _ => { let _ = QT_BACKSLASH;
"\\" }
}
}
pub fn multiquote(s: &str, ign: i32) -> String { let stack = crate::ported::zle::complete::COMPQSTACK .get_or_init(|| Mutex::new(String::new()))
.lock()
.map(|g| g.clone())
.unwrap_or_default();
let p_bytes = stack.as_bytes();
if !p_bytes.is_empty() && (ign == 0 || p_bytes.len() > 1) { let start = if ign != 0 { 1 } else { 0 }; let mut cur = s.to_string();
for &q in &p_bytes[start..] { let qt = match q as i32 { x if x == QT_BACKSLASH => crate::ported::zsh_h::QT_BACKSLASH,
x if x == QT_SINGLE => crate::ported::zsh_h::QT_SINGLE,
x if x == QT_DOUBLE => crate::ported::zsh_h::QT_DOUBLE,
x if x == QT_DOLLARS => crate::ported::zsh_h::QT_DOLLARS,
_ => crate::ported::zsh_h::QT_BACKSLASH,
};
cur = crate::ported::utils::quotestring(&cur, qt);
}
cur } else {
s.to_string() }
}
pub fn tildequote(s: &str, ign: i32) -> String { let bytes = s.as_bytes(); let tilde = !bytes.is_empty() && bytes[0] == b'~'; let staged = if tilde { let mut tmp = String::with_capacity(s.len());
tmp.push('x');
tmp.push_str(&s[1..]);
tmp
} else {
s.to_string()
};
let mut quoted = multiquote(&staged, ign); if tilde && !quoted.is_empty() { let mut new_q = String::with_capacity(quoted.len());
let mut swapped = false;
for c in quoted.chars() {
if !swapped && c == 'x' {
new_q.push('~');
swapped = true;
} else {
new_q.push(c);
}
}
quoted = new_q;
}
quoted }
pub fn before_complete(lst: &mut i32) -> i32 { use crate::ported::zle::zle_h::{COMP_LIST_COMPLETE, COMP_LIST_EXPAND};
use crate::ported::zle::zle_tricky::{LASTAMBIG, SHOWAGAIN, VALIDLIST};
use crate::ported::zle::zle_refresh::SHOWINGLIST;
OLDMENUCMP.store(MENUCMP.load(Ordering::Relaxed), Ordering::Relaxed);
if SHOWAGAIN.load(Ordering::Relaxed) != 0
&& VALIDLIST.load(Ordering::Relaxed) != 0
{
SHOWINGLIST.store(-2, Ordering::Relaxed);
}
SHOWAGAIN.store(0, Ordering::Relaxed);
let has_cur = MINFO.get().and_then(|m| m.lock().ok())
.map(|m| m.cur.is_some())
.unwrap_or(false);
let menucmp_v = MENUCMP.load(Ordering::Relaxed);
if has_cur && menucmp_v != 0 && *lst != COMP_LIST_EXPAND {
return 1; }
if has_cur && menucmp_v != 0
&& VALIDLIST.load(Ordering::Relaxed) != 0
&& *lst == COMP_LIST_COMPLETE
{
SHOWINGLIST.store(-2, Ordering::Relaxed);
onlyexpl.store(0, Ordering::Relaxed); if let Some(ld) = listdat.get() {
if let Ok(mut g) = ld.lock() {
g.valid = 0;
}
}
return 1; }
if startauto.load(Ordering::Relaxed) != 0
&& LASTAMBIG.load(Ordering::Relaxed) != 0
{
let bashauto = isset(BASHAUTOLIST);
let last = LASTAMBIG.load(Ordering::Relaxed);
if !bashauto || last == 2 {
USEMENU.store(2, Ordering::Relaxed);
}
}
0 }
pub fn after_complete(dat: &mut [i32]) -> i32 { let menucmp_v = MENUCMP.load(Ordering::Relaxed);
let oldmenucmp_v = OLDMENUCMP.load(Ordering::Relaxed);
if menucmp_v == 0 || oldmenucmp_v != 0 {
return 0; }
let handlers: Vec<String> = crate::ported::module::HOOKTAB
.lock()
.ok()
.and_then(|t| t.get("menu_start").cloned())
.unwrap_or_default();
let mut ret: i32 = 0;
for fn_name in &handlers {
let r = crate::fusevm_bridge::with_executor(|exec| {
exec.dispatch_function_call(fn_name, &[]).unwrap_or(0)
});
if r != 0 {
ret = r;
break; }
}
if ret == 0 {
return 0; }
if dat.len() > 1 {
dat[1] = 0;
}
MENUCMP.store(0, Ordering::Relaxed);
menuacc.store(0, Ordering::Relaxed);
if let Some(m) = MINFO.get() {
if let Ok(mut mi) = m.lock() {
mi.cur = None;
}
}
if ret >= 2 { crate::ported::zle::zle_misc::fixsuffix();
ZLEMETACS.store(0, Ordering::Relaxed);
let metall = ZLEMETALL.load(Ordering::Relaxed);
crate::ported::zle::zle_utils::foredel(metall, crate::ported::zle::zle_h::CUT_RAW);
let origline_v: String = crate::ported::zle::zle_tricky::ORIGLINE
.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
let _ = crate::ported::zle::zle_tricky::inststr(&origline_v);
let origcs_v = crate::ported::zle::zle_tricky::ORIGCS.load(Ordering::Relaxed);
ZLEMETACS.store(origcs_v, Ordering::Relaxed);
if ret == 2 { crate::ported::zle::zle_refresh::CLEARLIST.store(1, Ordering::Relaxed);
crate::ported::zle::zle_h::invalidatelist();
}
}
0 }
pub fn set_list_array(name: &str, l: &[String]) { let _ = crate::ported::params::setaparam(name, l.to_vec()); }
pub fn get_user_var(nam: Option<&str>) -> Option<Vec<String>> { let nam = nam?; if nam.starts_with('(') { let mut arrlist: Vec<String> = Vec::new();
let bytes = nam.as_bytes();
let mut buf = Vec::<u8>::new();
let mut notempty = false; let mut brk = false;
let mut i = 1; while i < bytes.len() {
let b = bytes[i];
if b == b'\\' && i + 1 < bytes.len() { buf.push(bytes[i + 1]); notempty = true;
i += 2;
continue;
}
if b == b',' || b == b' ' || b == b'\t' || b == b'\n' || b == b')' {
if b == b')' { brk = true; } if notempty { let mut start = 0;
if !buf.is_empty() && buf[0] == b'\n' { start = 1; } let s = String::from_utf8_lossy(&buf[start..]).into_owned();
arrlist.push(s); }
buf.clear(); notempty = false;
} else {
notempty = true; buf.push(b);
}
i += 1;
if brk { break; } }
if !brk || arrlist.is_empty() { return None; } Some(arrlist) } else { crate::ported::signals::queue_signals();
let result = {
let tab = match crate::ported::params::paramtab().read() {
Ok(t) => t,
Err(_) => {
crate::ported::signals::unqueue_signals();
return None;
}
};
tab.get(nam).and_then(|pm| {
if let Some(arr) = pm.u_arr.as_ref() {
Some(arr.clone()) } else if let Some(s) = pm.u_str.as_ref() {
Some(vec![s.clone()]) } else {
None
}
})
};
crate::ported::signals::unqueue_signals(); result
}
}
pub fn get_data_arr(name: &str, keys: bool) -> Option<Vec<String>> { use crate::ported::params::{paramtab, paramtab_hashed_storage};
use crate::ported::zsh_h::{PM_HASHED, PM_TYPE};
crate::ported::signals::queue_signals();
let is_hashed = match paramtab().read() {
Ok(t) => t.get(name)
.map(|pm| PM_TYPE(pm.node.flags as u32) == PM_HASHED)
.unwrap_or(false),
Err(_) => false,
};
let result = if is_hashed {
paramtab_hashed_storage().lock().ok().and_then(|m| {
m.get(name).map(|map| {
if keys {
map.keys().cloned().collect::<Vec<_>>()
} else {
map.values().cloned().collect::<Vec<_>>()
}
})
})
} else {
None
};
crate::ported::signals::unqueue_signals(); result
}
pub fn addmatch(str: &str, flags: i32, disp: Option<&str>, line: bool) { let mut cm = Cmatch::default(); cm.str = Some(str.to_string()); let complist_extra = {
let s = COMPLIST.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let packed = if s.contains("packed") { CMF_PACKED } else { 0 }; let rows = if s.contains("rows") { CMF_ROWS } else { 0 }; if s.is_empty() { 0 } else { packed | rows }
};
cm.flags = flags | complist_extra; if let Some(d) = disp { cm.disp = Some(d.to_string()); } else if line { cm.disp = Some(String::new()); cm.flags |= CMF_DISPLINE; }
mnum.fetch_add(1, Ordering::Relaxed); {
let cell = curexpl.get_or_init(|| Mutex::new(None)); if let Ok(mut g) = cell.lock() {
if let Some(e) = g.as_mut() { e.count += 1; }
}
}
let mcell = matches.get_or_init(|| Mutex::new(Vec::new())); if let Ok(mut g) = mcell.lock() { g.push(cm); }
newmatches.store(1, Ordering::Relaxed); {
let cell = mgroup.get_or_init(|| Mutex::new(None)); if let Ok(mut g) = cell.lock() {
if let Some(grp) = g.as_mut() { grp.new_ = 1; }
}
}
}
pub fn begcmgroup(n: Option<&str>, flags: i32) { if let Some(name) = n { let mask = CGF_NOSORT | CGF_UNIQALL | CGF_UNIQCON | CGF_MATSORT | CGF_NUMSORT | CGF_REVSORT;
let cell = amatches.get_or_init(|| Mutex::new(Vec::new()));
if let Ok(g) = cell.lock() {
for grp in g.iter() { if grp.name.as_deref() == Some(name) && (grp.flags & mask) == flags
{
let active = grp.clone(); let mc = mgroup.get_or_init(|| Mutex::new(None));
if let Ok(mut s) = mc.lock() { *s = Some(active); }
return; }
}
}
}
let mut grp = Cmgroup::default(); grp.name = n.map(String::from); grp.flags = flags; let cell = amatches.get_or_init(|| Mutex::new(Vec::new()));
if let Ok(mut g) = cell.lock() {
g.insert(0, grp.clone()); }
let mc = mgroup.get_or_init(|| Mutex::new(None));
if let Ok(mut s) = mc.lock() { *s = Some(grp); }
if let Ok(mut g) = expls.get_or_init(|| Mutex::new(Vec::new())).lock() { g.clear(); }
if let Ok(mut g) = matches.get_or_init(|| Mutex::new(Vec::new())).lock() { g.clear(); }
if let Ok(mut g) = fmatches.get_or_init(|| Mutex::new(Vec::new())).lock() { g.clear(); }
if let Ok(mut g) = allccs.get_or_init(|| Mutex::new(Vec::new())).lock() { g.clear(); }
}
pub fn endcmgroup(ylist: Option<Vec<String>>) { let cell = mgroup.get_or_init(|| Mutex::new(None));
if let Ok(mut g) = cell.lock() {
if let Some(grp) = g.as_mut() {
grp.ylist = ylist.unwrap_or_default(); }
}
}
pub fn addexpl(always: bool) { let curexpl_snap = {
let cell = curexpl.get_or_init(|| Mutex::new(None));
cell.lock().ok().and_then(|g| g.clone())
};
let curexpl_str = match curexpl_snap.as_ref().and_then(|e| e.str.clone()) {
Some(s) => s,
None => return,
};
let curexpl_count = curexpl_snap.as_ref().map(|e| e.count).unwrap_or(0);
let curexpl_fcount = curexpl_snap.as_ref().map(|e| e.fcount).unwrap_or(0);
let elist = expls.get_or_init(|| Mutex::new(Vec::new()));
if let Ok(mut g) = elist.lock() {
for e in g.iter_mut() { if e.str.as_deref() == Some(curexpl_str.as_str()) { e.count += curexpl_count; e.fcount += curexpl_fcount; if always { e.always = 1;
nmessages.fetch_add(1, Ordering::Relaxed); newmatches.store(1, Ordering::Relaxed); let mc = mgroup.get_or_init(|| Mutex::new(None));
if let Ok(mut mg) = mc.lock() {
if let Some(grp) = mg.as_mut() { grp.new_ = 1; }
}
}
return; }
}
if let Some(e) = curexpl_snap { g.push(e);
}
}
newmatches.store(1, Ordering::Relaxed); if always { let mc = mgroup.get_or_init(|| Mutex::new(None));
if let Ok(mut mg) = mc.lock() {
if let Some(grp) = mg.as_mut() { grp.new_ = 1; }
}
nmessages.fetch_add(1, Ordering::Relaxed); }
}
pub fn matchcmp(a: &Cmatch, b: &Cmatch) -> std::cmp::Ordering { let order = MATCHORDER.load(Ordering::Relaxed);
let sortdir = if (order & CGF_REVSORT) != 0 { -1 } else { 1 };
let cmp = (b.disp.is_some() as i32) - (a.disp.is_some() as i32); let (as_, bs) = if (order & CGF_MATSORT) != 0 || (cmp == 0 && a.disp.is_none()) {
(a.str.clone().unwrap_or_default(), b.str.clone().unwrap_or_default()) } else {
if cmp != 0 { let raw = (cmp as i32) * sortdir;
return if raw < 0 { std::cmp::Ordering::Less } else if raw > 0 { std::cmp::Ordering::Greater }
else { std::cmp::Ordering::Equal };
}
let displine_cmp = (b.flags & CMF_DISPLINE) - (a.flags & CMF_DISPLINE); if displine_cmp != 0 { let raw = displine_cmp * sortdir;
return if raw < 0 { std::cmp::Ordering::Less }
else if raw > 0 { std::cmp::Ordering::Greater }
else { std::cmp::Ordering::Equal };
}
(a.disp.clone().unwrap_or_default(), b.disp.clone().unwrap_or_default()) };
let raw = sortdir * if as_ == bs { 0 } else if as_ < bs { -1 } else { 1 };
if raw < 0 { std::cmp::Ordering::Less } else if raw > 0 { std::cmp::Ordering::Greater }
else { std::cmp::Ordering::Equal }
}
#[inline]
fn matchstreq(a: Option<&String>, b: Option<&String>) -> bool { match (a, b) {
(None, None) => true,
(Some(x), Some(y)) => x == y,
_ => false,
}
}
pub fn matcheq(a: &Cmatch, b: &Cmatch) -> bool { matchstreq(a.ipre.as_ref(), b.ipre.as_ref()) && matchstreq(a.pre.as_ref(), b.pre.as_ref()) && matchstreq(a.ppre.as_ref(), b.ppre.as_ref()) && matchstreq(a.psuf.as_ref(), b.psuf.as_ref()) && matchstreq(a.suf.as_ref(), b.suf.as_ref()) && matchstreq(a.str.as_ref(), b.str.as_ref()) }
pub fn freematch(_m: Cmatch) { }
pub fn freematches(_g: Vec<Cmgroup>) { }
pub fn do_completion(s: &str, incmd: i32, lst: i32) -> i32 {
let osl = crate::ported::zle::zle_refresh::SHOWINGLIST.load(Ordering::Relaxed); let mut ret: i32 = 0;
if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() { *g = None; }
if let Ok(mut g) = fainfo.get_or_init(|| Mutex::new(None)).lock() { *g = None; }
if let Ok(mut g) = matchers.get_or_init(|| Mutex::new(Vec::new())).lock() {
g.clear(); }
let instring = crate::ported::zle::zle_tricky::INSTRING.load(Ordering::Relaxed); let head_q: char = if instring == crate::ported::zsh_h::QT_NONE { crate::ported::zsh_h::QT_BACKSLASH as u8 as char
} else {
instring as u8 as char
};
if let Ok(mut g) = compqstack.get_or_init(|| Mutex::new(String::new())).lock() {
*g = head_q.to_string(); }
hasunqu.store(0, Ordering::Relaxed); let wouldinstab_v = WOULDINSTAB.load(Ordering::Relaxed); useline.store( if wouldinstab_v != 0 { -1 } else if lst != crate::ported::zle::zle_h::COMP_LIST_COMPLETE { 1 } else { 0 },
Ordering::Relaxed,
);
useexact.store(opt_isset("RECEXACT"), Ordering::Relaxed); set_compstate_str("exact_string", ""); let useline_v = useline.load(Ordering::Relaxed);
uselist.store( if useline_v != 0 {
if opt_isset("AUTOLIST") != 0 && opt_isset("BASHAUTOLIST") == 0 {
if opt_isset("LISTAMBIGUOUS") != 0 { 3 } else { 2 }
} else { 0 }
} else { 1 },
Ordering::Relaxed,
);
let useglob_v = USEGLOB.load(Ordering::Relaxed); let opm: String = if useglob_v != 0 { "*".into() } else { "".into() };
if let Ok(mut g) = comppatmatch.get_or_init(|| Mutex::new(None)).lock() {
*g = Some(opm.clone()); }
set_compstate_str("pattern_insert", "menu"); forcelist.store(0, Ordering::Relaxed); haspattern.store(0, Ordering::Relaxed); let _complistmax = env_iparam("LISTMAX");
set_compstate_str( "last_prompt",
if opt_isset("ALWAYSLASTPROMPT") != 0 { "yes" } else { "" },
);
dolastprompt.store(1, Ordering::Relaxed);
let cl_str = if opt_isset("LISTROWSFIRST") != 0 {
if opt_isset("LISTPACKED") != 0 { "packed rows" } else { "rows" }
} else if opt_isset("LISTPACKED") != 0 { "packed" } else { "" };
if let Ok(mut g) = crate::ported::zle::complete::COMPLIST
.get_or_init(|| Mutex::new(String::new())).lock()
{
*g = cl_str.into(); }
startauto.store(opt_isset("AUTOMENU"), Ordering::Relaxed);
let zlc = ZLEMETACS.load(Ordering::Relaxed);
let we_v = WE.load(Ordering::Relaxed);
movetoend.store( if zlc == we_v || opt_isset("ALWAYSTOEND") != 0 { 2 } else { 1 },
Ordering::Relaxed,
);
crate::ported::zle::zle_refresh::SHOWINGLIST.store(0, Ordering::Relaxed); hasmatched.store(0, Ordering::Relaxed); hasunmatched.store(0, Ordering::Relaxed); minmlen.store(1_000_000, Ordering::Relaxed); maxmlen.store(-1, Ordering::Relaxed); nmessages.store(0, Ordering::Relaxed); hasallmatch.store(0, Ordering::Relaxed);
if makecomplist(s, incmd, lst) != 0 { ZLEMETACS.store(0, Ordering::Relaxed); foredel(ZLEMETALL.load(Ordering::Relaxed)); inststr(&crate::ported::zle::zle_tricky::ORIGLINE.get_or_init(|| Mutex::new(String::new())).lock().map(|g| g.clone()).unwrap_or_default()); ZLEMETACS.store(crate::ported::zle::zle_tricky::ORIGCS.load(Ordering::Relaxed), Ordering::Relaxed); crate::ported::zle::zle_refresh::CLEARLIST.store(1, Ordering::Relaxed); ret = 1;
if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() { g.cur = None; } if useline.load(Ordering::Relaxed) < 0 { unmetafy_line();
ret = selfinsert(); metafy_line();
}
return goto_compend(ret); }
lastprebr_set(""); lastpostbr_set("");
let curpm = comppatmatch.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.clone()).unwrap_or_default();
if !curpm.is_empty() && curpm != opm { haspattern.store(1, Ordering::Relaxed); }
let nm = nmatches.load(Ordering::Relaxed); let dm = diffmatches.load(Ordering::Relaxed);
if iforcemenu.load(Ordering::Relaxed) != 0 { if nm != 0 { { let _ = crate::ported::zle::compresult::do_ambig_menu(); }; } ret = if nm == 0 { 1 } else { 0 }; } else if useline.load(Ordering::Relaxed) < 0 { unmetafy_line();
ret = selfinsert(); metafy_line();
} else if useline.load(Ordering::Relaxed) == 0
&& uselist.load(Ordering::Relaxed) != 0
{ ZLEMETACS.store(0, Ordering::Relaxed); foredel(ZLEMETALL.load(Ordering::Relaxed)); inststr(&crate::ported::zle::zle_tricky::ORIGLINE.get_or_init(|| Mutex::new(String::new())).lock().map(|g| g.clone()).unwrap_or_default()); ZLEMETACS.store(crate::ported::zle::zle_tricky::ORIGCS.load(Ordering::Relaxed), Ordering::Relaxed); crate::ported::zle::zle_refresh::SHOWINGLIST.store(-2, Ordering::Relaxed); } else if useline.load(Ordering::Relaxed) == 2 && nm > 1 { {
let groups = amatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let mut all: Vec<String> = Vec::new();
for g in groups {
for m in g.matches {
if let Some(s) = m.str { all.push(s); }
}
}
let buf = ZLEMETALINE.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let cs = ZLEMETACS.load(Ordering::Relaxed) as usize;
let wb = WB.load(Ordering::Relaxed) as usize;
let we = WE.load(Ordering::Relaxed) as usize;
let (new_buf, new_cs) = crate::ported::zle::compresult::do_allmatches(
&buf, cs, wb, we, &all, " ",
);
if let Ok(mut g) = ZLEMETALINE.get_or_init(|| Mutex::new(String::new())).lock() {
*g = new_buf;
ZLEMETALL.store(g.len() as i32, Ordering::Relaxed);
}
ZLEMETACS.store(new_cs as i32, Ordering::Relaxed);
}
if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() { g.cur = None; } if forcelist.load(Ordering::Relaxed) != 0 { crate::ported::zle::zle_refresh::SHOWINGLIST.store(-2, Ordering::Relaxed);
} else {
crate::ported::zle::zle_h::invalidatelist(); }
} else if useline.load(Ordering::Relaxed) != 0 { if nm > 1 && dm != 0 { ret = {
let groups = amatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let all: Vec<String> = groups.into_iter()
.flat_map(|g| g.matches.into_iter().filter_map(|m| m.str))
.collect();
crate::ported::zle::compresult::do_ambiguous(&all)
};
if crate::ported::zle::zle_refresh::SHOWINGLIST.load(Ordering::Relaxed) == 0
&& uselist.load(Ordering::Relaxed) != 0
&& crate::ported::zle::zle_refresh::LISTSHOWN.load(Ordering::Relaxed) != 0
&& (crate::ported::zle::zle_tricky::USEMENU
.load(Ordering::Relaxed) == 2
|| oldlist.load(Ordering::Relaxed) != 0)
{
crate::ported::zle::zle_refresh::SHOWINGLIST.store(osl, Ordering::Relaxed); }
} else if nm == 1 || (nm > 1 && dm == 0) { do_single_first_match(); if forcelist.load(Ordering::Relaxed) != 0 { if uselist.load(Ordering::Relaxed) != 0 {
crate::ported::zle::zle_refresh::SHOWINGLIST.store(-2, Ordering::Relaxed);
} else {
crate::ported::zle::zle_refresh::CLEARLIST.store(1, Ordering::Relaxed);
}
} else {
crate::ported::zle::zle_h::invalidatelist(); }
} else if nmessages.load(Ordering::Relaxed) != 0
&& forcelist.load(Ordering::Relaxed) != 0
{ if uselist.load(Ordering::Relaxed) != 0 {
crate::ported::zle::zle_refresh::SHOWINGLIST.store(-2, Ordering::Relaxed);
} else {
crate::ported::zle::zle_refresh::CLEARLIST.store(1, Ordering::Relaxed);
}
}
} else { crate::ported::zle::zle_h::invalidatelist(); crate::ported::zle::zle_tricky::LASTAMBIG.store( opt_isset("BASHAUTOLIST"),
Ordering::Relaxed,
);
if forcelist.load(Ordering::Relaxed) != 0 { crate::ported::zle::zle_refresh::CLEARLIST.store(1, Ordering::Relaxed); } ZLEMETACS.store(0, Ordering::Relaxed); foredel(ZLEMETALL.load(Ordering::Relaxed)); inststr(&crate::ported::zle::zle_tricky::ORIGLINE.get_or_init(|| Mutex::new(String::new())).lock().map(|g| g.clone()).unwrap_or_default()); ZLEMETACS.store(crate::ported::zle::zle_tricky::ORIGCS.load(Ordering::Relaxed), Ordering::Relaxed); }
if crate::ported::zle::zle_refresh::SHOWINGLIST.load(Ordering::Relaxed) == 0
&& crate::ported::zle::zle_tricky::VALIDLIST.load(Ordering::Relaxed) != 0
&& crate::ported::zle::zle_tricky::USEMENU.load(Ordering::Relaxed) != 2
&& uselist.load(Ordering::Relaxed) != 0
&& (nm != 1 || dm != 0)
&& useline.load(Ordering::Relaxed) >= 0
&& useline.load(Ordering::Relaxed) != 2
&& (oldlist.load(Ordering::Relaxed) == 0 || crate::ported::zle::zle_refresh::LISTSHOWN.load(Ordering::Relaxed) == 0)
{
onlyexpl.store(3, Ordering::Relaxed); crate::ported::zle::zle_refresh::SHOWINGLIST.store(-2, Ordering::Relaxed); }
goto_compend(ret)
}
fn do_single_first_match() { let groups = amatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
let first = groups.into_iter().find(|g| g.mcount > 0)
.and_then(|g| g.matches.first().cloned());
if let Some(m) = first {
if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() { g.cur = None; } if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() { g.asked = 0; } if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() {
g.cur = Some(Box::new(m));
}
}
}
fn goto_compend(ret: i32) -> i32 { if let Ok(mut g) = matchers.get_or_init(|| Mutex::new(Vec::new())).lock() {
g.clear(); }
let line_len = ZLEMETALL.load(Ordering::Relaxed); if ZLEMETACS.load(Ordering::Relaxed) > line_len { ZLEMETACS.store(line_len, Ordering::Relaxed); }
ret }
fn foredel(ct: i32) { if ct <= 0 { return; }
let cs = ZLEMETACS.load(Ordering::Relaxed) as usize;
if let Ok(mut g) = ZLEMETALINE.get_or_init(|| Mutex::new(String::new())).lock() {
let bytes = g.as_bytes();
if cs >= bytes.len() { return; }
let end = (cs + ct as usize).min(bytes.len());
let new_line: String = String::from_utf8_lossy(&bytes[..cs]).into_owned()
+ &String::from_utf8_lossy(&bytes[end..]);
let new_len = new_line.len() as i32;
*g = new_line;
ZLEMETALL.store(new_len, Ordering::Relaxed);
}
}
fn inststr(s: &str) { if s.is_empty() { return; }
let cs = ZLEMETACS.load(Ordering::Relaxed) as usize;
if let Ok(mut g) = ZLEMETALINE.get_or_init(|| Mutex::new(String::new())).lock() {
let bytes = g.as_bytes();
let cs = cs.min(bytes.len());
let new_line: String = String::from_utf8_lossy(&bytes[..cs]).into_owned()
+ s
+ &String::from_utf8_lossy(&bytes[cs..]);
let new_len = new_line.len() as i32;
*g = new_line;
ZLEMETALL.store(new_len, Ordering::Relaxed);
ZLEMETACS.store(cs as i32 + s.len() as i32, Ordering::Relaxed);
}
}
fn unmetafy_line() { let meta = ZLEMETALINE.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let unmeta = crate::ported::zle::zle_tricky::unmetafy_line(&meta);
let new_len = unmeta.len() as i32;
let cs = ZLEMETACS.load(Ordering::Relaxed); if let Ok(mut g) = ZLELINE.get_or_init(|| Mutex::new(String::new())).lock() {
*g = unmeta;
}
ZLELL.store(new_len, Ordering::Relaxed);
ZLECS.store(cs.min(new_len), Ordering::Relaxed);
}
fn metafy_line() { let raw = ZLELINE.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let meta = crate::ported::zle::zle_tricky::metafy_line(&raw);
let new_len = meta.len() as i32;
let cs = ZLECS.load(Ordering::Relaxed);
if let Ok(mut g) = ZLEMETALINE.get_or_init(|| Mutex::new(String::new())).lock() {
*g = meta;
}
ZLEMETALL.store(new_len, Ordering::Relaxed);
ZLEMETACS.store(cs.min(new_len), Ordering::Relaxed);
}
fn selfinsert() -> i32 { let ch = LASTCHAR.load(Ordering::Relaxed); if ch < 0 { return 1; } let cs = ZLECS.load(Ordering::Relaxed) as usize;
if let Ok(mut g) = ZLELINE.get_or_init(|| Mutex::new(String::new())).lock() {
let mut bytes = g.as_bytes().to_vec();
let cs = cs.min(bytes.len());
if (ch as u32) < 128 {
bytes.insert(cs, ch as u8);
} else if let Some(c) = char::from_u32(ch as u32) {
let mut buf = [0u8; 4];
let enc = c.encode_utf8(&mut buf).as_bytes();
for (i, b) in enc.iter().enumerate() {
bytes.insert(cs + i, *b);
}
}
*g = String::from_utf8_lossy(&bytes).into_owned();
let new_len = g.len() as i32;
ZLELL.store(new_len, Ordering::Relaxed);
ZLECS.store((cs + 1) as i32, Ordering::Relaxed);
}
0 }
pub static LASTCHAR: AtomicI32 = AtomicI32::new(0);
pub static MINFO: OnceLock<Mutex<crate::ported::zle::comp_h::Menuinfo>> = OnceLock::new();
fn opt_isset(name: &str) -> i32 { if crate::ported::options::opt_state_get(name).unwrap_or(false) { 1 } else { 0 }
}
fn env_iparam(name: &str) -> i32 { crate::ported::params::getiparam(name) as i32
}
fn lastprebr_set(s: &str) { if let Ok(mut g) = crate::ported::zle::zle_tricky::LASTPREBR
.get_or_init(|| Mutex::new(String::new())).lock()
{
*g = s.to_string();
}
}
fn lastpostbr_set(s: &str) { if let Ok(mut g) = crate::ported::zle::zle_tricky::LASTPOSTBR
.get_or_init(|| Mutex::new(String::new())).lock()
{
*g = s.to_string();
}
}
pub fn callcompfunc(s: &str, fn_name: &str) {
if fn_name.is_empty() { return; } let _lv = crate::ported::builtin::LASTVAL.load(Ordering::Relaxed); let _icf = crate::ported::utils::INCOMPFUNC.load(Ordering::Relaxed); let _osc = crate::ported::builtin::SFCONTEXT.load(Ordering::Relaxed);
let _useglob = USEGLOB.load(Ordering::Relaxed);
let context = compcontext_for(s); set_compstate_str("context", &context);
set_compstate_str(
"last_prompt",
if dolastprompt.load(Ordering::Relaxed) != 0 { "yes" } else { "" },
);
let cl_value = crate::ported::zle::complete::COMPLIST
.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
set_compstate_str("list", &cl_value);
let ul = useline.load(Ordering::Relaxed);
let um = crate::ported::zle::zle_tricky::USEMENU.load(Ordering::Relaxed);
let ins = if ul != 0 {
match um {
0 => "unambiguous",
1 => "menu",
2 => "automenu",
_ => "",
}
} else { "" };
set_compstate_str("insert", ins);
set_compstate_str(
"exact",
if useexact.load(Ordering::Relaxed) != 0 { "accept" } else { "" },
);
set_compstate_str(
"to_end",
if movetoend.load(Ordering::Relaxed) == 1 { "single" } else { "match" },
);
crate::ported::utils::INCOMPFUNC.store(1, Ordering::Relaxed);
let _ = shfunc_call(fn_name);
let post_insert = crate::ported::params::getsparam("compstate[insert]")
.unwrap_or_default();
if !post_insert.is_empty() {
if post_insert.contains("automenu") {
crate::ported::zle::zle_tricky::USEMENU.store(2, Ordering::Relaxed);
} else if post_insert.contains("menu") {
crate::ported::zle::zle_tricky::USEMENU.store(1, Ordering::Relaxed);
}
}
crate::ported::utils::INCOMPFUNC.store(_icf, Ordering::Relaxed);
}
fn compcontext_for(_s: &str) -> String { let ip = ispar.load(Ordering::Relaxed); if ip == 2 { return "brace_parameter".into(); } if ip == 1 { return "parameter".into(); } let lw = linwhat.load(Ordering::Relaxed); match lw { x if x == IN_PAR_LW => "assign_parameter".into(), x if x == IN_MATH_LW => "math".into(), x if x == IN_COND_LW => "condition".into(), x if x == IN_ENV_LW => "value".into(), _ => "command".into(), }
}
pub const IN_NOTHING_LW: i32 = 0; pub const IN_CMD_LW: i32 = 1; pub const IN_COND_LW: i32 = 2; pub const IN_MATH_LW: i32 = 3; pub const IN_PAR_LW: i32 = 4; pub const IN_ENV_LW: i32 = 5;
fn shfunc_call(name: &str) -> i32 { if crate::ported::utils::getshfunc(name).is_none() { return 1; }
crate::ported::builtin::LASTVAL.load(Ordering::Relaxed) }
fn set_compstate_str(key: &str, val: &str) { let pname = format!("compstate[{}]", key);
let _ = crate::ported::params::setsparam(&pname, val);
}
pub fn check_param(s: &str, set: bool, test: bool) -> Option<usize> {
if let Ok(mut g) = parpre.get_or_init(|| Mutex::new(String::new())).lock() {
g.clear();
}
if !test { ispar.store(0, Ordering::Relaxed); parq.store(0, Ordering::Relaxed); eparq.store(0, Ordering::Relaxed); }
let bytes = s.as_bytes(); let offs_v = OFFS.load(Ordering::Relaxed) as usize;
let mut found = false; let mut qstring = false; let mut p: usize = offs_v.min(bytes.len().saturating_sub(1));
loop {
if p < bytes.len() {
let ch = char_at(bytes, p);
if ch == Stringg || ch == Qstring { let next = char_at(bytes, p + ch.len_utf8());
let snull_next = ch == Stringg && next == Snull; let qstr_quot = ch == Qstring && next == '\''; if p < offs_v && !snull_next && !qstr_quot {
found = true; qstring = ch == Qstring; break;
}
}
}
if p == 0 { break; } p = prev_char_index(bytes, p);
}
if found { while p > 0 {
let prev = prev_char_index(bytes, p);
let pc = char_at(bytes, prev);
if pc == Stringg || pc == Qstring { p = prev; } else { break; }
}
loop { let n1 = p + char_at(bytes, p).len_utf8();
if n1 >= bytes.len() { break; }
let c1 = char_at(bytes, n1);
let n2 = n1 + c1.len_utf8();
if n2 >= bytes.len() { break; }
let c2 = char_at(bytes, n2);
if (c1 == Stringg || c1 == Qstring)
&& (c2 == Stringg || c2 == Qstring)
{
p = n2;
} else {
break;
}
}
}
let next_char = if p + 1 <= bytes.len() {
let dollar_len = char_at(bytes, p).len_utf8();
char_at(bytes, p + dollar_len)
} else { '\0' };
if !(found && next_char != Inpar && next_char != Inbrack && next_char != Snull) {
return None; }
let dollar_len = char_at(bytes, p).len_utf8();
let mut b: usize = p + dollar_len; let mut br: i32 = 1; let mut nest: i32 = 0;
if char_at(bytes, b) == Inbrace { let close = skip_token_parens(bytes, b, Inbrace, Outbrace);
if let Some(end) = close {
if end <= s.len() && offs_v >= end - bytes.iter().take(end).count() {
return None; }
} else {
return None;
}
b += Inbrace.len_utf8(); br += 1;
let (open_p, close_p) = if qstring { ('(', ')') } else { (Inpar, Outpar) };
let after_flags = skip_token_parens(bytes, b, open_p, close_p);
if let Some(end) = after_flags {
if end > offs_v + 1 {
ispar.store(2, Ordering::Relaxed); return None; }
b = end;
}
let mut tb = p;
while tb > 0 {
let prev = prev_char_index(bytes, tb);
let pc = char_at(bytes, prev);
if pc == Outbrace || pc == Inbrace { tb = prev; break; }
tb = prev;
}
if tb > 0 {
let cc = char_at(bytes, tb);
let prev = prev_char_index(bytes, tb);
let pp = char_at(bytes, prev);
if cc == Inbrace && (pp == Stringg || cc == Qstring) {
nest = 1; }
}
}
while b < bytes.len() {
let c = char_at(bytes, b);
if c == '^' || c == Hat || c == '=' || c == Equals || c == '~' || c == Tilde {
b += c.len_utf8();
} else {
break;
}
}
if b < bytes.len() {
let c = char_at(bytes, b);
if c == '#' || c == Pound || c == '+' { b += c.len_utf8(); }
}
let mut e: usize = b; if br != 0 { let qopen = if test { Dnull } else { '"' };
while e < bytes.len() && char_at(bytes, e) == qopen { e += qopen.len_utf8();
parq.fetch_add(1, Ordering::Relaxed); }
if !test { b = e; } }
if e < bytes.len() {
let c = char_at(bytes, e);
let one_char_name = matches!(c,
ch if ch == Quest || ch == Star || ch == Stringg || ch == Qstring
|| ch == '?' || ch == '*' || ch == '$' || ch == '-' || ch == '!' || ch == '@');
if one_char_name { e += c.len_utf8();
} else if c.is_ascii_digit() { while e < bytes.len() && char_at(bytes, e).is_ascii_digit() { e += 1;
}
} else {
let walked = walk_namespace(&bytes[e..]);
if walked > 0 {
e += walked;
} else if c == '.' { e += 1;
}
}
}
if offs_v <= e && offs_v >= b {
if br != 0 {
let qopen = if test { Dnull } else { '"' };
let mut pq = e;
while pq < bytes.len() && char_at(bytes, pq) == qopen {
pq += qopen.len_utf8();
parq.fetch_sub(1, Ordering::Relaxed);
eparq.fetch_add(1, Ordering::Relaxed);
}
}
if test { return Some(b); }
if set { if br >= 2 { mflags.fetch_or(CMF_PARBR, Ordering::Relaxed); if nest != 0 { mflags.fetch_or(CMF_PARNEST, Ordering::Relaxed); }
}
let mut tail = String::from_utf8_lossy(&bytes[e..]).into_owned();
tail = strip_tokens(&tail); if let Ok(mut g) = isuf.get_or_init(|| Mutex::new(String::new())).lock() {
*g = tail;
}
let head = String::from_utf8_lossy(&bytes[..b]).into_owned();
if let Ok(mut g) = ripre.get_or_init(|| Mutex::new(String::new())).lock() {
*g = format!("{}{}", *g, head);
}
if let Ok(mut g) = ipre.get_or_init(|| Mutex::new(String::new())).lock() {
*g = strip_tokens(&format!("{}{}", *g, head));
}
}
let cf_active = compfunc
.get_or_init(|| Mutex::new(None))
.lock()
.ok()
.and_then(|g| g.clone())
.map(|s| !s.is_empty())
.unwrap_or(false);
if cf_active {
let pf = if br >= 2 {
CMF_PARBR | (if nest != 0 { CMF_PARNEST } else { 0 })
} else {
0
};
parflags.store(pf, Ordering::Relaxed); let head = String::from_utf8_lossy(&bytes[..b]).into_owned();
if let Ok(mut g) = parpre.get_or_init(|| Mutex::new(String::new())).lock() {
*g = strip_tokens(&head); }
}
let off_delta = b as i32;
OFFS.fetch_sub(off_delta, Ordering::Relaxed); let new_offs = OFFS.load(Ordering::Relaxed);
let zlc = ZLEMETACS.load(Ordering::Relaxed);
WB.store(zlc - new_offs, Ordering::Relaxed); WE.store(WB.load(Ordering::Relaxed) + (e - b) as i32, Ordering::Relaxed); ispar.store(if br >= 2 { 2 } else { 1 }, Ordering::Relaxed); return Some(b); } else if offs_v > e && e < bytes.len() && char_at(bytes, e) == ':' { let offsptr = offs_v;
let mut e2 = e;
while e2 < offsptr && e2 < bytes.len() {
let c = char_at(bytes, e2);
if c != ':' && !c.is_alphanumeric() { break; }
e2 += c.len_utf8();
}
ispar.store(if br >= 2 { 2 } else { 1 }, Ordering::Relaxed); return None; }
let _ = (Bnull,); None }
#[inline]
fn prev_char_index(bytes: &[u8], pos: usize) -> usize { if pos == 0 { return 0; }
let mut i = pos - 1;
while i > 0 && (bytes[i] & 0xC0) == 0x80 { i -= 1; }
i
}
#[inline]
fn char_at(bytes: &[u8], pos: usize) -> char { if pos >= bytes.len() { return '\0'; }
let s = match std::str::from_utf8(&bytes[pos..]) { Ok(s) => s, Err(_) => return '\0' };
s.chars().next().unwrap_or('\0')
}
fn skip_token_parens(bytes: &[u8], start: usize, open: char, close: char) -> Option<usize>
{
let mut depth: i32 = 0;
let mut i = start;
while i < bytes.len() {
let c = char_at(bytes, i);
if c == open { depth += 1; }
else if c == close {
depth -= 1;
if depth == 0 { return Some(i + c.len_utf8()); }
}
i += c.len_utf8();
}
if depth == 0 { Some(i) } else { None }
}
fn walk_namespace(bytes: &[u8]) -> usize { let s = match std::str::from_utf8(bytes) { Ok(s) => s, Err(_) => return 0 };
let mut len = 0usize;
for c in s.chars() {
if c.is_alphanumeric() || c == '_' { len += c.len_utf8(); }
else { break; }
}
len
}
fn strip_tokens(s: &str) -> String { crate::lex::untokenize(s).to_string()
}
pub static OFFS: AtomicI32 = AtomicI32::new(0);
pub static freecl: OnceLock<Mutex<Option<i32>>> = OnceLock::new();
fn compfunc_active() -> bool {
compfunc.get_or_init(|| Mutex::new(None))
.lock().ok()
.and_then(|g| g.clone())
.map(|s| !s.is_empty())
.unwrap_or(false)
}
pub fn set_comp_sep() -> i32 { let (_s, _lip, _lp) = comp_str(false); let owe = WE.load(Ordering::Relaxed); let owb = WB.load(Ordering::Relaxed);
let _ooffs = OFFS.load(Ordering::Relaxed);
let lex_saved = lexsave();
lexrestore(lex_saved);
WB.store(owb, Ordering::Relaxed);
WE.store(owe, Ordering::Relaxed);
1 }
fn lexsave() -> usize { crate::ported::context::zcontext_save();
(LEXSAVE_DEPTH.fetch_add(1, Ordering::SeqCst) + 1) as usize
}
fn lexrestore(_token: usize) { let parts = crate::ported::zsh_h::ZCONTEXT_HIST
| crate::ported::zsh_h::ZCONTEXT_LEX
| crate::ported::zsh_h::ZCONTEXT_PARSE;
crate::ported::context::zcontext_restore_partial(parts);
LEXSAVE_DEPTH.fetch_sub(1, Ordering::SeqCst);
}
static LEXSAVE_DEPTH: AtomicI32 = AtomicI32::new(0);
pub fn addmatches(dat: &mut crate::ported::zle::comp_h::Cadata, argv: &[String]) -> i32
{
let _nm = mnum.load(Ordering::Relaxed);
if dat.dummies >= 0 { dat.aflags = (dat.aflags | CAF_NOSORT | CAF_UNIQCON) & !CAF_UNIQALL; }
let gflags = (if (dat.aflags & CAF_NOSORT) != 0 { CGF_NOSORT } else { 0 })
| (if (dat.aflags & CAF_MATSORT) != 0 { CGF_MATSORT } else { 0 })
| (if (dat.aflags & CAF_NUMSORT) != 0 { CGF_NUMSORT } else { 0 })
| (if (dat.aflags & CAF_REVSORT) != 0 { CGF_REVSORT } else { 0 })
| (if (dat.aflags & CAF_UNIQALL) != 0 { CGF_UNIQALL } else { 0 })
| (if (dat.aflags & CAF_UNIQCON) != 0 { CGF_UNIQCON } else { 0 });
if let Some(g) = dat.group.as_deref() { endcmgroup(None); begcmgroup(Some(g), gflags); } else {
endcmgroup(None); begcmgroup(Some("default"), 0); }
if dat.mesg.is_some() || dat.exp.is_some() { let mut e = Cexpl::default(); e.always = if dat.mesg.is_some() { 1 } else { 0 }; e.count = 0; e.fcount = 0; e.str = Some(dat.mesg.clone() .or_else(|| dat.exp.clone())
.unwrap_or_default());
if let Ok(mut g) = curexpl.get_or_init(|| Mutex::new(None)).lock() {
*g = Some(e);
}
if dat.mesg.is_some()
&& dat.dpar.is_empty()
&& dat.opar.is_none()
&& dat.apar.is_none()
{ addexpl(true); }
} else if let Ok(mut g) = curexpl.get_or_init(|| Mutex::new(None)).lock() {
*g = None; }
if argv.is_empty()
&& dat.dummies == 0
&& (dat.aflags & CAF_ALL) == 0
{
return 1; }
let _quote_mode = (dat.aflags & CAF_QUOTE) != 0;
if (dat.flags & 0x0008) != 0 { dat.flags |= parflags.load(Ordering::Relaxed); }
let qc = compquote_first(); if let Some(q) = qc { match q {
'`' => { instring_set(0); inbackt_set(0); autoq_set(""); } '\'' => instring_set(crate::ported::zsh_h::QT_SINGLE), '"' => instring_set(crate::ported::zsh_h::QT_DOUBLE), '$' => instring_set(crate::ported::zsh_h::QT_DOLLARS), _ => {}
}
} else {
instring_set(0); inbackt_set(0); autoq_set(""); }
let exact_str = crate::ported::params::getsparam("compexact").unwrap_or_default();
useexact.store(if exact_str == "accept" { 1 } else { 0 }, Ordering::Relaxed);
let mut added = 0i32;
for word in argv { addmatch(word, dat.flags, None, false); added += 1;
}
let _ = added;
0 }
fn compquote_first() -> Option<char> { crate::ported::zle::zle_tricky::COMPQUOTE
.get_or_init(|| Mutex::new(String::new()))
.lock().ok()
.and_then(|g| g.chars().next())
}
fn instring_set(v: i32) { crate::ported::zle::zle_tricky::INSTRING.store(v, Ordering::Relaxed);
}
fn inbackt_set(v: i32) { crate::ported::zle::zle_tricky::INBACKT.store(v, Ordering::Relaxed);
}
fn autoq_set(s: &str) { if let Ok(mut g) = crate::ported::zle::zle_tricky::AUTOQ
.get_or_init(|| Mutex::new(String::new())).lock()
{
*g = s.to_string();
}
}
#[allow(clippy::too_many_arguments)]
pub fn add_match_data( alt: i32,
str: &str,
orig: &str,
_line: Option<&str>, ipre_: &str,
ripre_: &str,
isuf_: &str,
pre: &str,
prpre: &str,
ppre: &str,
_pline: Option<&str>, psuf: &str,
_sline: Option<&str>, suf: &str,
flags: i32,
exact: i32,
) -> Cmatch {
let _ai_ref = if alt != 0 { &fainfo } else { &ainfo }; cline_matched_compcore(_line);
if _pline.is_some() { cline_matched_compcore(_pline); }
if _sline.is_some() { cline_matched_compcore(_sline); }
let psl = psuf.len();
let isl = isuf_.len();
let qisuf_v = qisuf_get(); let qisl = qisuf_v.len();
let _salen = (if _sline.is_none() { psl } else { 0 }) + isl + qisl;
let ipl = ipre_.len();
let _ppl = ppre.len();
let _pl = pre.len();
let qipl_v = qipre_get(); let _qipl = qipl_v.len();
let _stl = str.len();
let _lpl = ripre_.len();
let _lsl = suf.len();
let _ml = ipl;
let mut cm = Cmatch::default(); cm.str = Some(str.to_string()); cm.orig = Some(orig.to_string()); cm.ipre = if ipre_.is_empty() { None } else { Some(ipre_.into()) };
cm.ripre = if ripre_.is_empty() { None } else { Some(ripre_.into()) };
cm.isuf = if isuf_.is_empty() { None } else { Some(isuf_.into()) };
cm.ppre = if ppre.is_empty() { None } else { Some(ppre.into()) };
cm.psuf = if psuf.is_empty() { None } else { Some(psuf.into()) };
cm.prpre = if prpre.is_empty() { None } else { Some(prpre.into()) };
cm.pre = if pre.is_empty() { None } else { Some(pre.into()) };
cm.suf = if suf.is_empty() { None } else { Some(suf.into()) };
cm.flags = flags;
if exact != 0 { if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
if let Some(a) = g.as_mut() {
a.exact = 1; a.exactm = Some(Box::new(cm.clone())); }
}
}
let cell = matches.get_or_init(|| Mutex::new(Vec::new()));
if let Ok(mut g) = cell.lock() { g.push(cm.clone()); } mnum.fetch_add(1, Ordering::Relaxed);
cm }
fn cline_matched_compcore(line: Option<&str>) { let Some(s) = line else { return; };
if s.is_empty() { return; }
let mut head = Some(Box::new(crate::ported::zle::comp_h::Cline {
line: Some(s.to_string()),
llen: s.len() as i32,
..Default::default()
}));
crate::ported::zle::compmatch::cline_matched(&mut head);
}
fn qisuf_get() -> String { crate::ported::params::getsparam("qisuf").unwrap_or_default()
}
fn qipre_get() -> String { crate::ported::params::getsparam("qipre").unwrap_or_default()
}
pub fn makecomplist(s: &str, incmd: i32, lst: i32) -> i32 { let owb = WB.load(Ordering::Relaxed); let owe = WE.load(Ordering::Relaxed);
let ooffs = OFFS.load(Ordering::Relaxed);
let mut s_owned = s.to_string();
if compfunc_active() {
if let Some(p) = check_param(&s_owned, false, false) { s_owned = s_owned[p..].to_string(); PARWB.store(owb, Ordering::Relaxed); PARWE.store(owe, Ordering::Relaxed); PAROFFS.store(ooffs, Ordering::Relaxed); } else {
PARWB.store(-1, Ordering::Relaxed); }
} else {
PARWB.store(-1, Ordering::Relaxed); }
linwhat.store(INWHAT.load(Ordering::Relaxed), Ordering::Relaxed);
if compfunc_active() { let os = s_owned.clone(); let onm = nmatches.load(Ordering::Relaxed); let odm = diffmatches.load(Ordering::Relaxed); let osi = movefd(0);
if let Ok(mut g) = bmatchers.get_or_init(|| Mutex::new(None)).lock() {
*g = None;
}
if let Ok(mut g) = mstack.get_or_init(|| Mutex::new(None)).lock() {
*g = None;
}
if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
*g = Some(Aminfo::default());
}
if let Ok(mut g) = fainfo.get_or_init(|| Mutex::new(None)).lock() {
*g = Some(Aminfo::default());
}
if let Ok(mut g) = freecl.get_or_init(|| Mutex::new(None)).lock() {
*g = None; }
if crate::ported::zle::zle_tricky::VALIDLIST.load(Ordering::Relaxed) == 0 {
crate::ported::zle::zle_tricky::LASTAMBIG.store(0, Ordering::Relaxed); }
if let Ok(mut g) = amatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
g.clear(); }
mnum.store(0, Ordering::Relaxed); unambig_mnum.store(-1, Ordering::Relaxed); if let Ok(mut g) = isuf.get_or_init(|| Mutex::new(String::new())).lock() {
g.clear(); }
insmnum.store(ZMULT.load(Ordering::Relaxed), Ordering::Relaxed); oldlist.store(0, Ordering::Relaxed); oldins.store(0, Ordering::Relaxed); begcmgroup(Some("default"), 0); crate::ported::zle::zle_tricky::MENUCMP.store(0, Ordering::Relaxed); menuacc.store(0, Ordering::Relaxed); newmatches.store(0, Ordering::Relaxed); onlyexpl.store(0, Ordering::Relaxed);
let dup_s = crate::ported::mem::dupstring(&os); let cf_name = compfunc.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.clone()).unwrap_or_default();
callcompfunc(&dup_s, &cf_name); endcmgroup(None);
runhookdef_compcore("COMPCTLCLEANUPHOOK");
if oldlist.load(Ordering::Relaxed) != 0 { nmatches.store(onm, Ordering::Relaxed); diffmatches.store(odm, Ordering::Relaxed); crate::ported::zle::zle_tricky::VALIDLIST.store(1, Ordering::Relaxed); if let Ok(mut g) = amatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
if let Ok(last) = lastmatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
*g = last.clone(); }
}
if let Ok(mut g) = lmatches.get_or_init(|| Mutex::new(None)).lock() {
let last_l = lastlmatches.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
*g = last_l; }
if let Ok(mut g) = pmatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
g.clear(); }
hasperm.store(0, Ordering::Relaxed); redup(osi); return 0; }
if !lastmatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().map(|g| g.is_empty()).unwrap_or(true)
{ if let Ok(mut g) = lastmatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
g.clear(); }
}
permmatches(1); let p_snap = pmatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
if let Ok(mut g) = amatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
*g = p_snap.clone(); }
lastpermmnum.store(permmnum.load(Ordering::Relaxed), Ordering::Relaxed); lastpermgnum.store(permgnum.load(Ordering::Relaxed), Ordering::Relaxed); if let Ok(mut g) = lastmatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
*g = p_snap; }
let lm_snap = lmatches.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
if let Ok(mut g) = lastlmatches.get_or_init(|| Mutex::new(None)).lock() {
*g = lm_snap; }
if let Ok(mut g) = pmatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
g.clear(); }
hasperm.store(0, Ordering::Relaxed); hasoldlist.store(1, Ordering::Relaxed);
let any_nm = nmatches.load(Ordering::Relaxed) != 0
|| nmessages.load(Ordering::Relaxed) != 0;
let errset = errflag_get();
if any_nm && !errset { crate::ported::zle::zle_tricky::VALIDLIST.store(1, Ordering::Relaxed); redup(osi); return 0; }
redup(osi); return 1; } else { let mut dat = crate::ported::zle::comp_h::Ccmakedat {
str: Some(s_owned.clone()), incmd, lst, };
runhookdef_compctlmake(&mut dat); runhookdef_compcore("COMPCTLCLEANUPHOOK"); return dat.lst; }
}
pub static bmatchers: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cmlist>>>>
= OnceLock::new();
pub static mstack: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cmlist>>>>
= OnceLock::new();
fn movefd(fd: i32) -> i32 { crate::ported::utils::movefd(fd)
}
fn redup(new: i32) { crate::ported::utils::redup(new, -1);
}
fn errflag_get() -> bool {
crate::ported::utils::errflag.load(Ordering::Relaxed) != 0 }
fn runhookdef_compcore(hook: &str) { let fns: Vec<String> = crate::ported::module::HOOKTAB.lock()
.ok()
.and_then(|g| g.get(hook).cloned())
.unwrap_or_default();
for f in fns {
let _ = shfunc_call(&f);
}
}
fn runhookdef_compctlmake( dat: &mut crate::ported::zle::comp_h::Ccmakedat,
) {
let s = dat.str.clone().unwrap_or_default();
let _ = crate::ported::zle::compctl::makecomplistctl(dat.lst);
let _ = s;
}
pub static HOOK_FNS: OnceLock<Mutex<std::collections::HashMap<String, Vec<String>>>>
= OnceLock::new();
pub fn makearray(mut rp: Vec<Cmatch>, flags: i32) -> (Vec<Cmatch>, i32, i32, i32) { let mut n: i32 = rp.len() as i32; let mut nl: i32 = 0; let mut ll: i32 = 0;
if n > 0 { if (flags & CGF_NOSORT) == 0 { MATCHORDER.store(flags, Ordering::Relaxed); rp.sort_by(matchcmp);
if (flags & CGF_UNIQCON) == 0 { let mut cp = 0usize; let mut ap = 0usize;
while ap < rp.len() { if ap != cp { rp.swap(ap, cp); } cp += 1;
let mut bp = ap;
while bp + 1 < rp.len() && matcheq(&rp[ap], &rp[bp + 1]) {
bp += 1; n -= 1; }
let mut dup = 0i32; while bp + 1 < rp.len()
&& rp[ap].disp.is_none()
&& rp[bp + 1].disp.is_none() && rp[ap].str == rp[bp + 1].str
{
rp[bp + 1].flags |= CMF_MULT; dup = 1; bp += 1;
}
if dup != 0 { rp[ap].flags |= CMF_FMULT; }
ap = bp + 1; }
rp.truncate(cp); }
for m in rp.iter() { if m.disp.is_some() && (m.flags & CMF_DISPLINE) != 0 { ll += 1;
}
if (m.flags & (CMF_NOLIST | CMF_MULT)) != 0 { nl += 1;
}
}
} else { if (flags & CGF_UNIQALL) == 0 && (flags & CGF_UNIQCON) == 0 { MATCHORDER.store(flags, Ordering::Relaxed); let mut sp: Vec<Cmatch> = rp.clone(); sp.sort_by(matchcmp);
let mut del = false; for w in sp.windows(2) { if matcheq(&w[0], &w[1]) {
for m in rp.iter_mut() {
if matcheq(m, &w[1]) {
m.flags = CMF_DELETE; del = true; break;
}
}
} else if w[0].disp.is_none() {
if w[1].disp.is_none() && w[0].str == w[1].str { for m in rp.iter_mut() {
if matcheq(m, &w[1]) {
m.flags |= CMF_MULT; break;
}
}
for m in rp.iter_mut() {
if matcheq(m, &w[0]) {
m.flags |= CMF_FMULT; break;
}
}
}
}
}
if del { rp.retain(|m| (m.flags & CMF_DELETE) == 0); n = rp.len() as i32;
}
} else if (flags & CGF_UNIQCON) == 0 { let mut cp = 0usize;
let mut ap = 0usize;
while ap < rp.len() { if ap != cp { rp.swap(ap, cp); }
cp += 1;
let mut bp = ap;
while bp + 1 < rp.len() && matcheq(&rp[ap], &rp[bp + 1]) {
bp += 1; n -= 1; }
let mut dup = 0i32;
while bp + 1 < rp.len()
&& rp[ap].disp.is_none()
&& rp[bp + 1].disp.is_none()
&& rp[ap].str == rp[bp + 1].str
{
rp[bp + 1].flags |= CMF_MULT; dup = 1; bp += 1;
}
if dup != 0 {
rp[ap].flags |= CMF_FMULT; }
ap = bp + 1;
}
rp.truncate(cp); }
for m in rp.iter() { if m.disp.is_some() && (m.flags & CMF_DISPLINE) != 0 { ll += 1;
}
if (m.flags & (CMF_NOLIST | CMF_MULT)) != 0 { nl += 1;
}
}
}
}
(rp, n, nl, ll) }
pub fn makearray_strings(mut rp: Vec<String>, flags: i32) -> (Vec<String>, i32) { let mut n: i32 = rp.len() as i32;
if flags != 0 && n > 0 { let numeric = isset(NUMERICGLOBSORT); let mut sf = SORTIT_IGNORING_BACKSLASHES as u32;
if numeric {
sf |= SORTIT_NUMERICALLY as u32;
}
crate::ported::sort::strmetasort(&mut rp, sf, None);
let mut cp = 0usize;
let mut ap = 0usize;
while ap < rp.len() {
if ap != cp { rp.swap(ap, cp); }
cp += 1;
let mut bp = ap;
while bp + 1 < rp.len() && rp[ap] == rp[bp + 1] { bp += 1; n -= 1;
}
ap = bp + 1; }
rp.truncate(cp); }
(rp, n)
}
pub fn dupmatch(m: &Cmatch, nbeg: i32, nend: i32) -> Cmatch { let mut r = Cmatch::default(); r.str = m.str.clone(); r.orig = m.orig.clone(); r.ipre = m.ipre.clone(); r.ripre = m.ripre.clone(); r.isuf = m.isuf.clone(); r.ppre = m.ppre.clone(); r.psuf = m.psuf.clone(); r.prpre = m.prpre.clone(); r.pre = m.pre.clone(); r.suf = m.suf.clone(); r.flags = m.flags; if !m.brpl.is_empty() { let take = (nbeg as usize).min(m.brpl.len()); r.brpl = m.brpl[..take].to_vec(); } else {
r.brpl = Vec::new(); }
if !m.brsl.is_empty() { let take = (nend as usize).min(m.brsl.len()); r.brsl = m.brsl[..take].to_vec(); } else {
r.brsl = Vec::new(); }
r.rems = m.rems.clone(); r.remf = m.remf.clone(); r.autoq = m.autoq.clone(); r.qipl = m.qipl; r.qisl = m.qisl; r.disp = m.disp.clone(); r.mode = m.mode; r.modec = m.modec; r.fmode = m.fmode; r.fmodec = m.fmodec; r }
static PERMMATCHES_FI: AtomicI32 = AtomicI32::new(0);
pub fn permmatches(last: i32) -> i32 { let ofi = PERMMATCHES_FI.load(Ordering::Relaxed);
let pmatches_set = pmatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().map(|g| !g.is_empty()).unwrap_or(false);
if pmatches_set && newmatches.load(Ordering::Relaxed) == 0 { if last != 0 && PERMMATCHES_FI.load(Ordering::Relaxed) != 0 { let famref = fainfo.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
if let Ok(mut a) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
*a = famref;
}
}
return PERMMATCHES_FI.load(Ordering::Relaxed); }
newmatches.store(0, Ordering::Relaxed); PERMMATCHES_FI.store(0, Ordering::Relaxed);
{
if let Ok(mut g) = pmatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
g.clear();
}
if let Ok(mut g) = lmatches.get_or_init(|| Mutex::new(None)).lock() {
*g = None;
}
}
nmatches.store(0, Ordering::Relaxed); smatches.store(0, Ordering::Relaxed); diffmatches.store(0, Ordering::Relaxed);
let ainfo_count = ainfo.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.as_ref().map(|a| a.count)).unwrap_or(0);
if ainfo_count == 0 { if last != 0 { let famref = fainfo.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
if let Ok(mut a) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
*a = famref;
}
}
PERMMATCHES_FI.store(1, Ordering::Relaxed); }
let nbeg = crate::ported::zle::zle_tricky::NBRBEG.load(Ordering::Relaxed);
let nend = crate::ported::zle::zle_tricky::NBREND.load(Ordering::Relaxed);
let mut gn: i32 = 1; let mut mn: i32 = 1; let fi = PERMMATCHES_FI.load(Ordering::Relaxed);
let groups_snapshot: Vec<Cmgroup> = {
amatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().ok().map(|g| g.clone()).unwrap_or_default()
};
let mut new_pmatches: Vec<Cmgroup> = Vec::with_capacity(groups_snapshot.len());
for g_orig in groups_snapshot.into_iter() { let mut g = g_orig; let must_rebuild = fi != ofi || g.perm.is_none() || g.new_ != 0; if must_rebuild { let src_list = if fi != 0 { g.lfmatches.clone() } else { g.lmatches.clone() };
let (arr, nn, nl, ll) = makearray(src_list, g.flags); g.mcount = nn; g.lcount = nn - nl; if g.lcount < 0 { g.lcount = 0; } g.llcount = ll; if !g.ylist.is_empty() { g.lcount = g.ylist.len() as i32; smatches.store(2, Ordering::Relaxed); }
let mut exps = g.lexpls.clone(); g.ecount = exps.len() as i32;
g.ccount = 0; nmatches.fetch_add(g.mcount, Ordering::Relaxed); smatches.fetch_add(g.lcount, Ordering::Relaxed); if g.mcount > 1 { diffmatches.store(1, Ordering::Relaxed); }
let mut n_grp = Cmgroup::default();
g.perm = None;
n_grp.num = gn; gn += 1; n_grp.flags = g.flags; n_grp.mcount = g.mcount; n_grp.matches = arr.iter() .map(|m| dupmatch(m, nbeg, nend))
.collect();
n_grp.name = g.name.clone(); n_grp.lcount = g.lcount; n_grp.llcount = g.llcount; if !g.ylist.is_empty() { n_grp.ylist = g.ylist.clone(); } else {
n_grp.ylist = Vec::new(); }
if g.ecount != 0 { n_grp.expls = exps.drain(..).map(|o| Cexpl { count: if fi != 0 { o.fcount } else { o.count }, always: o.always, fcount: 0, str: o.str.clone(), }).collect();
n_grp.ecount = g.ecount;
} else {
n_grp.expls = Vec::new(); }
n_grp.widths = Vec::new(); g.matches = arr; g.perm = Some(Box::new(n_grp.clone())); new_pmatches.push(n_grp); } else {
nmatches.fetch_add(g.mcount, Ordering::Relaxed); smatches.fetch_add(g.lcount, Ordering::Relaxed); if g.mcount > 1 {
diffmatches.store(1, Ordering::Relaxed); }
g.num = gn; gn += 1; if let Some(p) = g.perm.as_deref() {
new_pmatches.push(p.clone()); }
}
g.new_ = 0; }
let mut first_first: Option<Cmatch> = None;
for g_pm in new_pmatches.iter_mut() {
g_pm.nbrbeg = nbeg; g_pm.nbrend = nend; let mut rn = 1i32; for m in g_pm.matches.iter_mut() {
m.rnum = rn; rn += 1; m.gnum = mn; mn += 1; }
if diffmatches.load(Ordering::Relaxed) == 0 && !g_pm.matches.is_empty() {
match first_first.as_ref() { Some(p0) => {
if !matcheq(&g_pm.matches[0], p0) {
diffmatches.store(1, Ordering::Relaxed); }
}
None => first_first = Some(g_pm.matches[0].clone()), }
}
}
if let Ok(mut g) = pmatches.get_or_init(|| Mutex::new(Vec::new())).lock() {
*g = new_pmatches;
}
hasperm.store(1, Ordering::Relaxed); permmnum.store(mn - 1, Ordering::Relaxed); permgnum.store(gn - 1, Ordering::Relaxed); if let Ok(mut ld) = listdat.get_or_init(|| Mutex::new(Default::default())).lock() {
ld.valid = 0; }
fi }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rembslash_basic() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(rembslash("hello\\ world"), "hello world");
assert_eq!(rembslash("no\\\\slash"), "no\\slash");
assert_eq!(rembslash("plain"), "plain");
}
#[test]
fn comp_quoting_string_table() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(comp_quoting_string(QT_SINGLE), "'");
assert_eq!(comp_quoting_string(QT_DOUBLE), "\"");
assert_eq!(comp_quoting_string(QT_DOLLARS), "$'");
assert_eq!(comp_quoting_string(0), "\\");
assert_eq!(comp_quoting_string(QT_BACKSLASH), "\\");
}
#[test]
fn matcheq_equal_strings() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut a = Cmatch::default(); a.str = Some("foo".into());
let mut b = Cmatch::default(); b.str = Some("foo".into());
assert!(matcheq(&a, &b));
}
#[test]
fn matcheq_different_strings() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut a = Cmatch::default(); a.str = Some("foo".into());
let mut b = Cmatch::default(); b.str = Some("bar".into());
assert!(!matcheq(&a, &b));
}
#[test]
fn matcheq_one_side_none() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut a = Cmatch::default(); a.pre = Some("p".into());
let b = Cmatch::default();
assert!(!matcheq(&a, &b));
}
#[test]
fn get_user_var_reads_array_from_paramtab() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::params::setaparam(
"__test_arr",
vec!["a".into(), "bb".into(), "ccc".into()],
);
let got = get_user_var(Some("__test_arr"));
assert_eq!(got, Some(vec!["a".into(), "bb".into(), "ccc".into()]));
crate::ported::params::setaparam("__test_arr", vec![]);
}
#[test]
fn get_user_var_reads_scalar_as_single_element_array() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::params::setsparam("__test_scalar", "hello");
let got = get_user_var(Some("__test_scalar"));
assert_eq!(got, Some(vec!["hello".to_string()]));
crate::ported::params::setsparam("__test_scalar", "");
}
#[test]
fn get_user_var_paren_list_splits_on_separators() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let got = get_user_var(Some("(one two three)"));
assert_eq!(got, Some(vec!["one".into(), "two".into(), "three".into()]));
}
#[test]
fn get_user_var_none_for_missing() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let got = get_user_var(Some("__definitely_not_a_param_xyz"));
assert_eq!(got, None);
}
#[test]
fn get_data_arr_reads_hashed_keys_or_values() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::params::sethparam(
"__test_hash",
vec!["k1".into(), "v1".into(), "k2".into(), "v2".into()],
);
let keys = get_data_arr("__test_hash", true);
assert!(keys.is_some(), "hashed param should have keys");
let mut keys = keys.unwrap();
keys.sort();
assert_eq!(keys, vec!["k1".to_string(), "k2".to_string()]);
let vals = get_data_arr("__test_hash", false);
assert!(vals.is_some(), "hashed param should have values");
let mut vals = vals.unwrap();
vals.sort();
assert_eq!(vals, vec!["v1".to_string(), "v2".to_string()]);
}
#[test]
fn get_data_arr_none_for_non_hashed() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::params::setsparam("__test_scalar2", "value");
let got = get_data_arr("__test_scalar2", false);
assert_eq!(got, None,
"scalar params must NOT come out of get_data_arr");
}
#[test]
fn before_complete_snapshots_oldmenucmp() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
MENUCMP.store(7, Ordering::Relaxed);
OLDMENUCMP.store(0, Ordering::Relaxed);
let mut lst = 0;
let _ = before_complete(&mut lst);
assert_eq!(OLDMENUCMP.load(Ordering::Relaxed), 7);
MENUCMP.store(0, Ordering::Relaxed);
OLDMENUCMP.store(0, Ordering::Relaxed);
}
#[test]
fn before_complete_clears_showagain() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
crate::ported::zle::zle_tricky::SHOWAGAIN.store(5, Ordering::Relaxed);
let mut lst = 0;
let _ = before_complete(&mut lst);
assert_eq!(
crate::ported::zle::zle_tricky::SHOWAGAIN.load(Ordering::Relaxed),
0,
"SHOWAGAIN must be cleared by before_complete"
);
}
#[test]
fn remsquote_default_quoting() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut s = String::from("a'\\''b");
let n = remsquote(&mut s);
assert_eq!(s, "a'b");
assert_eq!(n, 3);
}
#[test]
fn ctokenize_dollar_substitution() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let out = ctokenize("$x{y}");
let chars: Vec<char> = out.chars().collect();
assert_eq!(chars[0], Stringg);
assert_eq!(chars[1], 'x');
assert_eq!(chars[2], Inbrace);
assert_eq!(chars[3], 'y');
assert_eq!(chars[4], Outbrace);
}
#[test]
fn get_user_var_inline_list() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let result = get_user_var(Some("(a b c)")).unwrap();
assert_eq!(result, vec!["a", "b", "c"]);
}
#[test]
fn matchcmp_str_sort_default() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
MATCHORDER.store(CGF_MATSORT, Ordering::Relaxed);
let mut a = Cmatch::default(); a.str = Some("apple".into());
let mut b = Cmatch::default(); b.str = Some("banana".into());
assert_eq!(matchcmp(&a, &b), std::cmp::Ordering::Less);
assert_eq!(matchcmp(&b, &a), std::cmp::Ordering::Greater);
assert_eq!(matchcmp(&a, &a), std::cmp::Ordering::Equal);
MATCHORDER.store(0, Ordering::Relaxed);
}
#[test]
fn dupmatch_clones_strings_and_truncates_braces() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut src = Cmatch::default();
src.str = Some("foo".into());
src.ipre = Some("ipre".into());
src.flags = 7;
src.brpl = vec![10, 20, 30, 40];
src.brsl = vec![5, 6, 7];
src.qipl = 1;
src.qisl = 2;
src.mode = 0o755;
src.modec = 'd';
let r = dupmatch(&src, 2, 1);
assert_eq!(r.str.as_deref(), Some("foo"));
assert_eq!(r.ipre.as_deref(), Some("ipre"));
assert_eq!(r.flags, 7);
assert_eq!(r.brpl, vec![10, 20]); assert_eq!(r.brsl, vec![5]); assert_eq!(r.qipl, 1);
assert_eq!(r.qisl, 2);
assert_eq!(r.mode, 0o755);
assert_eq!(r.modec, 'd');
}
#[test]
fn dupmatch_empty_braces_stay_empty() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let src = Cmatch::default();
let r = dupmatch(&src, 5, 5);
assert!(r.brpl.is_empty());
assert!(r.brsl.is_empty());
}
#[test]
fn makearray_sorted_and_deduped() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut a = Cmatch::default(); a.str = Some("z".into());
let mut b = Cmatch::default(); b.str = Some("a".into());
let mut c = Cmatch::default(); c.str = Some("a".into());
let (arr, n, _nl, _ll) = makearray(vec![a, b, c], CGF_MATSORT);
assert_eq!(arr.len(), 2);
assert_eq!(n, 2);
assert_eq!(arr[0].str.as_deref(), Some("a"));
assert_eq!(arr[1].str.as_deref(), Some("z"));
}
#[test]
fn makearray_nosort_unchanged_order() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut a = Cmatch::default(); a.str = Some("z".into());
let mut b = Cmatch::default(); b.str = Some("a".into());
let (arr, n, _, _) = makearray(vec![a, b], CGF_NOSORT | CGF_UNIQALL);
assert_eq!(n, 2);
assert_eq!(arr[0].str.as_deref(), Some("z"));
assert_eq!(arr[1].str.as_deref(), Some("a"));
}
#[test]
fn makearray_strings_dedup_consecutive() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let (arr, n) = makearray_strings(
vec!["b".into(), "a".into(), "a".into(), "c".into()],
1,
);
assert_eq!(n, 3);
assert_eq!(arr, vec!["a", "b", "c"]);
}
#[test]
fn check_param_no_dollar_returns_none() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
OFFS.store(2, Ordering::Relaxed);
assert_eq!(check_param("abc", false, false), None);
}
#[test]
fn check_param_simple_dollar_var_at_cursor() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
OFFS.store(2, Ordering::Relaxed);
let s = format!("{}FOO", crate::ported::zsh_h::Stringg);
let r = check_param(&s, false, true);
assert!(r.is_some(), "expected Some(b) inside $FOO");
}
#[test]
fn callcompfunc_empty_fn_no_panic() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
callcompfunc("anything", "");
}
#[test]
fn callcompfunc_sets_compstate_context() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
ispar.store(0, Ordering::Relaxed);
linwhat.store(IN_PAR_LW, Ordering::Relaxed);
assert_eq!(compcontext_for("foo"), "assign_parameter");
callcompfunc("foo", "_test_fn");
}
static GLOBAL_MUT_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
fn compcontext_for_routes_ispar_first() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
ispar.store(2, Ordering::Relaxed);
linwhat.store(IN_NOTHING_LW, Ordering::Relaxed);
assert_eq!(compcontext_for("x"), "brace_parameter");
ispar.store(1, Ordering::Relaxed);
assert_eq!(compcontext_for("x"), "parameter");
ispar.store(0, Ordering::Relaxed);
linwhat.store(IN_MATH_LW, Ordering::Relaxed);
assert_eq!(compcontext_for("x"), "math");
linwhat.store(IN_COND_LW, Ordering::Relaxed);
assert_eq!(compcontext_for("x"), "condition");
linwhat.store(IN_ENV_LW, Ordering::Relaxed);
assert_eq!(compcontext_for("x"), "value");
linwhat.store(IN_NOTHING_LW, Ordering::Relaxed);
assert_eq!(compcontext_for("x"), "command");
}
#[test]
fn addmatches_empty_argv_early_return() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut dat = crate::ported::zle::comp_h::Cadata::default();
dat.dummies = 0;
dat.aflags = 0;
assert_eq!(addmatches(&mut dat, &[]), 1);
}
#[test]
fn addmatches_appends_argv_to_default_group() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
amatches.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap().clear();
matches.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap().clear();
let mut dat = crate::ported::zle::comp_h::Cadata::default();
dat.dummies = -1;
let _ = addmatches(&mut dat, &["a".into(), "b".into()]);
let n = matches.get().unwrap().lock().unwrap().len();
assert!(n >= 2);
}
#[test]
fn add_match_data_returns_populated_cmatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
matches.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap().clear();
let before = mnum.load(Ordering::Relaxed);
let cm = add_match_data(
0, "match", "match-orig", None,
"ipre", "ripre", "isuf",
"pre", "prpre", "ppre", None,
"psuf", None,
"suf", 0, 0,
);
assert_eq!(cm.str.as_deref(), Some("match"));
assert_eq!(cm.orig.as_deref(), Some("match-orig"));
assert_eq!(cm.pre.as_deref(), Some("pre"));
assert_eq!(cm.suf.as_deref(), Some("suf"));
assert_eq!(mnum.load(Ordering::Relaxed), before + 1);
}
#[test]
fn add_match_data_exact_records_into_ainfo() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
*g = Some(Aminfo::default());
}
let _ = add_match_data(
0, "x", "x", None, "", "", "", "", "", "", None, "", None, "", 0, 1,
);
let a = ainfo.get().unwrap().lock().unwrap().clone().unwrap();
assert_eq!(a.exact, 1);
assert!(a.exactm.is_some());
}
#[test]
fn set_comp_sep_returns_one() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(set_comp_sep(), 1);
}
#[test]
fn foredel_deletes_forward_from_zlemetacs() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
if let Ok(mut g) = ZLEMETALINE.get_or_init(|| Mutex::new(String::new())).lock() {
*g = "abcdef".to_string();
}
ZLEMETACS.store(2, Ordering::Relaxed);
ZLEMETALL.store(6, Ordering::Relaxed);
foredel(3);
let line = ZLEMETALINE.get().unwrap().lock().unwrap().clone();
assert_eq!(line, "abf");
assert_eq!(ZLEMETALL.load(Ordering::Relaxed), 3);
}
#[test]
fn inststr_inserts_at_zlemetacs() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
if let Ok(mut g) = ZLEMETALINE.get_or_init(|| Mutex::new(String::new())).lock() {
*g = "hello".to_string();
}
ZLEMETACS.store(5, Ordering::Relaxed);
ZLEMETALL.store(5, Ordering::Relaxed);
inststr(" world");
let line = ZLEMETALINE.get().unwrap().lock().unwrap().clone();
assert_eq!(line, "hello world");
assert_eq!(ZLEMETACS.load(Ordering::Relaxed), 11);
}
#[test]
fn metafy_and_unmetafy_roundtrip_globals() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
if let Ok(mut g) = ZLELINE.get_or_init(|| Mutex::new(String::new())).lock() {
*g = "plain ascii".to_string();
}
ZLECS.store(3, Ordering::Relaxed);
ZLELL.store(11, Ordering::Relaxed);
metafy_line();
assert_eq!(
ZLEMETALINE.get().unwrap().lock().unwrap().clone(),
"plain ascii"
);
assert_eq!(ZLEMETACS.load(Ordering::Relaxed), 3);
unmetafy_line();
assert_eq!(
ZLELINE.get().unwrap().lock().unwrap().clone(),
"plain ascii"
);
}
#[test]
fn selfinsert_appends_lastchar_at_zlecs() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
if let Ok(mut g) = ZLELINE.get_or_init(|| Mutex::new(String::new())).lock() {
*g = "ab".to_string();
}
ZLECS.store(2, Ordering::Relaxed);
ZLELL.store(2, Ordering::Relaxed);
LASTCHAR.store(b'c' as i32, Ordering::Relaxed);
let rv = selfinsert();
assert_eq!(rv, 0);
assert_eq!(ZLELINE.get().unwrap().lock().unwrap().clone(), "abc");
assert_eq!(ZLECS.load(Ordering::Relaxed), 3);
}
#[test]
fn minfo_clear_and_asked_zero_mutate_state() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() {
let mut cm = Cmatch::default();
cm.str = Some("x".into());
g.cur = Some(Box::new(cm));
g.asked = 1;
}
if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() { g.cur = None; }
if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() { g.asked = 0; }
let m = MINFO.get().unwrap().lock().unwrap().clone();
assert!(m.cur.is_none());
assert_eq!(m.asked, 0);
}
#[test]
fn cline_matched_stub_marks_node() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
cline_matched_compcore(Some("foo"));
cline_matched_compcore(None);
cline_matched_compcore(Some(""));
}
#[test]
fn permmatches_returns_fi_zero_when_count_present() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _g = GLOBAL_MUT_LOCK.lock().unwrap();
amatches.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap().clear();
pmatches.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap().clear();
if let Ok(mut a) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
*a = Some(Aminfo { count: 5, ..Default::default() });
}
newmatches.store(1, Ordering::Relaxed);
let fi = permmatches(0);
assert_eq!(fi, 0);
assert_eq!(hasperm.load(Ordering::Relaxed), 1);
}
}