fn to_lower(c: u8) -> u8 {
c.to_ascii_lowercase()
}
fn at(s: &[u8], i: usize) -> u8 {
s.get(i).copied().unwrap_or(0)
}
fn c_str(s: &[u8]) -> &[u8] {
match s.iter().position(|&b| b == 0) {
Some(n) => &s[..n],
None => s,
}
}
pub(crate) fn matches(pattern: &[u8], name: &[u8], ignore_case: bool, stop_at_dir: bool) -> bool {
let sepc = if stop_at_dir { b'/' } else { 0 };
recmatch(c_str(pattern), c_str(name), ignore_case, sepc) == 1
}
fn recmatch(pat: &[u8], s: &[u8], ic: bool, sepc: u8) -> i32 {
let case = |x: u8| if ic { to_lower(x) } else { x };
let mut stack: Vec<Star> = Vec::new();
let (mut pi, mut si) = (0usize, 0usize);
'call: loop {
let r: i32 = 'eval: loop {
let c = at(pat, pi);
pi += 1;
if c == 0 {
break 'eval i32::from(at(s, si) == 0);
}
if c == b'?' {
if at(s, si) != 0 && (sepc == 0 || at(s, si) != sepc) {
si += 1;
continue 'eval;
}
break 'eval 0;
}
if c == b'*' {
if sepc != 0 {
if at(pat, pi) != b'*' {
if at(s, si) != 0 && at(s, si) != sepc {
stack.push(Star {
p: pi,
s: si,
component: true,
});
continue 'eval;
}
match after_component(pat, s, pi, si, sepc) {
None => continue 'eval,
Some(r) => break 'eval r,
}
}
pi += 1;
}
if at(pat, pi) == 0 {
break 'eval 1;
}
let p = &pat[pi..];
if isshexp(p).is_none() {
let rest = &s[si.min(s.len())..];
if rest.len() < p.len() {
break 'eval 0;
}
let srest = &rest[rest.len() - p.len()..];
break 'eval i32::from(if ic {
namecmp(p, srest) == 0
} else {
p == srest
});
}
if at(s, si) != 0 {
stack.push(Star {
p: pi,
s: si,
component: false,
});
continue 'eval;
}
break 'eval 2; }
if c == b'[' {
if at(s, si) == 0 {
break 'eval 0;
}
let r = at(pat, pi) == b'!' || at(pat, pi) == b'^'; if r {
pi += 1;
}
let p = &pat[pi.min(pat.len())..];
let mut q = 0usize;
let mut e = false;
while at(p, q) != 0 {
if e {
e = false;
} else if at(p, q) == b'\\' {
e = true;
} else if at(p, q) == b']' {
break;
}
q += 1;
}
if at(p, q) != b']' {
break 'eval 0;
}
let mut cr: u32 = 0; let mut e = at(p, 0) == b'-';
let cc = u32::from(case(at(s, si)));
let mut hit = false;
'set: for i in 0..q {
let pc = at(p, i);
if !e && pc == b'\\' {
e = true;
} else if !e && pc == b'-' {
cr = u32::from(at(p, i.wrapping_sub(1)));
} else {
if at(p, i + 1) != b'-' {
let mut c = if cr != 0 { cr } else { u32::from(pc) };
while c <= u32::from(pc) {
if u32::from(case(c as u8)) == cc {
hit = true;
break 'set;
}
c += 1;
}
}
cr = 0; e = false;
}
}
if hit == r {
break 'eval 0;
}
pi += q + 1;
si += 1;
continue 'eval;
}
let c = if c == b'\\' {
let n = at(pat, pi);
if n == 0 {
break 'eval 0;
}
pi += 1;
n
} else {
c
};
if at(s, si) != 0 && case(c) == case(at(s, si)) {
si += 1;
continue 'eval;
}
break 'eval 0;
};
if r != 0 {
return r;
}
loop {
let Some(top) = stack.last_mut() else {
return 0;
};
top.s += 1;
let Star {
p,
s: at_s,
component,
} = *top;
if component {
if at(s, at_s) != 0 && at(s, at_s) != sepc {
(pi, si) = (p, at_s);
continue 'call;
}
stack.pop();
match after_component(pat, s, p, at_s, sepc) {
None => {
(pi, si) = (p, at_s);
continue 'call;
}
Some(0) => continue,
Some(r) => return r,
}
}
if at(s, at_s) != 0 {
(pi, si) = (p, at_s);
continue 'call;
}
return 2;
}
}
}
#[derive(Clone, Copy)]
struct Star {
p: usize,
s: usize,
component: bool,
}
fn after_component(pat: &[u8], s: &[u8], pi: usize, si: usize, sepc: u8) -> Option<i32> {
if at(pat, pi) == 0 {
return Some(i32::from(at(s, si) == 0));
}
if at(pat, pi) == sepc || (at(pat, pi) == b'\\' && at(pat, pi + 1) == sepc) {
None
} else {
Some(2)
}
}
fn isshexp(p: &[u8]) -> Option<usize> {
let mut i = 0;
while at(p, i) != 0 {
let c = at(p, i);
if c == b'\\' && at(p, i + 1) != 0 {
i += 1;
} else if c == b'?' || c == b'*' || c == b'[' {
return Some(i);
}
i += 1;
}
None
}
fn namecmp(s1: &[u8], s2: &[u8]) -> i32 {
let mut i = 0;
loop {
let (a, b) = (at(s1, i), at(s2, i));
let d = i32::from(to_lower(a)) - i32::from(to_lower(b));
if d != 0 || a == 0 || b == 0 {
return d;
}
i += 1;
}
}
pub(crate) fn iswild(p: &[u8]) -> bool {
isshexp(c_str(p)).is_some()
}
#[cfg(test)]
mod tests;