use super::{ClassifyError, Sink};
use crate::action::AtomicAction;
const OWN_NAMES: &[&str] = &["openmoat", "moat"];
pub(super) fn note(argv: &[String], program: &str, sink: &mut Sink) -> Result<(), ClassifyError> {
if !matches!(program, "pkill" | "killall") {
return Ok(());
}
let patterns = argv[1..].iter().filter(|w| !w.starts_with('-'));
for name in OWN_NAMES {
if patterns.clone().any(|p| could_match(p, name)) {
sink.push(AtomicAction::Shell {
argv: vec!["@kills".to_owned(), (*name).to_owned()],
})?;
}
}
Ok(())
}
fn could_match(pattern: &str, name: &str) -> bool {
if pattern.contains(['(', '|']) {
return true;
}
let mut runs: Vec<String> = Vec::new();
let mut run = String::new();
let mut chars = pattern.chars().peekable();
while let Some(c) = chars.next() {
match c {
'\\' => match chars.next() {
Some(escaped) if !escaped.is_ascii_alphanumeric() => run.push(escaped),
_ => end_run(&mut runs, &mut run),
},
'[' => {
end_run(&mut runs, &mut run);
if chars.peek() == Some(&'^') {
chars.next();
}
if chars.peek() == Some(&']') {
chars.next();
}
for inner in chars.by_ref() {
if inner == ']' {
break;
}
}
}
'*' | '?' | '{' => {
run.pop();
end_run(&mut runs, &mut run);
if c == '{' {
for inner in chars.by_ref() {
if inner == '}' {
break;
}
}
}
}
'.' | '+' | '^' | '$' => end_run(&mut runs, &mut run),
_ => run.push(c),
}
}
end_run(&mut runs, &mut run);
runs.iter().any(|r| r.contains(name)) || runs.iter().all(|r| name.contains(r.as_str()))
}
fn end_run(runs: &mut Vec<String>, run: &mut String) {
if !run.is_empty() {
runs.push(std::mem::take(run));
}
}
#[cfg(test)]
mod tests {
use super::could_match;
#[test]
fn patterns_that_could_stop_openmoat() {
for pattern in [
"moat",
"openmoat",
"open.*",
".",
"o.*t",
"m.at",
"openmoat proxy",
"/usr/local/bin/openmoat",
"^moat$",
"mo+at",
"(open)?moat",
"vite|moat",
"[om]+",
"openx?moat",
"moatx",
] {
assert!(
could_match(pattern, "openmoat") || could_match(pattern, "moat"),
"{pattern}"
);
}
}
#[test]
fn patterns_that_cannot() {
for pattern in [
"latency-compare.py",
"my-server.js",
"node .*vite",
"python3",
"node",
"nginx",
"boat",
"m\\.at",
"mo.t\\.py",
"[a-z]+-server",
] {
assert!(
!could_match(pattern, "openmoat") && !could_match(pattern, "moat"),
"{pattern}"
);
}
}
}