#![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_MATCH, CAF_MATSORT, CAF_NOSORT, CAF_NUMSORT, CAF_QUOTE, CAF_REVSORT, CAF_UNIQALL, CAF_UNIQCON};
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)
}
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 (fromcomp.load(Ordering::Relaxed) & crate::ported::zle::comp_h::FC_INWORD) != 0 {
let le = lastend.load(Ordering::Relaxed);
let ll = ZLEMETALL.load(Ordering::Relaxed);
let new_cs = if le > ll { ll } else { le };
ZLEMETACS.store(new_cs, Ordering::Relaxed);
}
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 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);
}
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; }
let drained = pmatches.get_or_init(|| Mutex::new(Vec::new()))
.lock().map(|mut g| std::mem::take(&mut *g))
.unwrap_or_default();
freematches(drained, 1); 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 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 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 }
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 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 }
#[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 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 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);
if let Some(ref m) = dat.match_ { if let Ok(mut mst) = mstack
.get_or_init(|| std::sync::Mutex::new(None))
.lock()
{
let new_link = Box::new(crate::ported::zle::comp_h::Cmlist {
next: mst.take(),
matcher: m.clone(),
str: String::new(),
});
*mst = Some(new_link);
}
crate::ported::zle::compmatch::add_bmatchers(Some(m));
if let Ok(mut g) = matchers
.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock()
{
g.push(m.clone());
}
}
let (aign, pign) = if let Some(ign_name) = dat.ign.as_deref() { let aign_raw = get_user_var(Some(ign_name)).unwrap_or_default();
let mut literal_suffixes: Vec<String> = Vec::new();
let mut pat_progs: Vec<crate::ported::pattern::Patprog> = Vec::new();
for entry in aign_raw {
let bytes = entry.as_bytes();
let star_prefix = bytes.len() >= 3
&& ((bytes[0] == b'?' && bytes[1] == b'*')
|| (bytes[0] == b'*' && bytes[1] == b'?'))
&& !crate::ported::pattern::haswilds(
std::str::from_utf8(&bytes[2..]).unwrap_or(""));
if star_prefix {
literal_suffixes.push(
std::str::from_utf8(&bytes[2..]).unwrap_or("").to_string());
} else if let Some(prog) = crate::ported::pattern::patcompile(
&entry, 0, None::<&mut String>)
{
pat_progs.push(prog);
}
}
(literal_suffixes, pat_progs)
} else {
(Vec::new(), Vec::new())
};
let disp_arr: Vec<String> = if let Some(ref d) = dat.disp {
get_user_var(Some(d.as_str())).unwrap_or_default()
} else {
Vec::new()
};
let compiprefix_s = crate::ported::zle::complete::COMPIPREFIX
.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let compisuffix_s = crate::ported::zle::complete::COMPISUFFIX
.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let compprefix_s = crate::ported::zle::complete::COMPPREFIX
.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let compsuffix_s = crate::ported::zle::complete::COMPSUFFIX
.get_or_init(|| Mutex::new(String::new()))
.lock().map(|g| g.clone()).unwrap_or_default();
let lipre = compiprefix_s.clone();
let lisuf = compisuffix_s.clone();
let lpre = compprefix_s.clone();
let lsuf = compsuffix_s.clone();
if let Some(ref existing) = dat.ipre.clone() {
dat.ipre = Some(if !lipre.is_empty() {
format!("{}{}", lipre, existing)
} else {
existing.clone()
});
} else if !lipre.is_empty() {
dat.ipre = Some(lipre.clone());
}
if let Some(ref existing) = dat.isuf.clone() {
dat.isuf = Some(if !lisuf.is_empty() {
format!("{}{}", lisuf, existing)
} else {
existing.clone()
});
} else if !lisuf.is_empty() {
dat.isuf = Some(lisuf.clone());
}
let quote_flag = if (dat.aflags & CAF_QUOTE) != 0 { 1 } else { 0 };
if let Some(ref existing) = dat.ppre.clone() {
let quoted = if (dat.flags & 0x0001) != 0 {
tildequote(existing, quote_flag)
} else {
multiquote(existing, quote_flag)
};
dat.ppre = Some(quoted);
}
if let Some(ref existing) = dat.psuf.clone() {
dat.psuf = Some(multiquote(existing, quote_flag));
}
let ppl = dat.ppre.as_deref().map(|s| s.len()).unwrap_or(0);
let psl = dat.psuf.as_deref().map(|s| s.len()).unwrap_or(0);
let doadd = dat.apar.is_none() && dat.opar.is_none() && dat.dpar.is_empty();
let mut apar_list: Vec<String> = Vec::new();
let mut opar_list: Vec<String> = Vec::new();
let mut added = 0i32;
let mut disp_idx = 0usize;
let mut compignored_local = 0i32;
'cand: for word in argv { let cur_disp = if !disp_arr.is_empty() && disp_idx < disp_arr.len() {
let d = disp_arr[disp_idx].clone();
disp_idx += 1;
Some(d)
} else { None };
if !aign.is_empty() || !pign.is_empty() {
let full = format!("{}{}{}",
dat.ppre.as_deref().unwrap_or(""),
word,
dat.psuf.as_deref().unwrap_or(""));
for suf in &aign {
if full.len() >= suf.len() && full.ends_with(suf.as_str()) {
compignored_local += 1;
continue 'cand;
}
}
for prog in &pign {
if crate::ported::pattern::pattry(prog, &full) {
compignored_local += 1;
continue 'cand;
}
}
}
let ms: String;
let _lc;
let isexact;
if (dat.aflags & CAF_MATCH) == 0 {
ms = if (dat.aflags & CAF_QUOTE) != 0 {
word.clone()
} else {
multiquote(word, 0)
};
let sl = ms.len() as i32;
_lc = crate::ported::zle::compmatch::bld_parts(
&ms, sl, -1, None, None);
isexact = 0;
} else { let qu = if (dat.aflags & CAF_QUOTE) != 0 { 0 }
else if dat.ppre.is_some()
|| (dat.flags & 0x0001) == 0 { 1 }
else { 2 };
let mut lc_out: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
let mut isexact_out = 0i32;
match crate::ported::zle::compmatch::comp_match(
&lpre, &lsuf, word, None,
Some(&mut lc_out), qu,
None, 0, None, 0,
&mut isexact_out,
) {
Some(matched) => {
ms = matched;
_lc = lc_out;
isexact = isexact_out;
}
None => {
continue 'cand; }
}
}
if doadd { let cm = add_match_data(
0,
&ms,
word,
_lc.clone(), dat.ipre.as_deref().unwrap_or(""),
"", dat.isuf.as_deref().unwrap_or(""),
dat.pre.as_deref().unwrap_or(""),
dat.prpre.as_deref().unwrap_or(""),
dat.ppre.as_deref().unwrap_or(""),
None, dat.psuf.as_deref().unwrap_or(""),
None, dat.suf.as_deref().unwrap_or(""),
dat.flags,
isexact,
);
let _ = cur_disp;
let _ = cm;
added += 1;
} else { if dat.apar.is_some() { apar_list.push(ms.clone());
}
if dat.opar.is_some() { opar_list.push(word.clone());
}
}
}
if let Some(ref name) = dat.apar {
crate::ported::params::setaparam(name, apar_list);
}
if let Some(ref name) = dat.opar {
crate::ported::params::setaparam(name, opar_list);
}
if dat.exp.is_some() { addexpl(false);
}
let hasall = hasallmatch.load(Ordering::Relaxed);
if hasall == 0 && (dat.aflags & CAF_ALL) != 0 {
addmatch("<all>", dat.flags | crate::ported::zle::comp_h::CMF_ALL,
None, true);
hasallmatch.store(1, Ordering::Relaxed);
}
while dat.dummies > 0 {
addmatch("", dat.flags | crate::ported::zle::comp_h::CMF_DUMMY,
None, false);
dat.dummies -= 1;
}
let _ = (ppl, psl, compignored_local, added);
0 }
#[allow(clippy::too_many_arguments)]
pub fn add_match_data( alt: i32,
str: &str,
orig: &str,
mut line: Option<Box<crate::ported::zle::comp_h::Cline>>,
ipre_: &str,
ripre_: &str,
isuf_: &str,
pre: &str,
prpre: &str,
ppre: &str,
mut pline: Option<Box<crate::ported::zle::comp_h::Cline>>,
psuf: &str,
mut sline: Option<Box<crate::ported::zle::comp_h::Cline>>,
suf: &str,
flags: i32,
exact: i32,
) -> Cmatch {
let _ai_ref = if alt != 0 { &fainfo } else { &ainfo }; crate::ported::zle::compmatch::cline_matched(&mut line);
if pline.is_some() {
crate::ported::zle::compmatch::cline_matched(&mut pline);
}
if sline.is_some() {
crate::ported::zle::compmatch::cline_matched(&mut 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 qipre_v = qipre_get(); let qipl_v = qipre_v.clone();
let _qipl = qipl_v.len();
let _stl = str.len();
let _lpl = ripre_.len();
let _lsl = suf.len();
let _ml = ipl;
let psl_local = if sline.is_none() && !psuf.is_empty() { psuf.len() as i32 } else { 0 };
let isl_local = isuf_.len() as i32;
let qisl_local = qisuf_v.len() as i32;
let salen = psl_local + isl_local + qisl_local;
if salen > 0 && line.is_some() {
unsafe {
let mut tail: *mut Option<Box<crate::ported::zle::comp_h::Cline>> =
&mut line;
while let Some(ref n) = *tail {
if n.next.is_none() { break; }
tail = &mut (*tail).as_mut().unwrap().next;
}
if psl_local > 0 {
let s = crate::ported::zle::compmatch::bld_parts(
psuf, psl_local, psl_local, None, None);
if let Some(node) = (*tail).as_mut() {
node.next = s;
while let Some(ref nn) = node.next {
if nn.next.is_none() { break; }
break;
}
}
while let Some(ref n) = *tail {
if n.next.is_none() { break; }
tail = &mut (*tail).as_mut().unwrap().next;
}
}
if isl_local > 0 {
let s = crate::ported::zle::compmatch::bld_parts(
isuf_, isl_local, isl_local, None, None);
if let Some(node) = (*tail).as_mut() {
node.next = s;
}
while let Some(ref n) = *tail {
if n.next.is_none() { break; }
tail = &mut (*tail).as_mut().unwrap().next;
}
}
if qisl_local > 0 {
let mut s = crate::ported::zle::compmatch::bld_parts(
&qisuf_v, qisl_local, qisl_local, None, None);
if let Some(qsl) = s.as_mut() {
qsl.flags |= crate::ported::zle::comp_h::CLF_SUF;
qsl.suffix = qsl.prefix.take();
}
if let Some(node) = (*tail).as_mut() {
node.next = s;
}
}
}
}
let qipl_local = qipre_v.len() as i32;
let ipl_local = ipre_.len() as i32;
let pl_local = pre.len() as i32;
let ppl_local = if pline.is_none() && !ppre.is_empty() { ppre.len() as i32 } else { 0 };
if pl_local > 0 {
if ppl_local > 0 {
let p = crate::ported::zle::compmatch::bld_parts(
ppre, ppl_local, ppl_local, None, None);
if p.is_some() {
let mut p_chain = p;
let mut tail: *mut Option<Box<crate::ported::zle::comp_h::Cline>> =
&mut p_chain;
unsafe {
while let Some(ref n) = *tail {
if n.next.is_none() { break; }
tail = &mut (*tail).as_mut().unwrap().next;
}
if let Some(t) = (*tail).as_mut() {
t.next = line.take();
}
}
line = p_chain;
}
}
let p = crate::ported::zle::compmatch::bld_parts(
pre, pl_local, pl_local, None, None);
if let Some(mut head) = p {
let mut t: *mut Option<Box<crate::ported::zle::comp_h::Cline>> = &mut head.next;
unsafe {
while (*t).is_some() {
if (*t).as_deref().unwrap().next.is_none() { break; }
t = &mut (*t).as_mut().unwrap().next;
}
*t = line.take();
}
line = Some(head);
}
if ipl_local > 0 {
let p = crate::ported::zle::compmatch::bld_parts(
ipre_, ipl_local, ipl_local, None, None);
if let Some(mut head) = p {
let mut t: *mut Option<Box<crate::ported::zle::comp_h::Cline>> = &mut head.next;
unsafe {
while (*t).is_some() {
if (*t).as_deref().unwrap().next.is_none() { break; }
t = &mut (*t).as_mut().unwrap().next;
}
*t = line.take();
}
line = Some(head);
}
}
if qipl_local > 0 {
let p = crate::ported::zle::compmatch::bld_parts(
&qipre_v, qipl_local, qipl_local, None, None);
if let Some(mut head) = p {
let mut t: *mut Option<Box<crate::ported::zle::comp_h::Cline>> = &mut head.next;
unsafe {
while (*t).is_some() {
if (*t).as_deref().unwrap().next.is_none() { break; }
t = &mut (*t).as_mut().unwrap().next;
}
*t = line.take();
}
line = Some(head);
}
}
} else if qipl_local + ipl_local + pl_local + ppl_local > 0 || pline.is_some() {
let apre = format!("{}{}{}{}",
qipre_v.as_str(),
ipre_,
pre,
if pline.is_none() { ppre } else { "" });
let apre_len = apre.len() as i32;
if apre_len > 0 {
let p = crate::ported::zle::compmatch::bld_parts(
&apre, apre_len, apre_len, None, None);
if let Some(mut head) = p {
let mut t: *mut Option<Box<crate::ported::zle::comp_h::Cline>> = &mut head.next;
unsafe {
while (*t).is_some() {
if (*t).as_deref().unwrap().next.is_none() { break; }
t = &mut (*t).as_mut().unwrap().next;
}
*t = line.take();
}
line = Some(head);
}
}
}
let stl = str.len();
let mut cm = Cmatch::default(); cm.str = Some(str.to_string()); cm.orig = Some(orig.to_string()); 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 (flags & crate::ported::zle::comp_h::CMF_FILE) != 0
&& !prpre.is_empty()
{ Some(prpre.into()) } else { None };
cm.ipre = if !qipre_v.is_empty() {
if !ipre_.is_empty() { Some(format!("{}{}", qipre_v, ipre_)) }
else { Some(qipre_v.clone()) }
} else if !ipre_.is_empty() { Some(ipre_.into()) } else { None };
cm.ripre = if ripre_.is_empty() { None } else { Some(ripre_.into()) };
cm.isuf = if !qisuf_v.is_empty() {
if !isuf_.is_empty() { Some(format!("{}{}", isuf_, qisuf_v)) }
else { Some(qisuf_v.clone()) }
} else if !isuf_.is_empty() { Some(isuf_.into()) } else { None };
cm.pre = if pre.is_empty() { None } else { Some(pre.into()) }; cm.suf = if suf.is_empty() { None } else { Some(suf.into()) };
let complist_s = crate::ported::zle::complete::COMPLIST.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
let extra_flags = (if complist_s.contains("packed") {
crate::ported::zle::comp_h::CMF_PACKED
} else { 0 })
| (if complist_s.contains("rows") {
crate::ported::zle::comp_h::CMF_ROWS
} else { 0 });
cm.flags = flags | extra_flags;
cm.mode = 0; cm.fmode = 0;
cm.modec = '\0'; cm.fmodec = '\0';
use crate::ported::zle::comp_h::CMF_FILE;
if (flags & CMF_FILE) != 0 && !orig.is_empty() && !orig.ends_with('/') {
let pb = format!("{}{}",
cm.prpre.as_deref().unwrap_or("./"),
orig);
if let Some(meta) = crate::ported::zle::compresult::ztat(&pb, false) {
use std::os::unix::fs::MetadataExt;
cm.mode = meta.mode();
}
if let Some(meta) = crate::ported::zle::compresult::ztat(&pb, true) {
use std::os::unix::fs::MetadataExt;
cm.fmode = meta.mode();
}
}
cm.brpl = BRBEG.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.as_ref().map(|head| {
let mut out: Vec<i32> = Vec::new();
let mut cur = Some(head.as_ref());
while let Some(n) = cur {
out.push(n.qpos);
cur = n.next.as_deref();
}
out
})).unwrap_or_default();
cm.brsl = BREND.get_or_init(|| Mutex::new(None))
.lock().ok().and_then(|g| g.as_ref().map(|head| {
let mut out: Vec<i32> = Vec::new();
let mut cur = Some(head.as_ref());
while let Some(n) = cur {
out.push(n.qpos);
cur = n.next.as_deref();
}
out
})).unwrap_or_default();
cm.qipl = qipre_v.len() as i32; cm.qisl = qisuf_v.len() as i32; let autoq_v = AUTOQ.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
cm.autoq = if !autoq_v.is_empty() {
Some(autoq_v)
} else if crate::ported::zle::zle_tricky::INBACKT.load(Ordering::Relaxed) != 0 {
Some("`".into())
} else { None };
cm.rems = None; cm.remf = None; cm.disp = None;
if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
if let Some(a) = g.as_mut() {
let old_line = a.line.take();
a.line = crate::ported::zle::compmatch::join_clines(
old_line, line);
}
}
mnum.fetch_add(1, Ordering::Relaxed);
if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
if let Some(a) = g.as_mut() {
a.count += 1;
}
}
newmatches.store(1, Ordering::Relaxed);
if alt != 0 {
crate::ported::zle::complete::COMPIGNORED
.fetch_add(1, Ordering::Relaxed);
}
let complastprompt_v = crate::ported::zle::complete::COMPLASTPREFIX.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
if complastprompt_v.is_empty() {
dolastprompt.store(0, Ordering::Relaxed);
}
if let Ok(mut g) = curexpl.get_or_init(|| Mutex::new(None)).lock() {
if let Some(e) = g.as_mut() {
if alt != 0 { e.fcount += 1; } else { e.count += 1; }
}
}
if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
if let Some(a) = g.as_mut() {
if a.firstm.is_none() {
a.firstm = Some(Box::new(cm.clone()));
}
}
}
let lpl = cm.ppre.as_deref().map(|s| s.len()).unwrap_or(0);
let lsl = cm.psuf.as_deref().map(|s| s.len()).unwrap_or(0);
let ml = (stl + lpl + lsl) as i32;
let cur_min = minmlen.load(Ordering::Relaxed);
let cur_max = maxmlen.load(Ordering::Relaxed);
if ml < cur_min { minmlen.store(ml, Ordering::Relaxed); }
if ml > cur_max { maxmlen.store(ml, Ordering::Relaxed); }
if exact != 0 { if let Ok(mut g) = ainfo.get_or_init(|| Mutex::new(None)).lock() {
if let Some(a) = g.as_mut() {
if a.exact == 0 { a.exact = useexact.load(Ordering::Relaxed);
a.exactm = Some(Box::new(cm.clone())); } else if useexact.load(Ordering::Relaxed) != 0 { a.exact = 2;
a.exactm = None;
}
}
}
}
let cell = if alt != 0 {
fmatches.get_or_init(|| Mutex::new(Vec::new()))
} else {
matches.get_or_init(|| Mutex::new(Vec::new()))
};
if let Ok(mut g) = cell.lock() { g.push(cm.clone()); }
cm }
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>>) { if let Ok(mut g) = mgroup.get_or_init(|| Mutex::new(None)).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 }
}
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 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 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 }
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 }
pub fn freematch(_m: Cmatch) { }
pub fn freematches(g: Vec<Cmgroup>, cm: i32) { drop(g);
if cm != 0 { if let Ok(mut g) = MINFO.get_or_init(|| Mutex::new(
crate::ported::zle::comp_h::Menuinfo::default()
)).lock() {
g.cur = None; }
}
}
pub static LASTEND: AtomicI32 = AtomicI32::new(0);
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();
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<Box<crate::ported::zle::comp_h::Cmatcher>>>> =
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 BRBEG: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Brinfo>>>>
= OnceLock::new();
pub static BREND: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Brinfo>>>>
= OnceLock::new();
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 static LASTCHAR: AtomicI32 = AtomicI32::new(0);
pub static MINFO: OnceLock<Mutex<crate::ported::zle::comp_h::Menuinfo>> = OnceLock::new();
#[inline]
fn matchstreq(a: Option<&String>, b: Option<&String>) -> bool { match (a, b) {
(None, None) => true,
(Some(x), Some(y)) => x == y,
_ => false,
}
}
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);
}
}
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 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 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();
}
}
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 static OFFS: AtomicI32 = AtomicI32::new(0);
pub static freecl: OnceLock<Mutex<Option<i32>>> = OnceLock::new();
pub 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);
}
#[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')
}
static LEXSAVE_DEPTH: AtomicI32 = AtomicI32::new(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()
}
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)
}
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);
}
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();
}
}
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 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()
}
fn movefd(fd: i32) -> i32 { crate::ported::utils::movefd(fd)
}
fn redup(new: i32) { crate::ported::utils::redup(new, -1);
}
pub static HOOK_FNS: OnceLock<Mutex<std::collections::HashMap<String, Vec<String>>>>
= OnceLock::new();
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;
}
static PERMMATCHES_FI: AtomicI32 = AtomicI32::new(0);
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)
}
#[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();
let saved_useexact = useexact.load(Ordering::Relaxed);
useexact.store(1, Ordering::Relaxed);
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();
useexact.store(saved_useexact, Ordering::Relaxed);
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();
{
let mut g = crate::ported::zle::zle_main::ZLELINE.lock().unwrap();
*g = "ab".chars().collect();
}
crate::ported::zle::zle_main::ZLECS.store(2, Ordering::Relaxed);
crate::ported::zle::zle_main::ZLELL.store(2, Ordering::Relaxed);
LASTCHAR.store(b'c' as i32, Ordering::Relaxed);
crate::ported::zle::zle_main::LASTCHAR_WIDE_VALID
.store(0, Ordering::Relaxed);
let rv = selfinsert();
assert_eq!(rv, 0);
let buf: String = crate::ported::zle::zle_main::ZLELINE
.lock().unwrap().iter().collect();
assert_eq!(buf, "abc");
assert_eq!(
crate::ported::zle::zle_main::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);
}
#[test]
fn rembslash_unescapes_canonical_pairs() {
assert_eq!(rembslash(r"\a"), "a");
assert_eq!(rembslash(r"\\"), r"\");
assert_eq!(rembslash(r"\$foo"), "$foo");
assert_eq!(rembslash("plain"), "plain");
}
#[test]
fn rembslash_empty_input_returns_empty() {
assert_eq!(rembslash(""), "");
}
#[test]
fn rembslash_trailing_lone_backslash_drops_silently() {
assert_eq!(rembslash(r"foo\"), "foo");
}
#[test]
fn ctokenize_passes_alphanumerics_through() {
assert_eq!(ctokenize("foo123"), "foo123");
assert_eq!(ctokenize(""), "");
assert_eq!(ctokenize("path/to"), "path/to");
}
#[test]
fn comp_quoting_string_dispatches_known_styles() {
for stype in 0..=8 {
let _ = comp_quoting_string(stype);
}
}
#[test]
fn multiquote_empty_stack_returns_input_unchanged() {
if let Some(c) = crate::ported::zle::complete::COMPQSTACK.get() {
if let Ok(mut g) = c.lock() { g.clear(); }
}
assert_eq!(multiquote("hello", 0), "hello");
assert_eq!(multiquote("", 0), "");
}
#[test]
fn tildequote_non_tilde_input_unchanged() {
if let Some(c) = crate::ported::zle::complete::COMPQSTACK.get() {
if let Ok(mut g) = c.lock() { g.clear(); }
}
assert_eq!(tildequote("foo/bar", 0), "foo/bar");
}
#[test]
fn tildequote_empty_input_empty_output() {
if let Some(c) = crate::ported::zle::complete::COMPQSTACK.get() {
if let Ok(mut g) = c.lock() { g.clear(); }
}
assert_eq!(tildequote("", 0), "");
}
}