#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ShellChar {
pub ch: char,
pub literal: bool,
pub quote_mark: bool,
}
pub fn scan(command: &str) -> Vec<ShellChar> {
let mut out = Vec::with_capacity(command.len());
let mut chars = command.chars();
let mut quote: Option<char> = None;
while let Some(ch) = chars.next() {
let escapes = match quote {
None => ch == '\\',
Some(q) => ch == '\\' && q == '"',
};
if escapes {
out.push(ShellChar {
ch,
literal: true,
quote_mark: false,
});
if let Some(next) = chars.next() {
out.push(ShellChar {
ch: next,
literal: true,
quote_mark: false,
});
}
continue;
}
match quote {
None if ch == '\'' || ch == '"' => {
quote = Some(ch);
out.push(ShellChar {
ch,
literal: false,
quote_mark: true,
});
}
None => out.push(ShellChar {
ch,
literal: false,
quote_mark: false,
}),
Some(q) if ch == q => {
quote = None;
out.push(ShellChar {
ch,
literal: false,
quote_mark: true,
});
}
Some(_) => out.push(ShellChar {
ch,
literal: true,
quote_mark: false,
}),
}
}
out
}
pub fn blank_quoted(command: &str) -> String {
scan(command)
.into_iter()
.map(|c| if c.literal || c.quote_mark { ' ' } else { c.ch })
.collect()
}