#[derive(Debug, Default)]
pub struct EscapingState {
pub single_quote: bool,
pub double_quote: bool,
pub backslash: bool,
}
impl EscapingState {
pub fn new() -> EscapingState {
EscapingState {
single_quote: false,
double_quote: false,
backslash: false,
}
}
pub fn step(&mut self, ch: char) {
match ch {
'"' => {
if !self.doublequote_escaped() {
self.double_quote = !self.double_quote
}
}
'\'' => {
if !self.singlequote_escaped() {
self.single_quote = !self.single_quote
}
}
_ => {}
}
if self.backslash {
self.backslash = false
} else if ch == '\\' {
self.backslash = true
}
}
pub fn whitespace_escaped(&self) -> bool {
self.single_quote || self.double_quote || self.backslash
}
pub fn doublequote_escaped(&self) -> bool {
self.single_quote || self.backslash
}
pub fn singlequote_escaped(&self) -> bool {
self.double_quote || self.backslash
}
pub fn backslash_escaped(&self) -> bool {
self.double_quote || self.backslash
}
pub fn process(line: &str) -> EscapingState {
let mut state = EscapingState::new();
for ch in line.chars() {
state.step(ch);
}
state
}
}
pub fn split(cmdline: &str) -> Vec<String> {
let mut parts = vec![];
let mut act = String::new();
let mut state = EscapingState::new();
for ch in cmdline.chars() {
if !state.whitespace_escaped() && ch.is_whitespace() {
if !act.is_empty() {
parts.push(act);
act = String::new();
}
continue;
}
match ch {
'"' => {
if state.doublequote_escaped() {
act.push(ch);
}
}
'\'' => {
if state.singlequote_escaped() {
act.push(ch);
}
}
'\\' => {
if state.backslash_escaped() {
act.push(ch);
}
}
ch => act.push(ch),
}
state.step(ch);
}
if !act.is_empty() {
parts.push(act);
}
parts
}
pub fn ends_with_whitespace(text: &str) -> bool {
if let Some(ch) = text.chars().last() {
ch.is_whitespace()
} else {
false
}
}