#[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::*;
pub fn cpatterns_same( mut a: Option<&crate::ported::zle::comp_h::Cpattern>,
mut b: Option<&crate::ported::zle::comp_h::Cpattern>,
) -> bool { use crate::ported::zle::comp_h::{CPAT_CCLASS, CPAT_CHAR, CPAT_EQUIV, CPAT_NCLASS};
while let Some(ap) = a { let bp = match b { None => return false, Some(p) => p,
};
if ap.tp != bp.tp { return false; }
match ap.tp { x if x == CPAT_CCLASS || x == CPAT_NCLASS || x == CPAT_EQUIV => { if ap.str != bp.str { return false; }
}
x if x == CPAT_CHAR => { if ap.chr != bp.chr { return false; }
}
_ => { }
}
a = ap.next.as_deref(); b = bp.next.as_deref(); }
b.is_none() }
pub fn cmatchers_same( a: &crate::ported::zle::comp_h::Cmatcher,
b: &crate::ported::zle::comp_h::Cmatcher,
) -> bool { use crate::ported::zle::comp_h::{CMF_LEFT, CMF_RIGHT};
if std::ptr::eq(a, b) {
return true;
}
if a.flags != b.flags || a.llen != b.llen || a.wlen != b.wlen {
return false;
}
if a.llen != 0 && !cpatterns_same(a.line.as_deref(), b.line.as_deref()) {
return false;
}
if a.wlen > 0 && !cpatterns_same(a.word.as_deref(), b.word.as_deref()) {
return false;
}
if (a.flags & (CMF_LEFT | CMF_RIGHT)) != 0 {
if a.lalen != b.lalen || a.ralen != b.ralen { return false;
}
if a.lalen != 0 && !cpatterns_same(a.left.as_deref(), b.left.as_deref()) {
return false; }
if a.ralen != 0 && !cpatterns_same(a.right.as_deref(), b.right.as_deref()) {
return false; }
}
true
}
pub fn add_bmatchers(m: Option<&crate::ported::zle::comp_h::Cmatcher>) { use crate::ported::zle::comp_h::{Cmlist, CMF_RIGHT};
let cell = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None));
let old = cell.lock().ok().and_then(|mut g| g.take()); let mut head = old;
for mat in std::iter::successors(m, |p| p.next.as_deref())
.collect::<Vec<_>>().into_iter().rev() {
let qual = (mat.flags == 0 && mat.wlen > 0 && mat.llen > 0) || (mat.flags == CMF_RIGHT && mat.wlen < 0 && mat.llen == 0);
if qual { head = Some(Box::new(Cmlist {
next: head, matcher: Box::new(mat.clone()), str: String::new(),
}));
}
}
if let Ok(mut g) = cell.lock() { *g = head; }
}
pub fn update_bmatchers() { let bm_cell = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None));
let ms_cell = crate::ported::zle::compcore::mstack
.get_or_init(|| std::sync::Mutex::new(None));
let mut p = bm_cell.lock().ok().and_then(|mut g| g.take()); let ms_head = ms_cell.lock().ok().and_then(|g| g.as_ref().map(|b| (**b).clone()));
let mut new_bmatchers: Option<Box<crate::ported::zle::comp_h::Cmlist>> = p.as_ref().map(|b| (**b).clone()).map(Box::new);
while let Some(node) = p { let mut t = false; let mut ms = ms_head.as_ref(); while let Some(mscur) = ms {
if t { break; }
let mut mp = Some(mscur.matcher.as_ref()); while let Some(mpcur) = mp {
if t { break; }
t = cmatchers_same(mpcur, &*node.matcher); mp = mpcur.next.as_deref();
}
ms = mscur.next.as_deref();
}
p = node.next; if !t { new_bmatchers = p.as_ref().map(|b| (**b).clone()).map(Box::new); }
}
if let Ok(mut g) = bm_cell.lock() { *g = new_bmatchers; }
}
pub fn get_cline(l: Option<String>, ll: i32, w: Option<String>, wl: i32, o: Option<String>, ol: i32, fl: i32)
-> Box<crate::ported::zle::comp_h::Cline>
{
use crate::ported::zle::comp_h::Cline;
Box::new(Cline {
next: None, line: l, llen: ll,
word: w, wlen: wl,
orig: o, olen: ol,
slen: 0, flags: fl, prefix: None, suffix: None,
min: 0, max: 0,
})
}
pub fn free_cline(l: Option<Box<crate::ported::zle::comp_h::Cline>>) { drop(l);
}
pub fn cp_cline( l: Option<&crate::ported::zle::comp_h::Cline>,
deep: i32,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> { let mut r: Option<Box<crate::ported::zle::comp_h::Cline>> = None; let mut tail: *mut Option<Box<crate::ported::zle::comp_h::Cline>> = &mut r;
let mut cur = l;
while let Some(node) = cur { let mut t: Box<crate::ported::zle::comp_h::Cline> = Box::new(node.clone());
t.next = None;
if deep != 0 { if let Some(pre) = node.prefix.as_deref() {
t.prefix = cp_cline(Some(pre), 0); }
if let Some(suf) = node.suffix.as_deref() {
t.suffix = cp_cline(Some(suf), 0); }
}
unsafe {
*tail = Some(t);
let new_node = (*tail).as_mut().unwrap();
tail = &mut new_node.next;
}
cur = node.next.as_deref(); }
r }
pub fn cline_sublen(l: &crate::ported::zle::comp_h::Cline) -> i32 { use crate::ported::zle::comp_h::{CLF_LINE, CLF_SUF};
let mut len: i32 = if (l.flags & CLF_LINE) != 0 { l.llen } else { l.wlen };
let no_subs = if (l.flags & CLF_SUF) != 0 {
l.suffix.is_none()
} else {
l.prefix.is_none()
};
if l.olen != 0 && no_subs {
len += l.olen; } else { let mut p = l.prefix.as_deref();
while let Some(pp) = p {
len += if (pp.flags & CLF_LINE) != 0 { pp.llen } else { pp.wlen };
p = pp.next.as_deref();
}
let mut p = l.suffix.as_deref();
while let Some(pp) = p {
len += if (pp.flags & CLF_LINE) != 0 { pp.llen } else { pp.wlen };
p = pp.next.as_deref();
}
}
len }
pub fn cline_setlens(l: &mut Option<Box<crate::ported::zle::comp_h::Cline>>, both: i32) { let mut cur = l.as_deref_mut();
while let Some(node) = cur { let s = cline_sublen(node); node.min = s; if both != 0 { node.max = s; }
cur = node.next.as_deref_mut(); }
}
use std::sync::Mutex;
use std::sync::OnceLock;
pub fn cline_matched(p: &mut Option<Box<crate::ported::zle::comp_h::Cline>>) { use crate::ported::zle::comp_h::CLF_MATCHED;
let mut cur = p.as_deref_mut();
while let Some(node) = cur { node.flags |= CLF_MATCHED; cline_matched(&mut node.prefix); cline_matched(&mut node.suffix); cur = node.next.as_deref_mut(); }
}
pub fn revert_cline( mut p: Option<Box<crate::ported::zle::comp_h::Cline>>,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> { let mut r: Option<Box<crate::ported::zle::comp_h::Cline>> = None; while let Some(mut node) = p { let n = node.next.take(); node.next = r; r = Some(node); p = n; }
r }
pub fn start_match() { MATCHBUF
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clear();
*MATCHPARTS.get_or_init(|| Mutex::new(None)).lock().unwrap() = None;
*MATCHSUBS.get_or_init(|| Mutex::new(None)).lock().unwrap() = None;
}
pub fn abort_match() { free_cline(MATCHPARTS.get_or_init(|| Mutex::new(None)).lock().unwrap().take()); free_cline(MATCHSUBS.get_or_init(|| Mutex::new(None)).lock().unwrap().take()); }
pub fn add_match_str(m: Option<&crate::ported::zle::comp_h::Cmatcher>, l: &str, w: &str, mut wl: i32, sfx: i32)
{
use crate::ported::zle::comp_h::CMF_LINE;
let (eff_w_owned, eff_w): (String, &str) = match m {
Some(mat) if (mat.flags & CMF_LINE) != 0 => {
wl = mat.llen;
let owned = l.to_string();
let s = owned.clone();
(owned, Box::leak(s.into_boxed_str()))
}
_ => (String::new(), w),
};
let _ = eff_w_owned;
if wl <= 0 { return; }
if let Ok(mut buf) = MATCHBUF.get_or_init(|| Mutex::new(String::new())).lock() {
let take_n = wl as usize;
let new_chunk: String = eff_w.chars().take(take_n).collect();
if sfx != 0 { *buf = format!("{}{}", new_chunk, *buf); } else { buf.push_str(&new_chunk);
}
MATCHBUFADDED.fetch_add(wl, std::sync::atomic::Ordering::Relaxed); }
}
pub fn add_match_part(
m: Option<&crate::ported::zle::comp_h::Cmatcher>, l: Option<&str>, _ll: i32,
w: &str, wl: i32,
o: Option<&str>, ol: i32,
s: &str, sl: i32,
osl: i32, sfx: i32,
) {
use crate::ported::zle::comp_h::{Cline, CMF_LEFT, CLF_NEW, CLF_SUF};
let l_eff: Option<String> = match l {
Some(lstr) if lstr.len() >= wl as usize
&& wl > 0
&& &lstr[..wl as usize] == &w[..wl as usize] => None,
Some(lstr) => Some(lstr.to_string()),
None => None,
};
let mut lp: Option<Box<Cline>> = None;
let mut lprem: Option<Box<Cline>> = None;
let mut p = bld_parts(s, sl, osl, Some(&mut lp), Some(&mut lprem));
if let Some(rem) = lprem.as_mut() {
if m.map(|mat| (mat.flags & CMF_LEFT) != 0).unwrap_or(false) {
rem.flags |= CLF_SUF; rem.suffix = rem.prefix.take(); }
}
if sfx != 0 {
if let Some(chain) = p.take() {
p = revert_cline(Some(chain));
}
}
let subs = MATCHSUBS.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok().and_then(|mut g| g.take());
if let Some(subs_chain) = subs { if let Some(lp_node) = lp.as_mut() {
if sfx != 0 { let mut tail_ref: *mut Option<Box<Cline>> = &mut lp_node.prefix;
unsafe {
while let Some(ref mut next_node) = *tail_ref {
tail_ref = &mut next_node.next as *mut _;
}
*tail_ref = Some(subs_chain);
}
} else if let Some(ref mut p_node) = p { let old_prefix = p_node.prefix.take();
let mut new_head = subs_chain;
{
let mut tail_ref: *mut Option<Box<Cline>> = &mut new_head.next;
unsafe {
while let Some(ref mut nn) = *tail_ref {
tail_ref = &mut nn.next as *mut _;
}
*tail_ref = old_prefix;
}
}
p_node.prefix = Some(new_head);
}
}
if let Ok(mut g) = MATCHLASTSUB
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
*g = None;
}
}
if let Some(lp_node) = lp.as_mut() {
if lp_node.llen != 0 || lp_node.wlen != 0 { let next = get_cline(
l_eff.clone(), wl, Some(w.to_string()), wl,
o.map(|s| s.to_string()), ol, CLF_NEW,
);
lp_node.next = Some(next); } else { lp_node.line = l_eff.clone(); lp_node.llen = wl;
lp_node.word = Some(w.to_string()); lp_node.wlen = wl;
lp_node.orig = o.map(|s| s.to_string()); lp_node.olen = ol;
}
if o.is_some() || ol != 0 { lp_node.flags &= !CLF_NEW;
}
}
let last_present = MATCHLASTPART.get()
.and_then(|c| c.lock().ok().map(|g| g.is_some()))
.unwrap_or(false);
if last_present { if let Ok(mut tail) = MATCHLASTPART
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
if let Some(t) = tail.as_mut() {
t.next = p.clone();
}
}
} else if let Ok(mut head) = MATCHPARTS
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
*head = p.clone(); }
if let Some(lp_node) = lp {
if let Ok(mut tail) = MATCHLASTPART
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
*tail = Some(lp_node); }
}
}
pub fn add_match_sub(
m: Option<&crate::ported::zle::comp_h::Cmatcher>, l: Option<&str>, ll: i32, w: Option<&str>, wl: i32,
) {
use crate::ported::zle::comp_h::{Cline, CLF_NEW};
let (eff_w, eff_wl) = match m {
Some(mat) if (mat.flags & crate::ported::zle::comp_h::CMF_LINE) != 0
=> (l, mat.llen),
_ => (w, wl),
};
if eff_wl <= 0 && ll <= 0 { return; }
let node = Box::new(Cline {
flags: CLF_NEW,
line: l.map(|s| s.to_string()),
llen: ll,
word: eff_w.map(|s| s.to_string()),
wlen: eff_wl,
..Default::default()
});
let last_cell = MATCHLASTSUB.get_or_init(|| Mutex::new(None));
let head_cell = MATCHSUBS.get_or_init(|| Mutex::new(None));
let last_present = last_cell.lock().ok().map(|g| g.is_some()).unwrap_or(false);
if last_present { if let Ok(mut tail) = last_cell.lock() {
if let Some(t) = tail.as_mut() {
t.next = Some(node.clone()); }
}
} else { if let Ok(mut h) = head_cell.lock() {
*h = Some(node.clone()); }
}
if let Ok(mut tail) = last_cell.lock() {
*tail = Some(node); }
}
pub fn match_str( l_in: &str, w_in: &str,
_bpp: Option<&mut Option<Box<crate::ported::zle::zle_h::brinfo>>>,
bc: i32,
rwlp: Option<&mut i32>,
sfx: i32, test: i32, part: i32,
) -> i32 {
use crate::ported::zle::comp_h::{CMF_INTER, CMF_LEFT, CMF_LINE, CMF_RIGHT};
let l_bytes = l_in.as_bytes();
let w_bytes = w_in.as_bytes();
let mut ll = l_bytes.len() as i32;
let mut lw = w_bytes.len() as i32;
let mut il: i32 = 0;
let mut iw: i32 = 0;
let mut exact: i32 = 0;
let mut wexact: i32 = 0;
let mut bc = bc;
let _obc = bc;
let add: i32 = if sfx != 0 { -1 } else { 1 };
let ind: i32 = if sfx != 0 { -1 } else { 0 };
if test == 0 { start_match();
}
let mut l_pos: i32 = if sfx != 0 { ll } else { 0 };
let mut w_pos: i32 = if sfx != 0 { lw } else { 0 };
let mut ow_pos: i32 = w_pos;
let mut lm: Option<Box<crate::ported::zle::comp_h::Cmatcher>> = None;
let mut he = 0i32;
let mstack_snapshot: Vec<Box<crate::ported::zle::comp_h::Cmatcher>> = {
let g = crate::ported::zle::compcore::mstack
.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok();
let mut out = Vec::new();
if let Some(g) = g {
let mut cur = g.as_deref();
while let Some(ms) = cur {
let mut mp_cur: Option<&crate::ported::zle::comp_h::Cmatcher> =
Some(&*ms.matcher);
while let Some(mp) = mp_cur {
out.push(Box::new(mp.clone()));
mp_cur = mp.next.as_deref();
}
cur = ms.next.as_deref();
}
}
out
};
'outer: while ll > 0 { if sfx == 0 && lw > 0 && (part == 0 || test != 0) {
let l_idx = (l_pos + ind) as usize;
let w_idx = (w_pos + ind) as usize;
if l_idx < l_bytes.len() && w_idx < w_bytes.len() {
let l_ch = l_bytes[l_idx];
let w_ch = w_bytes[w_idx];
let bslash = lw > 1 && w_ch == b'\\'
&& w_idx + 1 < w_bytes.len()
&& w_bytes[w_idx + 1] == l_bytes[(l_pos + ind) as usize];
if l_ch == w_ch || bslash {
let advance_w = if bslash { 2 } else { 1 };
l_pos += add; w_pos += if bslash { add + add } else { add };
il += 1; iw += advance_w;
ll -= 1; lw -= advance_w;
bc += 1;
exact += 1;
wexact += advance_w;
lm = None;
he = 0;
continue 'outer; }
}
}
let mut matched: Option<Box<crate::ported::zle::comp_h::Cmatcher>> = None;
for mp in mstack_snapshot.iter() {
if let Some(ref lm_box) = lm {
if std::ptr::addr_eq(lm_box.as_ref() as *const _,
mp.as_ref() as *const _) {
continue; }
}
if mp.wlen < 0 {
let llen_p = mp.llen;
let (alen, aol): (i32, i32) = if (mp.flags & CMF_LEFT) != 0 {
(mp.lalen, mp.ralen)
} else {
(mp.ralen, mp.lalen)
};
if ll < llen_p + alen || lw < alen + aol { continue;
}
let (ap, aop, moff, both, loff, aoff): (
Option<&crate::ported::zle::comp_h::Cpattern>,
Option<&crate::ported::zle::comp_h::Cpattern>,
i32, i32, i32, i32);
if (mp.flags & CMF_LEFT) != 0 { ap = mp.left.as_deref();
aop = mp.right.as_deref();
moff = alen;
if sfx != 0 { both = 0; loff = -llen_p; aoff = -(llen_p + alen);
} else { both = 1; loff = alen; aoff = 0;
}
} else { ap = mp.right.as_deref();
aop = mp.left.as_deref();
moff = 0;
if sfx != 0 { both = 1; loff = -(llen_p + alen); aoff = -alen;
} else { both = 0; loff = 0; aoff = llen_p;
}
}
let l_off_idx = (l_pos + loff).max(0) as usize;
if l_off_idx >= l_bytes.len() { continue; }
let line_slice = std::str::from_utf8(
&l_bytes[l_off_idx..]).unwrap_or("");
if pattern_match(mp.line.as_deref(), line_slice, None, "") == 0 {
continue;
}
if let Some(ap_pat) = ap {
let l_anchor_idx = (l_pos + aoff).max(0) as usize;
let l_anchor = std::str::from_utf8(
&l_bytes[l_anchor_idx..]).unwrap_or("");
if pattern_match(Some(ap_pat), l_anchor, None, "") == 0 {
continue;
}
if both != 0 { let w_anchor_idx = (w_pos + aoff).max(0) as usize;
let w_anchor = std::str::from_utf8(
&w_bytes[w_anchor_idx..]).unwrap_or("");
if pattern_match(Some(ap_pat), w_anchor, None, "") == 0 {
continue;
}
if aol > 0 && aol <= aoff + iw {
let w_op_idx = (w_pos + aoff - aol).max(0) as usize;
let w_op = std::str::from_utf8(
&w_bytes[w_op_idx..]).unwrap_or("");
if pattern_match(aop, w_op, None, "") == 0 {
continue;
}
}
let mp_l = std::str::from_utf8(
&l_bytes[l_anchor_idx..]).unwrap_or("");
let mp_w = std::str::from_utf8(
&w_bytes[(w_pos + aoff).max(0) as usize..])
.unwrap_or("");
if match_parts(mp_l, mp_w, alen, part) == 0 {
continue;
}
}
} else { let cmf_check = if (mp.flags & CMF_INTER) != 0 {
if (mp.flags & CMF_LINE) != 0 { iw } else { il }
} else { il | iw };
if both == 0 || cmf_check != 0 {
continue;
}
}
let mut t = 0i32;
let mut ct = 0i32;
let ict_total = lw - alen + 1;
let mut found_tp_pos: i32 = w_pos;
for step in 0..ict_total.max(0) {
let tp_pos = w_pos + step * add;
let mut accept = false;
if both != 0 {
let ap_fails = ap.is_none() || test == 0 || {
let tp_anchor_idx = (tp_pos + aoff).max(0) as usize;
let tp_slice = std::str::from_utf8(
&w_bytes.get(tp_anchor_idx..).unwrap_or(&[]))
.unwrap_or("");
pattern_match(ap, tp_slice, None, "") == 0
};
if ap_fails {
accept = true;
}
} else {
let tp_anchor_idx = (tp_pos - moff).max(0) as usize;
let tp_slice = std::str::from_utf8(
&w_bytes.get(tp_anchor_idx..).unwrap_or(&[]))
.unwrap_or("");
if pattern_match(ap, tp_slice, None, "") != 0 {
let aol_ok = aol == 0 || (aol <= iw + ct - moff && {
let aop_idx = (tp_pos - moff - aol).max(0) as usize;
let aop_slice = std::str::from_utf8(
&w_bytes.get(aop_idx..).unwrap_or(&[]))
.unwrap_or("");
pattern_match(aop, aop_slice, None, "") != 0
});
if aol_ok {
let l_aoff_idx = (l_pos + aoff).max(0) as usize;
let l_aoff_slice = std::str::from_utf8(
&l_bytes[l_aoff_idx..]).unwrap_or("");
let mp_ok = mp.wlen == -1 || match_parts(
l_aoff_slice, tp_slice, alen, part) != 0;
if mp_ok {
accept = true;
}
}
}
}
if accept {
if sfx != 0 {
let l_bound = (l_pos - llen_p - alen).max(0) as usize;
let w_bound = (tp_pos - alen).max(0) as usize;
let l_rest = std::str::from_utf8(
&l_bytes[..l_bound.min(l_bytes.len())])
.unwrap_or("");
let w_rest = std::str::from_utf8(
&w_bytes[..w_bound.min(w_bytes.len())])
.unwrap_or("");
t = match_str(l_rest, w_rest, None, 0, None, sfx,
2, part);
} else {
let l_rest_start = (l_pos + llen_p + moff) as usize;
let l_rest = std::str::from_utf8(
&l_bytes.get(l_rest_start..).unwrap_or(&[]))
.unwrap_or("");
let w_rest_start = (tp_pos + moff) as usize;
let w_rest = std::str::from_utf8(
&w_bytes.get(w_rest_start..).unwrap_or(&[]))
.unwrap_or("");
t = match_str(l_rest, w_rest, None, 0, None, sfx,
1, part);
}
if t != 0 || (mp.wlen == -1 && both == 0) {
found_tp_pos = tp_pos;
break;
}
}
ct += 1;
}
if t == 0 { continue; }
let _tp_pos = found_tp_pos;
if test == 0 && (he == 0 || (llen_p + alen) != 0) {
let (op_start, ol, lp_start, map_start, wap_start, wmp_start);
if sfx != 0 { op_start = w_pos as usize;
ol = (ow_pos - w_pos).max(0);
lp_start = (l_pos - (llen_p + alen)).max(0) as usize;
map_start = (found_tp_pos - alen).max(0) as usize;
if (mp.flags & CMF_LEFT) != 0 { wap_start = (found_tp_pos - alen).max(0) as usize;
wmp_start = found_tp_pos as usize;
} else { wap_start = (w_pos - alen).max(0) as usize;
wmp_start = (found_tp_pos - alen).max(0) as usize;
}
} else { op_start = ow_pos as usize;
ol = (w_pos - ow_pos).max(0);
lp_start = l_pos as usize;
map_start = ow_pos as usize;
if (mp.flags & CMF_LEFT) != 0 { wap_start = w_pos as usize;
wmp_start = (w_pos + alen) as usize;
} else { wap_start = found_tp_pos as usize;
wmp_start = ow_pos as usize;
}
}
if (mp.flags & CMF_LINE) != 0 { let op_str = std::str::from_utf8(
&w_bytes[op_start..op_start + ol as usize])
.unwrap_or("");
let lp_str = std::str::from_utf8(
&l_bytes[lp_start..lp_start + (llen_p + alen) as usize])
.unwrap_or("");
add_match_str(None, "", op_str, ol, sfx);
add_match_str(None, "", lp_str, llen_p + alen, sfx);
add_match_sub(None, None, ol, Some(op_str), ol);
add_match_sub(None, None, llen_p + alen,
Some(lp_str), llen_p + alen);
} else { let map_len = ct + ol + alen;
let map_str = std::str::from_utf8(
&w_bytes[map_start..(map_start + map_len.max(0) as usize)
.min(w_bytes.len())])
.unwrap_or("");
add_match_str(None, "", map_str, map_len, sfx);
let ol_eff = if both != 0 {
let op_str = std::str::from_utf8(
&w_bytes[op_start..op_start + ol as usize])
.unwrap_or("");
add_match_sub(None, None, ol, Some(op_str), ol);
-1
} else {
ct + ol
};
let l_aoff_idx = (l_pos + aoff).max(0) as usize;
let l_loff_idx = (l_pos + loff).max(0) as usize;
let l_aoff_str = std::str::from_utf8(
&l_bytes[l_aoff_idx..l_aoff_idx + alen.max(0) as usize])
.unwrap_or("");
let l_loff_str = std::str::from_utf8(
&l_bytes[l_loff_idx..l_loff_idx + llen_p.max(0) as usize])
.unwrap_or("");
let wap_str = std::str::from_utf8(
&w_bytes[wap_start..(wap_start + alen.max(0) as usize)
.min(w_bytes.len())])
.unwrap_or("");
let wmp_str = std::str::from_utf8(
&w_bytes[wmp_start..(wmp_start + ol_eff.max(0) as usize)
.min(w_bytes.len())])
.unwrap_or("");
add_match_part(Some(mp),
Some(l_aoff_str), alen,
wap_str, alen,
Some(l_loff_str), llen_p,
wmp_str, ol_eff,
ol_eff, sfx);
}
}
let llen_new = llen_p + alen;
let alen_new = alen + ct;
if sfx != 0 { l_pos -= llen_new;
w_pos -= alen_new;
} else { l_pos += llen_new;
w_pos += alen_new;
}
ll -= llen_new; il += llen_new;
lw -= alen_new; iw += alen_new;
bc += llen_new;
exact = 0;
ow_pos = w_pos;
if llen_new == 0 && alen_new == 0 { lm = Some(Box::new((**mp).clone()));
if he == 0 {
he = 1;
} else {
matched = Some(mp.clone());
break;
}
} else {
lm = None;
he = 0;
}
matched = Some(mp.clone());
break;
}
if ll < mp.llen || lw < mp.wlen { continue; }
if (mp.flags & (CMF_LEFT | CMF_RIGHT)) == 0
&& mp.llen == mp.wlen
{
let (l_start, w_start) = if sfx != 0 {
((l_pos - mp.llen) as usize, (w_pos - mp.wlen) as usize)
} else {
(l_pos as usize, w_pos as usize)
};
let l_chunk = &l_bytes[l_start..l_start + mp.llen as usize];
let w_chunk = &w_bytes[w_start..w_start + mp.wlen as usize];
if l_chunk == w_chunk { continue; }
}
let (tl_pos, tw_pos, til, tiw, tll, tlw) = if sfx != 0 {
(l_pos - mp.llen, w_pos - mp.wlen,
ll - mp.llen, lw - mp.wlen,
il + mp.llen, iw + mp.wlen)
} else {
(l_pos, w_pos, il, iw, ll, lw)
};
let mut t: i32 = 1;
if (mp.flags & CMF_LEFT) != 0 {
if til < mp.lalen || tiw < mp.lalen + mp.ralen {
continue;
}
if let Some(ref left_pat) = mp.left {
let l_anchor_start = (tl_pos - mp.lalen) as usize;
let w_anchor_start = (tw_pos - mp.lalen) as usize;
let l_slice = std::str::from_utf8(
&l_bytes[l_anchor_start..]).unwrap_or("");
let w_slice = std::str::from_utf8(
&w_bytes[w_anchor_start..]).unwrap_or("");
let lm_ok = pattern_match(Some(left_pat), l_slice, None, "") != 0;
let wm_ok = pattern_match(Some(left_pat), w_slice, None, "") != 0;
let r_ok = mp.ralen == 0 || {
let r_anchor_start = (tw_pos - mp.lalen - mp.ralen) as usize;
let r_slice = std::str::from_utf8(
&w_bytes[r_anchor_start..]).unwrap_or("");
let right_pat = mp.right.as_deref();
pattern_match(right_pat, r_slice, None, "") != 0
};
t = if lm_ok && wm_ok && r_ok { 1 } else { 0 };
} else {
let cmf_check = if (mp.flags & CMF_INTER) != 0 {
if (mp.flags & CMF_LINE) != 0 { iw } else { il }
} else { il | iw };
t = if sfx == 0 && cmf_check == 0 { 1 } else { 0 };
}
}
if (mp.flags & CMF_RIGHT) != 0 {
if tll < mp.llen + mp.ralen
|| tlw < mp.wlen + mp.ralen + mp.lalen
{
continue;
}
if let Some(ref right_pat) = mp.right {
let l_anchor_start = (tl_pos + mp.llen) as usize;
let w_anchor_start = (tw_pos + mp.wlen) as usize;
let l_slice = std::str::from_utf8(
&l_bytes[l_anchor_start..]).unwrap_or("");
let w_slice = std::str::from_utf8(
&w_bytes[w_anchor_start..]).unwrap_or("");
let lm_ok = pattern_match(Some(right_pat), l_slice, None, "") != 0;
let wm_ok = pattern_match(Some(right_pat), w_slice, None, "") != 0;
let l_ok = mp.lalen == 0 || {
let l_anchor_2 = (tw_pos + mp.wlen - mp.ralen - mp.lalen) as usize;
let l_slice_2 = std::str::from_utf8(
&w_bytes[l_anchor_2..]).unwrap_or("");
let left_pat = mp.left.as_deref();
pattern_match(left_pat, l_slice_2, None, "") != 0
};
t = if lm_ok && wm_ok && l_ok { 1 } else { 0 };
} else {
let cmf_check = if (mp.flags & CMF_INTER) != 0 {
if (mp.flags & CMF_LINE) != 0 { iw } else { il }
} else { il | iw };
t = if sfx != 0 && cmf_check == 0 { 1 } else { 0 };
}
}
if t == 0 { continue; }
let line_pat = mp.line.as_deref();
let word_pat = mp.word.as_deref();
let tl_slice = std::str::from_utf8(
&l_bytes[tl_pos as usize..]).unwrap_or("");
let tw_slice = std::str::from_utf8(
&w_bytes[tw_pos as usize..]).unwrap_or("");
if pattern_match(line_pat, tl_slice, word_pat, tw_slice) == 0 {
continue;
}
if test == 0 {
let carry_l = if sfx != 0 {
if ow_pos >= w_pos { w_pos as usize } else { ow_pos as usize }
} else {
if w_pos >= ow_pos { ow_pos as usize } else { w_pos as usize }
};
let carry_len = if sfx != 0 {
(ow_pos - w_pos).max(0)
} else {
(w_pos - ow_pos).max(0)
};
if carry_len > 0 {
let carry_slice = std::str::from_utf8(
&w_bytes[carry_l..carry_l + carry_len as usize])
.unwrap_or("");
add_match_str(None, "", carry_slice, carry_len, sfx);
add_match_sub(None, None, 0, Some(carry_slice), carry_len);
}
let tw_str = std::str::from_utf8(
&w_bytes[tw_pos as usize..
(tw_pos + mp.wlen) as usize]).unwrap_or("");
add_match_str(Some(mp), tl_slice, tw_str, mp.wlen, sfx);
add_match_sub(Some(mp), Some(tl_slice), mp.llen, Some(tw_str), mp.wlen);
}
if sfx != 0 {
l_pos = tl_pos; w_pos = tw_pos;
} else {
l_pos += mp.llen; w_pos += mp.wlen;
}
il += mp.llen; iw += mp.wlen;
ll -= mp.llen; lw -= mp.wlen;
bc += mp.llen;
exact = 0;
ow_pos = w_pos;
lm = None;
he = 0;
matched = Some(mp.clone());
break;
}
if matched.is_some() { continue 'outer;
}
if (test == 0 || sfx != 0) && lw > 0 {
let l_idx = (l_pos + ind) as usize;
let w_idx = (w_pos + ind) as usize;
if l_idx < l_bytes.len() && w_idx < w_bytes.len() {
let l_ch = l_bytes[l_idx];
let w_ch = w_bytes[w_idx];
let bslash = lw > 1 && w_ch == b'\\'
&& (w_idx + 1) < w_bytes.len()
&& w_bytes[w_idx + 1] == l_bytes[l_idx];
if l_ch == w_ch || bslash {
let advance_w = if bslash { 2 } else { 1 };
l_pos += add; w_pos += if bslash { add + add } else { add };
il += 1; iw += advance_w;
ll -= 1; lw -= advance_w;
bc += 1;
lm = None;
he = 0;
continue 'outer;
}
}
}
if lw == 0 { break; }
if exact > 0 && part == 0 {
il -= exact; iw -= wexact;
ll += exact; lw += wexact;
bc -= exact;
l_pos -= add * exact;
w_pos -= add * wexact;
exact = 0;
wexact = 0;
continue 'outer;
}
if test != 0 { return 0; }
abort_match();
return -1;
}
if test != 0 {
return if part != 0 || ll == 0 { 1 } else { 0 };
}
if part == 0 && ll != 0 {
abort_match();
return -1;
}
if let Some(out) = rwlp {
*out = iw - if sfx != 0 { ow_pos - w_pos } else { w_pos - ow_pos };
}
let _ = (lm, he);
if part != 0 { il } else { iw }
}
pub fn match_parts(l: &str, w: &str, n: i32, part: i32) -> i32 { let ln = (n as usize).min(l.len());
let wn = (n as usize).min(w.len());
let l_slice = &l[..ln];
let w_slice = &w[..wn];
match_str(l_slice, w_slice, None, 0, None, 0, 1, part)
}
#[allow(clippy::too_many_arguments)]
pub fn comp_match( pfx: &str, sfx: &str, w: &str,
cp: Option<&crate::ported::pattern::Patprog>,
clp: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
qu: i32,
_bpl: Option<&mut Option<Box<crate::ported::zle::zle_h::brinfo>>>,
bcp: i32,
_bsl: Option<&mut Option<Box<crate::ported::zle::zle_h::brinfo>>>,
bcs: i32,
exact: &mut i32,
) -> Option<String>
{
use crate::ported::pattern::pattry;
use crate::ported::glob::{remnulargs, tokenize};
use crate::ported::lex::{parse_subst_string, untokenize};
use crate::ported::utils::set_noerrs;
use crate::ported::zle::compcore::{multiquote, tildequote, useqbr};
use std::sync::atomic::Ordering;
let r: String;
if let Some(prog) = cp { r = w.to_string();
let teststr: String = if qu == 0 { let mut t = r.clone();
tokenize(&mut t);
set_noerrs(1);
let parsed = parse_subst_string(&t).ok();
set_noerrs(0);
if let Some(p) = parsed {
let mut p = p;
remnulargs(&mut p);
untokenize(&p)
} else {
r.clone()
}
} else {
r.clone()
};
if !pattry(prog, &teststr) { return None;
}
let r_final = if qu == 2 { tildequote(&r, 0) } else { multiquote(&r, if qu != 0 { 0 } else { 1 }) };
let wl = w.len() as i32;
let lc = bld_parts(w, wl, wl, None, None);
if let Some(out) = clp { *out = lc; }
*exact = 0; return Some(r_final);
}
let w_quoted = if qu == 2 { tildequote(w, 0) } else { multiquote(w, if qu != 0 { 0 } else { 1 }) };
let wl = w_quoted.len() as i32;
useqbr.store(qu, Ordering::Relaxed);
let mut rpl: i32 = 0;
let mpl = match_str(pfx, &w_quoted, None, bcp, Some(&mut rpl), 0, 0, 0); if mpl < 0 {
return None;
}
if !sfx.is_empty() { let mut rsl: i32 = 0;
let suffix_start = (mpl as usize).min(w_quoted.len());
let suffix_part = &w_quoted[suffix_start..];
let msl = match_str(sfx, suffix_part, None, bcs, Some(&mut rsl), 1, 0, 0);
if msl < 0 {
return None; }
let middle_len = (wl - rpl - rsl).max(0) as usize;
let middle_start = (rpl as usize).min(w_quoted.len());
let middle = &w_quoted[middle_start..middle_start + middle_len.min(w_quoted.len() - middle_start)];
let mid_lc = bld_parts(middle, (wl - rpl - rsl).max(0),
(mpl - rpl) + (msl - rsl), None, None);
if let Some(out) = clp { *out = mid_lc; }
let pl = pfx.len();
*exact = if w_quoted.len() >= pl
&& w_quoted.starts_with(pfx)
&& w_quoted[pl..].ends_with(sfx)
{ 1 } else { 0 };
} else { let after_pfx_start = (rpl as usize).min(w_quoted.len());
let after_pfx = &w_quoted[after_pfx_start..];
let pli = bld_parts(after_pfx, (wl - rpl).max(0), mpl - rpl, None, None);
if let Some(out) = clp { *out = pli; }
*exact = if pfx == w_quoted.as_str() { 1 } else { 0 };
}
r = MATCHBUF.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
let r = if r.is_empty() { w_quoted } else { r };
Some(r)
}
pub fn pattern_match1(p: &crate::ported::zle::comp_h::Cpattern, c: u32, mtp: &mut i32) -> u32
{
use crate::ported::zle::comp_h::{CPAT_ANY, CPAT_CCLASS, CPAT_CHAR, CPAT_EQUIV, CPAT_NCLASS};
*mtp = 0; match p.tp { x if x == CPAT_CCLASS => { patmatchrange(p.str.as_deref(), c, None, None) as u32 }
x if x == CPAT_NCLASS => { if patmatchrange(p.str.as_deref(), c, None, None) { 0 } else { 1 } }
x if x == CPAT_EQUIV => { let mut ind: u32 = 0;
if patmatchrange(p.str.as_deref(), c, Some(&mut ind), Some(mtp)) {
ind + 1 } else {
0 }
}
x if x == CPAT_ANY => 1, x if x == CPAT_CHAR => if p.chr == c { c } else { 0 }, _ => 0, }
}
pub fn pattern_match_equivalence(
lp: &crate::ported::zle::comp_h::Cpattern, wind: u32, wmtp: i32, wchr: u32,
) -> u32 {
use crate::ported::zsh_h::{PP_LOWER, PP_RANGE, PP_UPPER};
use crate::ported::zle::zle_h::{ZC_tolower, ZC_toupper};
let Some(ref bytes) = lp.str else { return u32::MAX; };
let Some(target_idx) = (wind as i64).checked_sub(1) else { return u32::MAX; };
if target_idx < 0 { return u32::MAX; }
let mut lchr: Option<u32> = None;
let mut lmtp: i32 = 0;
let mut idx: i64 = 0;
let mut i = 0usize;
let pp_range_marker = (0x80u8).wrapping_add(PP_RANGE as u8);
while i < bytes.len() {
let b = bytes[i];
if b == pp_range_marker { if i + 2 >= bytes.len() { break; }
let r1 = bytes[i + 1];
let r2 = bytes[i + 2];
let span = (r2 as i64) - (r1 as i64);
if span >= 0 && idx + span >= target_idx { lchr = Some(((r1 as i64) + (target_idx - idx)) as u32);
break;
}
idx += span + 1; i += 3;
} else if b >= 0x80 {
let swtype = (b as i32) - 0x80;
if idx == target_idx { lmtp = swtype;
break;
}
idx += 1;
i += 1;
} else {
if idx == target_idx {
lchr = Some(b as u32);
break;
}
idx += 1;
i += 1;
}
}
if let Some(ch) = lchr {
if ch != u32::MAX { return ch; }
}
let wch = char::from_u32(wchr).unwrap_or('\0');
if wmtp == PP_UPPER && lmtp == PP_LOWER {
return ZC_tolower(wch) as u32;
}
if wmtp == PP_LOWER && lmtp == PP_UPPER {
return ZC_toupper(wch) as u32;
}
if wmtp != 0 && wmtp == lmtp { return wchr; }
u32::MAX }
pub fn pattern_match_restrict(
p: Option<&crate::ported::zle::comp_h::Cpattern>, wp: Option<&crate::ported::zle::comp_h::Cpattern>,
wsc: &[u32],
prestrict: Option<&crate::ported::zle::comp_h::Cpattern>,
new_line: &mut Vec<char>,
) -> i32 {
use crate::ported::zle::comp_h::{CPAT_ANY, CPAT_CHAR, CPAT_EQUIV};
use crate::ported::zsh_h::{PP_LOWER, PP_UPPER};
use crate::ported::zle::zle_h::ZC_tolower;
let mut p_cur = p;
let mut wp_cur = wp;
let mut pr_cur = prestrict;
let mut wsc_idx = 0usize;
while p_cur.is_some() && wp_cur.is_some() && wsc_idx < wsc.len() && pr_cur.is_some()
{
let pat = p_cur.unwrap();
let wpat = wp_cur.unwrap();
let pre = pr_cur.unwrap();
let wc = wsc[wsc_idx];
let mut wmt: i32 = 0;
let wind = pattern_match1(wpat, wc, &mut wmt); if wind == 0 { return 0; }
let c: u32 = if pre.tp == CPAT_CHAR { pre.chr } else if pat.tp == CPAT_CHAR { pat.chr } else if pat.tp == CPAT_EQUIV { let r = pattern_match_equivalence(pat, wind, wmt, wc);
if r == u32::MAX { return 0; } r
} else { wc };
if pre.tp != CPAT_CHAR {
let mut mt: i32 = 0;
if pattern_match1(pre, c, &mut mt) == 0 { return 0; } }
if pat.tp != CPAT_ANY || wpat.tp != CPAT_ANY { let mut mt: i32 = 0;
let ind = pattern_match1(pat, c, &mut mt); if ind == 0 || ind != wind { return 0; } if mt != wmt {
let case_pair = (mt == PP_LOWER || mt == PP_UPPER)
&& (wmt == PP_LOWER || wmt == PP_UPPER);
if case_pair {
let cc = char::from_u32(c).unwrap_or('\0');
let wcc = char::from_u32(wc).unwrap_or('\0');
if ZC_tolower(cc) != ZC_tolower(wcc) { return 0; } } else {
return 0; }
}
}
if let Some(ch) = char::from_u32(c) {
new_line.push(ch);
}
pr_cur = pre.next.as_deref(); wsc_idx += 1;
p_cur = pat.next.as_deref();
wp_cur = wpat.next.as_deref();
}
while p_cur.is_some() && pr_cur.is_some() { let pat = p_cur.unwrap();
let pre = pr_cur.unwrap();
let c: u32 = if pre.tp == CPAT_CHAR {
pre.chr
} else if pat.tp == CPAT_CHAR {
pat.chr
} else {
return 0; };
let mut mt: i32 = 0;
if pre.tp != CPAT_CHAR && pattern_match1(pre, c, &mut mt) == 0 {
return 0;
}
if let Some(ch) = char::from_u32(c) {
new_line.push(ch);
}
pr_cur = pre.next.as_deref();
p_cur = pat.next.as_deref();
}
if p_cur.is_none() && pr_cur.is_none()
&& (wp_cur.is_none() || wsc_idx >= wsc.len())
{
1 } else {
0 }
}
pub fn pattern_match(
p: Option<&crate::ported::zle::comp_h::Cpattern>, s: &str,
wp: Option<&crate::ported::zle::comp_h::Cpattern>,
ws: &str,
) -> i32 {
use crate::ported::zle::comp_h::CPAT_ANY;
use crate::ported::zsh_h::{PP_LOWER, PP_UPPER};
use crate::ported::zle::zle_h::ZC_tolower;
let (mut p_cur, mut wp_cur) = (p, wp); let mut s_bytes = s.chars().peekable();
let mut ws_bytes = ws.chars().peekable();
while p_cur.is_some() && wp_cur.is_some() && s_bytes.peek().is_some() && ws_bytes.peek().is_some()
{
let pat = p_cur.unwrap();
let wpat = wp_cur.unwrap();
let wc = ws_bytes.next().unwrap() as u32; let mut wmt: i32 = 0;
let wind = pattern_match1(wpat, wc, &mut wmt); if wind == 0 { return 0; }
let c = s_bytes.next().unwrap() as u32; if pat.tp != CPAT_ANY || wpat.tp != CPAT_ANY { let mut mt: i32 = 0;
let ind = pattern_match1(pat, c, &mut mt); if ind == 0 { return 0; } if ind != wind { return 0; } if mt != wmt { let case_pair = (mt == PP_LOWER || mt == PP_UPPER)
&& (wmt == PP_LOWER || wmt == PP_UPPER);
if case_pair {
let cc = char::from_u32(c).unwrap_or('\0');
let wcc = char::from_u32(wc).unwrap_or('\0');
if ZC_tolower(cc) != ZC_tolower(wcc) { return 0;
}
} else {
return 0; }
}
}
p_cur = pat.next.as_deref(); wp_cur = wpat.next.as_deref();
}
if p_cur.is_none() && wp_cur.is_none()
&& s_bytes.peek().is_none() && ws_bytes.peek().is_none()
{
1 } else {
0 }
}
pub fn bld_parts(
str: &str, len: i32, mut plen: i32, lp: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
lprem: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> {
use crate::ported::zle::comp_h::{Cline, CLF_NEW};
let bytes = str.as_bytes();
let total: usize = (len as usize).min(bytes.len());
let mut op = plen;
let mut p_start = 0usize;
let mut str_pos = 0usize;
let mut remaining = total as i32;
let mut head: Option<Box<Cline>> = None;
let mut tail_ref: *mut Option<Box<Cline>> = &mut head;
let mut last_n: Option<Box<Cline>> = None;
while remaining > 0 { let mut found_anchor = false;
let bmatchers_chain = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
let mut cur = bmatchers_chain.as_deref();
while let Some(ms) = cur {
let mp = &*ms.matcher;
let preds_ok = mp.flags == crate::ported::zle::comp_h::CMF_RIGHT
&& mp.wlen < 0
&& mp.ralen > 0
&& mp.llen == 0
&& remaining >= mp.ralen
&& (str_pos as i32 - p_start as i32) >= mp.lalen;
if !preds_ok { cur = ms.next.as_deref(); continue; }
let str_at = std::str::from_utf8(&bytes[str_pos..]).unwrap_or("");
if crate::ported::zle::compmatch::pattern_match(
mp.right.as_deref(), str_at, None, "") == 0
{
cur = ms.next.as_deref();
continue;
}
let l_anchor_ok = mp.lalen == 0 || {
let off = str_pos as i32 - mp.lalen;
if off < 0 { false } else {
let l_slice = std::str::from_utf8(&bytes[off as usize..])
.unwrap_or("");
crate::ported::zle::compmatch::pattern_match(
mp.left.as_deref(), l_slice, None, "") != 0
}
};
if !l_anchor_ok { cur = ms.next.as_deref(); continue; }
let olen = (str_pos - p_start) as i32;
let flags = if plen <= 0 { CLF_NEW } else { 0 };
let anchor_word: String = std::str::from_utf8(
&bytes[str_pos..str_pos + mp.ralen as usize]).unwrap_or("").into();
let mut node = Box::new(Cline {
llen: mp.ralen,
word: Some(anchor_word.clone()),
wlen: mp.ralen,
flags,
..Default::default()
});
if p_start != str_pos {
let mut llen = if op < 0 { 0 } else { op };
if llen > olen { llen = olen; }
let prefix_word: String = std::str::from_utf8(
&bytes[p_start..p_start + olen as usize]).unwrap_or("").into();
node.prefix = Some(Box::new(Cline {
llen,
word: Some(prefix_word),
wlen: olen,
..Default::default()
}));
}
unsafe {
*tail_ref = Some(node);
tail_ref = &mut (*tail_ref).as_mut().unwrap().next;
}
str_pos += mp.ralen as usize;
remaining -= mp.ralen;
plen -= mp.ralen;
op -= olen;
p_start = str_pos;
found_anchor = true;
break;
}
if !found_anchor {
str_pos += 1;
remaining -= 1;
plen -= 1;
}
}
if p_start != str_pos { let olen = (str_pos - p_start) as i32;
let mut llen = if op < 0 { 0 } else { op };
if llen > olen { llen = olen; }
let flags = if plen <= 0 { CLF_NEW } else { 0 };
let mut node = Box::new(Cline {
flags,
..Default::default()
});
let prefix_word: String = std::str::from_utf8(
&bytes[p_start..p_start + olen as usize]
).unwrap_or("").into();
node.prefix = Some(Box::new(Cline {
llen,
word: Some(prefix_word.clone()),
wlen: olen,
..Default::default()
}));
if let Some(out) = lprem { *out = Some(node.clone()); } last_n = Some(node.clone());
unsafe {
*tail_ref = Some(node);
}
} else if head.is_none() { let flags = if plen <= 0 { CLF_NEW } else { 0 };
let node = Box::new(Cline {
flags,
..Default::default()
});
if let Some(out) = lprem { *out = Some(node.clone()); } last_n = Some(node.clone());
head = Some(node);
} else if let Some(out) = lprem { *out = None;
}
if let (Some(out_lp), Some(n)) = (lp, last_n) { *out_lp = Some(n);
}
let _ = p_start;
let _ = op;
head }
pub fn bld_line(
mp: &crate::ported::zle::comp_h::Cmatcher, line: &mut Vec<char>,
mword: &str,
word: &str,
wlen: i32,
_sfx: i32,
) -> i32 {
use crate::ported::zle::comp_h::{CPAT_ANY, CPAT_CCLASS, CPAT_CHAR,
CPAT_EQUIV, CPAT_NCLASS};
let _ = mword;
let word_chars: Vec<char> = word.chars().collect();
let mut consumed: i32 = 0;
let mut lpat = mp.line.as_deref();
while let Some(p) = lpat {
if consumed >= wlen { break; }
let widx = consumed as usize;
match p.tp {
x if x == CPAT_CHAR => { if let Some(ch) = char::from_u32(p.chr) {
line.push(ch);
consumed += 1;
}
}
x if x == CPAT_ANY => { if let Some(&wch) = word_chars.get(widx) {
line.push(wch);
consumed += 1;
}
}
x if x == CPAT_CCLASS || x == CPAT_NCLASS || x == CPAT_EQUIV => { if let Some(&wch) = word_chars.get(widx) {
let mut mt = 0i32;
if pattern_match1(p, wch as u32, &mut mt) != 0 {
line.push(wch);
consumed += 1;
} else {
break;
}
} else {
break;
}
}
_ => break,
}
lpat = p.next.as_deref();
}
consumed }
pub fn join_strs(mut la: i32, sa: &str, mut lb: i32, sb: &str) -> Option<String>
{
let mut out = String::new();
let mut a_idx = 0usize;
let mut b_idx = 0usize;
let a_bytes = sa.as_bytes();
let b_bytes = sb.as_bytes();
while la > 0 && lb > 0 && a_idx < a_bytes.len() && b_idx < b_bytes.len() {
if a_bytes[a_idx] == b_bytes[b_idx] { out.push(a_bytes[a_idx] as char);
a_idx += 1;
b_idx += 1;
la -= 1;
lb -= 1;
} else {
let bmatchers = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
let mut advanced = false;
let mut cur = bmatchers.as_deref();
while let Some(ms) = cur { let mp = &*ms.matcher;
let ok = mp.flags == 0 && mp.wlen > 0 && mp.llen > 0
&& mp.wlen <= la && mp.wlen <= lb;
if ok {
let mp_word = mp.word.as_deref();
let a_slice = &sa[a_idx..];
let b_slice = &sb[b_idx..];
let t = if pattern_match(mp_word, a_slice, None, "") != 0 {
1
} else if pattern_match(mp_word, b_slice, None, "") != 0 {
2
} else { 0 };
if t != 0 {
let mut line: Vec<char> = Vec::new();
let bl = bld_line(
mp, &mut line,
"", if t == 1 { b_slice } else { a_slice },
if t == 1 { lb } else { la },
0,
);
if bl > 0 { for ch in &line { out.push(*ch); }
if t == 1 {
a_idx += mp.wlen as usize;
la -= mp.wlen;
b_idx += bl as usize;
lb -= bl;
} else {
b_idx += mp.wlen as usize;
lb -= mp.wlen;
a_idx += bl as usize;
la -= bl;
}
advanced = true;
break;
}
}
}
cur = ms.next.as_deref();
}
if !advanced { break; }
}
}
if !out.is_empty() { Some(out) } else { None } }
pub fn cmp_anchors(o: &mut crate::ported::zle::comp_h::Cline, n: &crate::ported::zle::comp_h::Cline,
join: i32) -> i32 {
use crate::ported::zle::comp_h::{CLF_JOIN, CLF_LINE};
let strncmp_eq = |a: &Option<String>, b: &Option<String>, n: usize| -> bool {
match (a, b) {
(Some(x), Some(y)) => {
let xb = x.as_bytes();
let yb = y.as_bytes();
xb.len() >= n && yb.len() >= n && xb[..n] == yb[..n]
}
_ => false,
}
};
let word_match = (o.flags & CLF_LINE) == 0
&& o.wlen == n.wlen
&& (o.word.is_none()
|| strncmp_eq(&o.word, &n.word, o.wlen as usize));
let line_match = !word_match && {
let both_empty = o.line.is_none() && n.line.is_none()
&& o.wlen == 0 && n.wlen == 0;
let both_lines = o.llen == n.llen
&& o.line.is_some() && n.line.is_some()
&& strncmp_eq(&o.line, &n.line, o.llen as usize);
both_empty || both_lines };
if word_match || line_match { if line_match {
o.flags |= CLF_LINE;
o.word = None; o.wlen = 0; }
return 1; }
if join != 0 && (o.flags & CLF_JOIN) == 0
&& o.word.is_some() && n.word.is_some()
{
if let Some(j) = join_strs(
o.wlen,
o.word.as_deref().unwrap(),
n.wlen,
n.word.as_deref().unwrap(),
) {
o.flags |= CLF_JOIN; o.wlen = j.len() as i32; o.word = Some(j); return 2; }
}
0 }
#[derive(Default, Clone, Debug)]
#[allow(non_camel_case_types)]
pub struct cmdata { pub cl: Option<Box<crate::ported::zle::comp_h::Cline>>, pub pcl: Option<Box<crate::ported::zle::comp_h::Cline>>, pub str: String, pub astr: String, pub len: i32, pub alen: i32, pub olen: i32, pub line: i32, }
pub fn check_cmdata(md: &mut cmdata, sfx: i32) -> i32 { use crate::ported::zle::comp_h::CLF_LINE;
if md.len != 0 { return 0; } let next = match md.cl.as_deref() { None => return 1,
Some(n) => n.clone(),
};
if (next.flags & CLF_LINE) != 0 { md.line = 1;
md.len = next.llen; md.str = next.line.clone().unwrap_or_default(); } else {
md.line = 0;
md.len = next.wlen; md.olen = next.wlen; if let Some(ref w) = next.word {
md.str = if sfx != 0 { w[md.len as usize..].to_string() } else { w.clone() };
}
md.alen = next.llen; if let Some(ref l) = next.line {
md.astr = if sfx != 0 { l[md.alen as usize..].to_string() } else { l.clone() };
}
}
md.pcl = Some(Box::new(next.clone())); md.cl = next.next.clone(); 0 }
pub fn undo_cmdata(md: &cmdata, sfx: i32) -> Option<Box<crate::ported::zle::comp_h::Cline>> { use crate::ported::zle::comp_h::CLF_LINE;
let mut r = md.pcl.as_deref().cloned()?;
if md.line != 0 { r.word = None; r.wlen = 0; r.flags |= CLF_LINE; r.llen = md.len; let off = if sfx != 0 { md.len as usize } else { 0 };
r.line = Some(md.str.chars().skip(md.str.len().saturating_sub(off + md.len as usize)).collect());
} else if md.len != md.olen { r.wlen = md.len; let off = if sfx != 0 { md.len as usize } else { 0 };
r.word = Some(md.str.chars().skip(md.str.len().saturating_sub(off + md.len as usize)).collect());
}
Some(Box::new(r)) }
pub fn join_sub(md: &mut cmdata, str: &str, len: i32, mlen: &mut i32, sfx: i32, join: i32) -> Option<Box<crate::ported::zle::comp_h::Cline>>
{
use crate::ported::zle::comp_h::CLF_JOIN;
if check_cmdata(md, sfx) != 0 {
return None;
}
let ow = str;
let nw = md.str.clone();
let ol = len;
let nl = md.len;
let bmatchers = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
let mut cur = bmatchers.as_deref();
while let Some(ms) = cur { let mp = &*ms.matcher;
if mp.flags == 0 && mp.wlen > 0 && mp.llen > 0 { if mp.llen <= ol && mp.wlen <= nl { let ow_off = if sfx != 0 { ol - mp.llen } else { 0 };
let nw_off = if sfx != 0 { nl - mp.wlen } else { 0 };
let line_slice = &ow[ow_off as usize..];
let word_slice = &nw[nw_off as usize..];
if pattern_match(
mp.line.as_deref(), line_slice,
mp.word.as_deref(), word_slice,
) != 0
{
if sfx != 0 {
md.str = md.str.chars().take(
md.str.chars().count().saturating_sub(mp.wlen as usize),
).collect();
} else {
md.str = md.str.chars()
.skip(mp.wlen as usize).collect();
}
md.len -= mp.wlen;
*mlen = mp.llen; return Some(get_cline( None, 0,
Some(line_slice[..mp.llen as usize].to_string()),
mp.llen, None, 0, 0,
));
}
}
if join != 0 && mp.wlen <= ol && mp.wlen <= nl { let ow_off = if sfx != 0 { ol - mp.wlen } else { 0 };
let nw_off = if sfx != 0 { nl - mp.wlen } else { 0 };
let mp_word = mp.word.as_deref();
let ow_slice = &ow[ow_off as usize..];
let nw_slice = &nw[nw_off as usize..];
let t = if pattern_match(mp_word, ow_slice, None, "") != 0 {
1
} else if pattern_match(mp_word, nw_slice, None, "") != 0 {
2
} else { 0 };
if t != 0 { let (mw_slice, other_slice, other_len) = if t == 1 {
(ow_slice, nw_slice, nl)
} else {
(nw_slice, ow_slice, ol)
};
let _ = mw_slice;
let mut line: Vec<char> = Vec::new();
let bl = bld_line(
mp, &mut line, "", other_slice, other_len, sfx,
);
if bl > 0 { let new_nl = if t == 1 { bl } else { mp.wlen };
let new_ol = if t == 1 { mp.wlen } else { bl };
if sfx != 0 {
md.str = md.str.chars().take(
md.str.chars().count().saturating_sub(new_nl as usize),
).collect();
} else {
md.str = md.str.chars().skip(new_nl as usize).collect();
}
md.len -= new_nl; *mlen = new_ol;
let line_str: String = line.iter().collect();
return Some(get_cline( None, 0,
Some(line_str), mp.llen, None, 0, CLF_JOIN,
));
}
}
}
}
cur = ms.next.as_deref();
}
None }
pub fn sub_match(md: &mut cmdata, str: &str, len: i32, sfx: i32) -> i32 { let mut ret = 0i32;
let str_bytes = str.as_bytes();
let mut remaining = len as usize;
let start_idx: usize = if sfx != 0 { (len as usize).min(str_bytes.len()) } else { 0 };
while remaining > 0 { if check_cmdata(md, sfx) != 0 { return ret;
}
let md_bytes = md.str.as_bytes();
let mut l: usize = 0;
let md_len_usize = md.len as usize;
let cap = remaining.min(md_len_usize);
while l < cap {
let s_idx: isize = if sfx != 0 {
start_idx as isize - (l as isize) - 1 - (ret as isize)
} else {
(ret as isize) + (l as isize)
};
let m_len = md_bytes.len();
let m_idx: isize = if sfx != 0 {
m_len as isize - (l as isize) - 1
} else {
l as isize
};
if s_idx < 0 || m_idx < 0 { break; }
let s_pos = s_idx as usize;
let m_pos = m_idx as usize;
if s_pos >= str_bytes.len() || m_pos >= md_bytes.len() { break; }
if str_bytes[s_pos] != md_bytes[m_pos] { break; }
l += 1;
}
if l == 0 { return ret; }
const META_BYTE: u8 = 0x83;
let check_pos: isize = if sfx != 0 {
start_idx as isize - (l as isize) - (ret as isize)
} else {
(ret as isize) + (l as isize) - 1
};
if check_pos >= 0 && (check_pos as usize) < str_bytes.len()
&& str_bytes[check_pos as usize] == META_BYTE && l > 0
{
l -= 1;
}
md.len -= l as i32;
if sfx != 0 {
md.str = md.str.chars().take(
md.str.chars().count().saturating_sub(l),
).collect();
} else {
md.str = md.str.chars().skip(l).collect();
}
ret += l as i32; remaining = remaining.saturating_sub(l);
if remaining == 0 || md.len == 0 { break;
}
}
ret }
pub fn join_psfx(
ot: &mut crate::ported::zle::comp_h::Cline, nt: &mut crate::ported::zle::comp_h::Cline,
orest: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
nrest: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
sfx: i32,
) {
use crate::ported::zle::comp_h::{CLF_DIFF, CLF_JOIN, CLF_LINE, CLF_MISS};
let mut remaining: Option<Box<crate::ported::zle::comp_h::Cline>> = if sfx != 0 {
ot.suffix.take()
} else {
ot.prefix.take()
};
let n_chain = if sfx != 0 { nt.suffix.clone() } else { nt.prefix.clone() };
if remaining.is_none() {
if let Some(out) = orest { *out = None; } if let Some(out) = nrest { *out = n_chain.clone(); } if let Some(ref nn) = n_chain { if nn.wlen != 0 {
ot.flags |= CLF_MISS; }
}
if sfx != 0 { ot.suffix = remaining; } else { ot.prefix = remaining; }
return; }
if n_chain.is_none() {
if let Some(out) = orest { *out = remaining.take();
} else {
free_cline(remaining.take()); }
if let Some(out) = nrest { *out = None; } return; }
let mut md = cmdata {
cl: n_chain.clone(),
pcl: None,
str: String::new(),
astr: String::new(),
len: 0,
alen: 0,
olen: 0,
line: 0,
};
let mut result_head: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
let mut result_tail_ptr: *mut Option<Box<crate::ported::zle::comp_h::Cline>> =
&mut result_head;
let mut have_prev = false;
let ot_slen = ot.slen;
'walk: while let Some(mut o_node) = remaining.take() {
remaining = o_node.next.take();
let omd = md.clone(); let mut len: i32;
let mut join = 0;
let mut line = 0;
if (o_node.flags & CLF_LINE) != 0 {
let line_str = o_node.line.clone().unwrap_or_default();
len = sub_match(&mut md, &line_str, o_node.llen, sfx);
if len != o_node.llen && len >= 0 {
join = 1;
line = 1;
}
} else {
let word_str = o_node.word.clone().unwrap_or_default();
len = sub_match(&mut md, &word_str, o_node.wlen, sfx);
if len != o_node.wlen && len >= 0 {
if o_node.line.is_some() {
md = omd;
o_node.flags |= CLF_LINE | CLF_DIFF; o_node.next = remaining.take();
remaining = Some(o_node);
continue 'walk; }
o_node.llen -= ot_slen;
join = 1;
line = 0;
}
}
if join != 0 {
let (sstr_owned, slen) = if line != 0 {
(o_node.line.clone().unwrap_or_default(), o_node.llen)
} else {
(o_node.word.clone().unwrap_or_default(), o_node.wlen)
};
let sstr_bytes = sstr_owned.as_bytes();
let rest_start = (len as usize).min(sstr_bytes.len());
let rest_str = String::from_utf8_lossy(&sstr_bytes[rest_start..]).into_owned();
let mut jlen: i32 = 0;
let new_join_flag = if (o_node.flags & CLF_JOIN) != 0 { 0 } else { 1 };
let joinl_opt = join_sub(&mut md, &rest_str, slen - len,
&mut jlen, sfx, new_join_flag);
if let Some(mut joinl) = joinl_opt {
joinl.flags |= CLF_DIFF; if len + jlen != slen {
let off = if sfx != 0 { 0usize } else { (len + jlen) as usize };
let off = off.min(sstr_bytes.len());
let take_n = ((slen - len - jlen).max(0) as usize)
.min(sstr_bytes.len() - off);
let rest_word_str = String::from_utf8_lossy(
&sstr_bytes[off..off + take_n],
).into_owned();
let mut rest = get_cline(
None, 0,
Some(rest_word_str),
slen - len - jlen,
None, 0, 0,
);
rest.next = remaining.take(); joinl.next = Some(rest);
} else {
joinl.next = remaining.take(); }
if len != 0 {
if sfx != 0 {
let drop_n = ((slen - len).max(0) as usize)
.min(sstr_bytes.len());
let kept = String::from_utf8_lossy(&sstr_bytes[drop_n..])
.into_owned();
if line != 0 { o_node.line = Some(kept); }
else { o_node.word = Some(kept); }
} else {
let keep_n = (len as usize).min(sstr_bytes.len());
let kept = String::from_utf8_lossy(&sstr_bytes[..keep_n])
.into_owned();
if line != 0 { o_node.line = Some(kept); }
else { o_node.word = Some(kept); }
}
if line != 0 { o_node.llen = len; } else { o_node.wlen = len; }
unsafe {
*result_tail_ptr = Some(o_node);
let nxt = &mut (*result_tail_ptr).as_mut().unwrap().next;
result_tail_ptr = nxt as *mut _;
}
have_prev = true;
} else {
drop(o_node);
}
remaining = Some(joinl); continue 'walk;
}
let orest_some = orest.is_some();
let nrest_some = nrest.is_some();
if len != 0 {
if orest_some {
let off = (len as usize).min(sstr_bytes.len());
let tail_str = String::from_utf8_lossy(&sstr_bytes[off..])
.into_owned();
let r = if line != 0 {
get_cline(Some(tail_str), slen - len,
None, 0, None, 0, o_node.flags)
} else {
get_cline(None, 0,
Some(tail_str), slen - len,
None, 0, o_node.flags)
};
let mut r = r;
r.next = remaining.take();
if let Some(out) = orest { *out = Some(r); }
if line != 0 {
o_node.llen = len;
let keep = String::from_utf8_lossy(&sstr_bytes[..off])
.into_owned();
o_node.line = Some(keep);
} else {
o_node.wlen = len;
let keep = String::from_utf8_lossy(&sstr_bytes[..off])
.into_owned();
o_node.word = Some(keep);
}
o_node.next = None;
unsafe {
*result_tail_ptr = Some(o_node);
}
} else {
if sfx != 0 {
let drop_n = ((slen - len).max(0) as usize)
.min(sstr_bytes.len());
let kept = String::from_utf8_lossy(&sstr_bytes[drop_n..])
.into_owned();
if line != 0 { o_node.line = Some(kept); }
else { o_node.word = Some(kept); }
} else {
let keep_n = (len as usize).min(sstr_bytes.len());
let kept = String::from_utf8_lossy(&sstr_bytes[..keep_n])
.into_owned();
if line != 0 { o_node.line = Some(kept); }
else { o_node.word = Some(kept); }
}
if line != 0 { o_node.llen = len; } else { o_node.wlen = len; }
free_cline(remaining.take()); o_node.next = None;
unsafe {
*result_tail_ptr = Some(o_node);
}
}
} else {
let _ = have_prev;
if orest_some {
o_node.next = remaining.take();
if let Some(out) = orest { *out = Some(o_node); }
} else {
drop(o_node);
}
}
if !orest_some || !nrest_some {
ot.flags |= CLF_MISS; }
if let Some(out) = nrest { *out = undo_cmdata(&md, sfx); }
if sfx != 0 { ot.suffix = result_head; }
else { ot.prefix = result_head; }
return; }
unsafe {
*result_tail_ptr = Some(o_node);
let nxt = &mut (*result_tail_ptr).as_mut().unwrap().next;
result_tail_ptr = nxt as *mut _;
}
have_prev = true;
}
if md.len != 0 || md.cl.is_some() {
ot.flags |= CLF_MISS; }
if let Some(out) = orest { *out = None; } if let Some(out) = nrest { *out = undo_cmdata(&md, sfx); }
if sfx != 0 { ot.suffix = result_head; }
else { ot.prefix = result_head; }
let _ = &nt;
}
pub fn join_mid(o: &mut crate::ported::zle::comp_h::Cline, n: &mut crate::ported::zle::comp_h::Cline)
{
use crate::ported::zle::comp_h::CLF_JOIN;
if (o.flags & CLF_JOIN) != 0 { let mut nr: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
join_psfx(o, n, None, Some(&mut nr), 0);
n.suffix = nr.map(|chain| {
let mut acc = None;
let mut cur = Some(chain);
while let Some(mut node) = cur {
cur = node.next.take();
node.next = acc;
acc = Some(node);
}
acc
}).flatten();
join_psfx(o, n, None, None, 1);
} else { o.flags |= CLF_JOIN;
let mut or_: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
let mut nr: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
join_psfx(o, n, Some(&mut or_), Some(&mut nr), 0);
if let Some(ref mut or_node) = or_ { let new_llen = if o.slen > or_node.wlen { or_node.wlen } else { o.slen };
or_node.llen = new_llen;
}
let mut reversed_or = None;
let mut cur = or_;
while let Some(mut node) = cur {
cur = node.next.take();
node.next = reversed_or;
reversed_or = Some(node);
}
o.suffix = reversed_or;
let mut reversed_nr = None;
let mut cur = nr;
while let Some(mut node) = cur {
cur = node.next.take();
node.next = reversed_nr;
reversed_nr = Some(node);
}
n.suffix = reversed_nr;
join_psfx(o, n, None, None, 1); }
n.suffix = None; }
pub fn sub_join(a: &mut crate::ported::zle::comp_h::Cline, b: Option<Box<crate::ported::zle::comp_h::Cline>>,
e: &mut crate::ported::zle::comp_h::Cline,
anew: i32) -> i32
{
use crate::ported::zle::comp_h::CLF_SUF;
if e.suffix.is_some() || a.prefix.is_none() {
return 0; }
let mut min: i32 = 0;
let mut max: i32 = 0;
let mut chain: Vec<Box<crate::ported::zle::comp_h::Cline>> = Vec::new();
let mut cur = b;
while let Some(mut b_node) = cur {
cur = b_node.next.take();
let mut walk_pref = b_node.prefix.take();
while let Some(mut p_node) = walk_pref {
walk_pref = p_node.next.take();
chain.push(p_node);
}
b_node.suffix = None;
b_node.prefix = None;
b_node.flags &= !CLF_SUF;
min += b_node.min;
max += b_node.max;
chain.push(b_node);
}
let mut walk_e = e.prefix.take();
let op_index = chain.len(); let mut had_op = false;
while let Some(mut node) = walk_e {
walk_e = node.next.take();
chain.push(node);
had_op = true;
}
let ca: Option<Box<crate::ported::zle::comp_h::Cline>> = a.prefix.clone();
let mut i = 0usize;
while i < chain.len() {
let mut head: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
let mut tail: *mut Option<Box<crate::ported::zle::comp_h::Cline>> = &mut head;
for src in &chain[i..] {
let mut clone = Box::new((**src).clone());
clone.next = None;
clone.prefix = cp_cline(src.prefix.as_deref(), 0);
clone.suffix = cp_cline(src.suffix.as_deref(), 0);
unsafe {
*tail = Some(clone);
let nn = (*tail).as_mut().unwrap();
tail = &mut nn.next;
}
}
e.prefix = head;
a.prefix = cp_cline(ca.as_deref(), 1);
let f = e.flags; if anew != 0 {
join_psfx(e, a, None, None, 0); e.flags = f; if e.prefix.is_some() { return max - min; }
} else {
join_psfx(a, e, None, None, 0); e.flags = f; if a.prefix.is_some() { return max - min; }
}
min -= chain[i].min;
if had_op && i == op_index {
break;
}
i += 1; }
max - min }
pub fn join_clines( o: Option<Box<crate::ported::zle::comp_h::Cline>>,
n: Option<Box<crate::ported::zle::comp_h::Cline>>,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> {
use crate::ported::zle::comp_h::{CLF_JOIN, CLF_MATCHED, CLF_MID, CLF_MISS,
CLF_NEW, CLF_SKIP, CLF_SUF};
let mut n_chain = n;
cline_setlens(&mut n_chain, 1);
let Some(_) = o else { return n_chain; };
let mut oo: Option<Box<crate::ported::zle::comp_h::Cline>> = o;
let mut nn: Option<Box<crate::ported::zle::comp_h::Cline>> = n_chain;
fn find_node_in_chain<F>(
head: &crate::ported::zle::comp_h::Cline,
mut pred: F,
) -> Option<usize>
where F: FnMut(&crate::ported::zle::comp_h::Cline) -> bool,
{
let mut cur = head.next.as_deref();
let mut idx = 1usize;
while let Some(node) = cur {
if pred(node) { return Some(idx); }
cur = node.next.as_deref();
idx += 1;
}
None
}
unsafe fn splice_take_at(
slot: *mut Option<Box<crate::ported::zle::comp_h::Cline>>,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> { unsafe {
(*slot).take()
} }
unsafe fn slot_at_offset(
head: *mut Option<Box<crate::ported::zle::comp_h::Cline>>,
n: usize,
) -> *mut Option<Box<crate::ported::zle::comp_h::Cline>> { unsafe {
let mut s = head;
for _ in 0..n {
s = &mut (*s).as_mut().unwrap().next;
}
s
} }
unsafe {
type Ptr = *mut Option<Box<crate::ported::zle::comp_h::Cline>>;
let mut oo_slot: Ptr = &mut oo;
let mut nn_slot: Ptr = &mut nn;
let mut po_slot: Ptr = std::ptr::null_mut();
let mut pn_slot: Ptr = std::ptr::null_mut();
while (*oo_slot).is_some() && (*nn_slot).is_some() {
let o_new;
let n_new;
let o_flags;
let n_flags;
{
let o_ref = (*oo_slot).as_deref().unwrap();
let n_ref = (*nn_slot).as_deref().unwrap();
o_new = (o_ref.flags & CLF_NEW) != 0;
n_new = (n_ref.flags & CLF_NEW) != 0;
o_flags = o_ref.flags;
n_flags = n_ref.flags;
}
if o_new && !n_new {
let n_immut: *const crate::ported::zle::comp_h::Cline =
(*nn_slot).as_deref().unwrap();
let o_head: *mut crate::ported::zle::comp_h::Cline =
(*oo_slot).as_deref_mut().unwrap();
let found = find_node_in_chain(&*o_head, |t| {
(t.flags & CLF_NEW) == 0
&& {
let mut t_copy = t.clone();
cmp_anchors(&mut t_copy, &*n_immut, 0) != 0
}
});
if let Some(steps) = found {
let tn_slot = slot_at_offset(oo_slot, steps);
let tn_taken = splice_take_at(tn_slot);
let x = splice_take_at(oo_slot);
*oo_slot = tn_taken;
if let Some(tn_ref) = (*oo_slot).as_deref_mut() {
tn_ref.flags |= CLF_MISS;
}
drop(x);
continue; }
po_slot = oo_slot;
oo_slot = &mut (*oo_slot).as_mut().unwrap().next;
pn_slot = nn_slot;
nn_slot = &mut (*nn_slot).as_mut().unwrap().next;
continue;
}
if !o_new && n_new {
let o_immut: *const crate::ported::zle::comp_h::Cline =
(*oo_slot).as_deref().unwrap();
let n_head: &crate::ported::zle::comp_h::Cline =
(*nn_slot).as_deref().unwrap();
let found = find_node_in_chain(n_head, |t| {
(t.flags & CLF_NEW) == 0
&& {
let mut o_copy = (*o_immut).clone();
cmp_anchors(&mut o_copy, t, 0) != 0
}
});
if let Some(steps) = found {
if let Some(o_ref) = (*oo_slot).as_deref_mut() {
let of = o_ref.flags & CLF_MISS;
o_ref.flags = (o_ref.flags & !CLF_MISS) | of | CLF_MISS;
}
let tn_slot = slot_at_offset(nn_slot, steps);
let tn_taken = splice_take_at(tn_slot);
*nn_slot = tn_taken;
continue;
}
po_slot = oo_slot;
oo_slot = &mut (*oo_slot).as_mut().unwrap().next;
pn_slot = nn_slot;
nn_slot = &mut (*nn_slot).as_mut().unwrap().next;
continue;
}
let mask = CLF_SUF | CLF_MID;
if (o_flags & mask) != (n_flags & mask) {
let o_immut: *const crate::ported::zle::comp_h::Cline =
(*oo_slot).as_deref().unwrap();
let n_head_im: &crate::ported::zle::comp_h::Cline =
(*nn_slot).as_deref().unwrap();
let o_mask = (*o_immut).flags & mask;
let found_n = find_node_in_chain(n_head_im, |t| {
(t.flags & mask) == o_mask
&& {
let mut o_copy = (*o_immut).clone();
cmp_anchors(&mut o_copy, t, 1) != 0
}
});
if let Some(steps) = found_n {
let tn_slot = slot_at_offset(nn_slot, steps);
let tn_taken = splice_take_at(tn_slot);
*nn_slot = tn_taken;
continue;
}
let n_immut_2: *const crate::ported::zle::comp_h::Cline =
(*nn_slot).as_deref().unwrap();
let o_head_im: &crate::ported::zle::comp_h::Cline =
(*oo_slot).as_deref().unwrap();
let n_mask = (*n_immut_2).flags & mask;
let found_o = find_node_in_chain(o_head_im, |t| {
(t.flags & mask) == n_mask
&& {
let mut t_copy = t.clone();
cmp_anchors(&mut t_copy, &*n_immut_2, 1) != 0
}
});
if let Some(steps) = found_o {
let tn_slot = slot_at_offset(oo_slot, steps);
let tn_taken = splice_take_at(tn_slot);
*oo_slot = None;
*oo_slot = tn_taken;
continue;
}
if (o_flags & CLF_MID) != 0 {
if let Some(o_ref) = (*oo_slot).as_deref_mut() {
let n_suf_bit = n_flags & CLF_SUF;
o_ref.flags = (o_ref.flags & !CLF_MID) | n_suf_bit;
if n_suf_bit != 0 {
o_ref.prefix = None;
} else {
o_ref.suffix = None;
}
}
}
break; }
let needs_skip_scan = (o_flags & CLF_MID) == 0 && {
let o_mut = (*oo_slot).as_deref_mut().unwrap();
let n_im = (*nn_slot).as_deref().unwrap();
cmp_anchors(o_mut, n_im, 1) == 0
};
if needs_skip_scan {
let n_head_im: &crate::ported::zle::comp_h::Cline =
(*nn_slot).as_deref().unwrap();
let o_head_im: &crate::ported::zle::comp_h::Cline =
(*oo_slot).as_deref().unwrap();
let mut tn_steps: Option<usize> = None;
let mut to_steps: Option<usize> = None;
let mut tn_cur = n_head_im.next.as_deref();
let mut tn_idx = 1usize;
'scan: while let Some(tn) = tn_cur {
if (tn.flags & CLF_NEW) == 0 && (tn.flags & CLF_SKIP) != 0 {
let mut to_cur = o_head_im.next.as_deref();
let mut to_idx = 1usize;
while let Some(to) = to_cur {
if (to.flags & CLF_NEW) == 0
&& (to.flags & CLF_SKIP) != 0
&& {
let mut tn_copy = tn.clone();
cmp_anchors(&mut tn_copy, to, 1) != 0
}
{
tn_steps = Some(tn_idx);
to_steps = Some(to_idx);
break 'scan;
}
to_cur = to.next.as_deref();
to_idx += 1;
}
}
tn_cur = tn.next.as_deref();
tn_idx += 1;
}
if let (Some(tn_s), Some(to_s)) = (tn_steps, to_steps) {
let to_slot = slot_at_offset(oo_slot, to_s);
let to_taken = splice_take_at(to_slot);
*oo_slot = None;
*oo_slot = to_taken;
if let Some(o_ref) = (*oo_slot).as_deref_mut() {
o_ref.flags |= CLF_MISS;
}
let tn_slot = slot_at_offset(nn_slot, tn_s);
let tn_taken = splice_take_at(tn_slot);
*nn_slot = tn_taken;
po_slot = oo_slot;
oo_slot = &mut (*oo_slot).as_mut().unwrap().next;
pn_slot = nn_slot;
nn_slot = &mut (*nn_slot).as_mut().unwrap().next;
continue;
}
let n_head_im: &crate::ported::zle::comp_h::Cline =
(*nn_slot).as_deref().unwrap();
let n_ptr: *const crate::ported::zle::comp_h::Cline = n_head_im;
let o_head_im: &crate::ported::zle::comp_h::Cline =
(*oo_slot).as_deref().unwrap();
let to_idx_o = find_node_in_chain(o_head_im, |t| {
(t.flags & CLF_SKIP) != 0
&& {
let mut t_copy = t.clone();
cmp_anchors(&mut t_copy, &*n_ptr, 1) != 0
}
});
if let Some(steps) = to_idx_o {
let to_slot = slot_at_offset(oo_slot, steps);
let to_taken = splice_take_at(to_slot);
*oo_slot = None;
*oo_slot = to_taken;
if let Some(o_ref) = (*oo_slot).as_deref_mut() {
o_ref.flags |= CLF_MISS;
}
continue;
}
let n_head_im2: &crate::ported::zle::comp_h::Cline =
(*nn_slot).as_deref().unwrap();
let o_head_im2: &crate::ported::zle::comp_h::Cline =
(*oo_slot).as_deref().unwrap();
let o_new_bit = o_head_im2.flags & CLF_NEW;
let o_ptr2: *const crate::ported::zle::comp_h::Cline = o_head_im2;
let tn_idx_n = {
let mut found: Option<usize> = None;
let mut cur = Some(n_head_im2);
let mut idx = 0usize;
while let Some(tn) = cur {
if (tn.flags & CLF_NEW) == o_new_bit
&& {
let mut tn_copy = tn.clone();
cmp_anchors(&mut tn_copy, &*o_ptr2, 1) != 0
}
{
found = Some(idx);
break;
}
cur = tn.next.as_deref();
idx += 1;
}
found
};
if let Some(steps) = tn_idx_n {
if let Some(o_ref) = (*oo_slot).as_deref_mut() {
o_ref.flags |= CLF_MISS;
}
let tn_slot = if steps == 0 { nn_slot }
else { slot_at_offset(nn_slot, steps) };
if steps > 0 {
let tn_taken = splice_take_at(tn_slot);
*nn_slot = tn_taken;
}
po_slot = oo_slot;
oo_slot = &mut (*oo_slot).as_mut().unwrap().next;
pn_slot = nn_slot;
nn_slot = &mut (*nn_slot).as_mut().unwrap().next;
continue;
}
if (o_flags & CLF_SUF) != 0 {
break;
}
if let Some(o_ref) = (*oo_slot).as_deref_mut() {
o_ref.word = None;
o_ref.line = None;
o_ref.orig = None;
o_ref.wlen = 0;
o_ref.next = None;
o_ref.flags |= CLF_MISS;
}
break;
}
{
let o_ref = (*oo_slot).as_deref_mut().unwrap();
let n_ref = (*nn_slot).as_deref().unwrap();
if o_ref.orig.is_none() && o_ref.olen == 0 { o_ref.orig = n_ref.orig.clone();
o_ref.olen = n_ref.olen;
}
if n_ref.min < o_ref.min { o_ref.min = n_ref.min; } if n_ref.max > o_ref.max { o_ref.max = n_ref.max; } let is_mid = (o_ref.flags & CLF_MID) != 0;
let is_suf = (o_ref.flags & CLF_SUF) != 0;
let n_mut_ptr: *mut crate::ported::zle::comp_h::Cline =
(*nn_slot).as_mut().unwrap().as_mut();
if is_mid { join_mid(o_ref, &mut *n_mut_ptr);
} else { join_psfx(o_ref, &mut *n_mut_ptr, None, None,
if is_suf { 1 } else { 0 });
}
}
po_slot = oo_slot;
oo_slot = &mut (*oo_slot).as_mut().unwrap().next;
pn_slot = nn_slot;
nn_slot = &mut (*nn_slot).as_mut().unwrap().next;
}
if (*oo_slot).is_some() {
*oo_slot = None;
}
let _ = (po_slot, pn_slot, CLF_MATCHED, CLF_JOIN);
drop(nn);
}
oo }
pub static MATCHBUF: OnceLock<Mutex<String>> = OnceLock::new();
pub static MATCHPARTS: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>> = OnceLock::new();
pub static MATCHSUBS: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>> = OnceLock::new();
pub static MATCHLASTPART: std::sync::OnceLock<std::sync::Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>>
= std::sync::OnceLock::new();
pub static MATCHBUFADDED: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub static MATCHLASTSUB: std::sync::OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>>
= std::sync::OnceLock::new();
fn patmatchrange(s: Option<&[u8]>, c: u32, mut indp: Option<&mut u32>,
mtp: Option<&mut i32>) -> bool
{
use crate::ported::zsh_h::{PP_LOWER, PP_RANGE, PP_UPPER};
let Some(bytes) = s else { return false; };
let pp_range_marker = (0x80u8).wrapping_add(PP_RANGE as u8);
let pp_lower_marker = (0x80u8).wrapping_add(PP_LOWER as u8);
let pp_upper_marker = (0x80u8).wrapping_add(PP_UPPER as u8);
let mut idx: u32 = 0;
let mut i = 0usize;
let mut mtp_dest: Option<&mut i32> = mtp;
while i < bytes.len() {
let b = bytes[i];
if b == pp_range_marker { if i + 2 >= bytes.len() { break; }
let r1 = bytes[i + 1] as u32;
let r2 = bytes[i + 2] as u32;
if c >= r1 && c <= r2 {
if let Some(out) = indp.as_deref_mut() { *out = idx; }
return true;
}
idx += 1;
i += 3;
} else if b >= 0x80 {
let is_lower = b == pp_lower_marker;
let is_upper = b == pp_upper_marker;
let matched = if is_lower {
c < 256 && (c as u8).is_ascii_lowercase()
} else if is_upper {
c < 256 && (c as u8).is_ascii_uppercase()
} else {
false
};
if matched {
if let Some(out) = indp.as_deref_mut() { *out = idx; }
if let Some(out) = mtp_dest.as_deref_mut() {
*out = (b as i32) - 0x80;
}
return true;
}
idx += 1;
i += 1;
} else {
if c == b as u32 {
if let Some(out) = indp.as_deref_mut() { *out = idx; }
return true;
}
idx += 1;
i += 1;
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pattern_match_equivalence_case_cross() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
use crate::ported::zle::comp_h::{Cpattern, CPAT_EQUIV};
let lp = Cpattern { tp: CPAT_EQUIV, str: Some(b"ab".to_vec()), chr: 0, next: None };
let r = pattern_match_equivalence(&lp, 1, 0, b'A' as u32);
assert_eq!(r, b'a' as u32);
}
use crate::ported::zle::comp_h::{
CLF_LINE, CLF_MATCHED, CLF_SUF, CMF_LEFT, CMF_RIGHT, CPAT_CCLASS, CPAT_CHAR, CPAT_NCLASS,
Cline, Cmatcher, Cpattern,
};
fn cpat_char(ch: u32) -> Cpattern {
Cpattern {
tp: CPAT_CHAR,
chr: ch,
..Default::default()
}
}
fn cpat_class(s: &str) -> Cpattern {
Cpattern {
tp: CPAT_CCLASS,
str: Some(s.as_bytes().to_vec()),
..Default::default()
}
}
#[test]
fn cpatterns_same_chr_match() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let b = cpat_char('a' as u32);
assert!(cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_chr_mismatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let b = cpat_char('b' as u32);
assert!(!cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_tp_mismatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let b = Cpattern {
tp: CPAT_NCLASS,
str: Some(b"a".to_vec()),
..Default::default()
};
assert!(!cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_class_match() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_class("a-z");
let b = cpat_class("a-z");
assert!(cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_length_mismatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let mut a_chain = a.clone();
a_chain.next = Some(Box::new(cpat_char('b' as u32)));
let b = cpat_char('a' as u32);
assert!(!cpatterns_same(Some(&a_chain), Some(&b)));
}
#[test]
fn cpatterns_same_both_empty() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert!(cpatterns_same(None, None));
}
#[test]
fn cmatchers_same_pointer_eq() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let m = Cmatcher::default();
assert!(cmatchers_same(&m, &m));
}
#[test]
fn cmatchers_same_flags_diff() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = Cmatcher { flags: 0, ..Default::default() };
let b = Cmatcher { flags: 1, ..Default::default() };
assert!(!cmatchers_same(&a, &b));
}
#[test]
fn cmatchers_same_anchor_lengths() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = Cmatcher {
flags: CMF_LEFT,
lalen: 2,
..Default::default()
};
let b = Cmatcher {
flags: CMF_LEFT,
lalen: 3,
..Default::default()
};
assert!(!cmatchers_same(&a, &b));
let a = Cmatcher {
flags: CMF_RIGHT,
ralen: 1,
..Default::default()
};
let b = Cmatcher {
flags: CMF_RIGHT,
ralen: 1,
..Default::default()
};
assert!(cmatchers_same(&a, &b));
}
#[test]
fn cline_sublen_simple() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let l = Cline {
flags: CLF_LINE,
llen: 5,
wlen: 999,
..Default::default()
};
assert_eq!(cline_sublen(&l), 5);
}
#[test]
fn cline_sublen_with_olen() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let l = Cline {
flags: 0,
llen: 0,
wlen: 3,
olen: 7,
..Default::default()
};
assert_eq!(cline_sublen(&l), 10);
}
#[test]
fn cline_sublen_with_prefix() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let pre = Cline {
flags: CLF_LINE,
llen: 4,
..Default::default()
};
let l = Cline {
flags: 0,
wlen: 2,
olen: 99, prefix: Some(Box::new(pre)),
..Default::default()
};
assert_eq!(cline_sublen(&l), 6);
}
#[test]
fn cline_sublen_clf_suf() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let suf = Cline {
flags: CLF_LINE,
llen: 3,
..Default::default()
};
let l = Cline {
flags: CLF_SUF,
wlen: 1,
olen: 99,
suffix: Some(Box::new(suf)),
..Default::default()
};
assert_eq!(cline_sublen(&l), 4);
}
#[test]
fn cline_setlens_propagates() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut head: Option<Box<Cline>> = Some(Box::new(Cline {
flags: CLF_LINE,
llen: 5,
next: Some(Box::new(Cline {
flags: CLF_LINE,
llen: 3,
..Default::default()
})),
..Default::default()
}));
cline_setlens(&mut head, 1);
let h = head.as_ref().unwrap();
assert_eq!(h.min, 5);
assert_eq!(h.max, 5);
let n = h.next.as_ref().unwrap();
assert_eq!(n.min, 3);
assert_eq!(n.max, 3);
}
#[test]
fn cline_matched_sets_flag_recursively() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut head: Option<Box<Cline>> = Some(Box::new(Cline {
prefix: Some(Box::new(Cline::default())),
suffix: Some(Box::new(Cline::default())),
next: Some(Box::new(Cline::default())),
..Default::default()
}));
cline_matched(&mut head);
let h = head.as_ref().unwrap();
assert!(h.flags & CLF_MATCHED != 0);
assert!(h.prefix.as_ref().unwrap().flags & CLF_MATCHED != 0);
assert!(h.suffix.as_ref().unwrap().flags & CLF_MATCHED != 0);
assert!(h.next.as_ref().unwrap().flags & CLF_MATCHED != 0);
}
#[test]
fn revert_cline_reverses_chain() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let head = Some(Box::new(Cline {
llen: 1,
next: Some(Box::new(Cline {
llen: 2,
next: Some(Box::new(Cline {
llen: 3,
..Default::default()
})),
..Default::default()
})),
..Default::default()
}));
let r = revert_cline(head);
let n = r.as_ref().unwrap();
assert_eq!(n.llen, 3);
let n = n.next.as_ref().unwrap();
assert_eq!(n.llen, 2);
let n = n.next.as_ref().unwrap();
assert_eq!(n.llen, 1);
assert!(n.next.is_none());
}
#[test]
fn cp_cline_shallow() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let src = Cline {
llen: 7,
wlen: 9,
next: Some(Box::new(Cline {
llen: 11,
..Default::default()
})),
..Default::default()
};
let dup = cp_cline(Some(&src), 0);
let n = dup.as_ref().unwrap();
assert_eq!(n.llen, 7);
assert_eq!(n.wlen, 9);
let n = n.next.as_ref().unwrap();
assert_eq!(n.llen, 11);
}
#[test]
fn start_match_clears_globals() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
MATCHBUF
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.push_str("garbage");
*MATCHPARTS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap() = Some(Box::new(Cline::default()));
start_match();
assert!(MATCHBUF.get().unwrap().lock().unwrap().is_empty());
assert!(MATCHPARTS.get().unwrap().lock().unwrap().is_none());
assert!(MATCHSUBS.get().unwrap().lock().unwrap().is_none());
}
#[test]
fn abort_match_drops_lists() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
*MATCHPARTS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap() = Some(Box::new(Cline::default()));
*MATCHSUBS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap() = Some(Box::new(Cline::default()));
abort_match();
assert!(MATCHPARTS.get().unwrap().lock().unwrap().is_none());
assert!(MATCHSUBS.get().unwrap().lock().unwrap().is_none());
}
#[test]
fn pattern_match_equivalence_upper_to_lower() {
use crate::ported::zsh_h::{PP_LOWER, PP_UPPER};
use crate::ported::zle::comp_h::CPAT_EQUIV;
let _g = crate::ported::zle::zle_main::zle_test_setup();
let lp = Cpattern {
tp: CPAT_EQUIV,
str: Some(vec![(0x80u8).wrapping_add(PP_LOWER as u8)]),
chr: 0,
next: None,
};
let r = pattern_match_equivalence(&lp, 1, PP_UPPER, b'A' as u32);
assert_eq!(r, b'a' as u32);
}
#[test]
fn bld_line_cpat_char_emits_literal() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let m = Cmatcher {
line: Some(Box::new(cpat_char('x' as u32))),
..Default::default()
};
let mut line: Vec<char> = Vec::new();
let n = bld_line(&m, &mut line, "", "abc", 1, 0);
assert_eq!(n, 1);
assert_eq!(line, vec!['x']);
}
#[test]
fn bld_line_cpat_any_emits_word_char() {
use crate::ported::zle::comp_h::CPAT_ANY;
let _g = crate::ported::zle::zle_main::zle_test_setup();
let m = Cmatcher {
line: Some(Box::new(Cpattern {
tp: CPAT_ANY, ..Default::default()
})),
..Default::default()
};
let mut line: Vec<char> = Vec::new();
let n = bld_line(&m, &mut line, "", "abc", 1, 0);
assert_eq!(n, 1);
assert_eq!(line, vec!['a'], "CPAT_ANY copies the word char");
}
#[test]
fn match_str_exact_char_skip_full_match() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let r = match_str("abc", "abc", None, 0, None, 0, 0, 0);
assert_eq!(r, 3, "full literal match returns iw=3");
}
#[test]
fn match_parts_truncates_and_matches() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
if let Ok(mut g) = crate::ported::zle::compcore::mstack
.get_or_init(|| std::sync::Mutex::new(None))
.lock()
{
*g = None;
}
let r = match_parts("abcXYZ", "abcdef", 3, 0);
assert_eq!(r, 1, "first 3 chars match exactly (test=1 → 1)");
}
#[test]
fn comp_match_exact_prefix_match() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
if let Ok(mut g) = crate::ported::zle::compcore::mstack
.get_or_init(|| std::sync::Mutex::new(None))
.lock()
{
*g = None;
}
let mut clp: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
let mut exact = 99i32;
let r = comp_match("hello", "", "hello", None,
Some(&mut clp), 0, None, 0, None, 0, &mut exact);
assert!(r.is_some(), "literal prefix match succeeds");
assert_eq!(exact, 1, "pfx == w → exact=1");
}
#[test]
fn match_str_diverging_returns_neg_one_with_empty_mstack() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
if let Ok(mut g) = crate::ported::zle::compcore::mstack
.get_or_init(|| std::sync::Mutex::new(None))
.lock()
{
*g = None;
}
let r = match_str("abc", "xyz", None, 0, None, 0, 0, 0);
assert_eq!(r, -1, "no matcher can bridge `a` vs `x`");
}
#[test]
fn update_bmatchers_with_empty_mstack_trims_all_entries() {
use crate::ported::zle::comp_h::{Cmlist, Cmatcher};
let _g = crate::ported::zle::zle_main::zle_test_setup();
let matcher = Cmatcher {
refc: 1,
next: None, flags: 0, line: None, llen: 1,
word: None, wlen: 1, left: None, lalen: 0, right: None, ralen: 0,
};
let bm_cell = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None));
*bm_cell.lock().unwrap() = Some(Box::new(Cmlist {
next: None, matcher: Box::new(matcher), str: String::new(),
}));
let ms_cell = crate::ported::zle::compcore::mstack
.get_or_init(|| std::sync::Mutex::new(None));
*ms_cell.lock().unwrap() = None;
update_bmatchers();
assert!(bm_cell.lock().unwrap().is_none(),
"every bmatcher must be trimmed when mstack is empty");
}
#[test]
fn cmatchers_same_pointer_identity_short_circuits() {
use crate::ported::zle::comp_h::Cmatcher;
let m = Cmatcher {
refc: 1, next: None, flags: 0, line: None, llen: 0,
word: None, wlen: 0, left: None, lalen: 0, right: None, ralen: 0,
};
assert!(cmatchers_same(&m, &m));
}
#[test]
fn cmatchers_same_different_flags_compare_unequal() {
use crate::ported::zle::comp_h::Cmatcher;
let a = Cmatcher {
refc: 1, next: None, flags: 0, line: None, llen: 0,
word: None, wlen: 0, left: None, lalen: 0, right: None, ralen: 0,
};
let b = Cmatcher {
refc: 1, next: None,
flags: crate::ported::zle::comp_h::CMF_LEFT,
line: None, llen: 0,
word: None, wlen: 0, left: None, lalen: 0, right: None, ralen: 0,
};
assert!(!cmatchers_same(&a, &b));
}
#[test]
fn cmatchers_same_different_lengths_compare_unequal() {
use crate::ported::zle::comp_h::Cmatcher;
let a = Cmatcher {
refc: 1, next: None, flags: 0, line: None, llen: 1,
word: None, wlen: 1, left: None, lalen: 0, right: None, ralen: 0,
};
let b = Cmatcher {
refc: 1, next: None, flags: 0, line: None, llen: 2,
word: None, wlen: 1, left: None, lalen: 0, right: None, ralen: 0,
};
assert!(!cmatchers_same(&a, &b), "differing llen must NOT be equal");
let c = Cmatcher {
refc: 1, next: None, flags: 0, line: None, llen: 1,
word: None, wlen: 5, left: None, lalen: 0, right: None, ralen: 0,
};
assert!(!cmatchers_same(&a, &c), "differing wlen must NOT be equal");
}
}