#[derive(Debug)]
enum State {
BetweenArgs,
InArg(bool),
OnQuote,
Backslashes(usize, bool),
}
pub fn next_arg<AddC, ArgVec>(line: &[u16], mut arg: ArgVec, push: AddC) -> (Option<ArgVec>, &[u16])
where
AddC: Fn(&mut ArgVec, u16, bool),
{
use self::State::*;
let mut state = BetweenArgs;
for (i, &cu) in line.iter().enumerate() {
state = match state {
BetweenArgs => match cu {
c if c == u16::from(b' ') => BetweenArgs,
c if c == u16::from(b'"') => InArg(true),
c if c == u16::from(b'\\') => Backslashes(1, false),
c => {
push(&mut arg, c, false);
InArg(false)
},
},
InArg(quoted) => match cu {
c if c == u16::from(b'\\') => Backslashes(1, quoted),
c if quoted && c == u16::from(b'"') => OnQuote,
c if !quoted && c == u16::from(b'"') => InArg(true),
c if !quoted && c == u16::from(b' ') => {
return (Some(arg), &line[i+1..]);
},
c => {
push(&mut arg, c, quoted);
InArg(quoted)
},
},
OnQuote => match cu {
c if c == u16::from(b'"') => {
push(&mut arg, u16::from(b'"'), true);
InArg(false)
},
c if c == u16::from(b' ') => {
return (Some(arg), &line[i+1..]);
},
c => {
push(&mut arg, c, false);
InArg(false)
},
},
Backslashes(count, quoted) => match cu {
c if c == u16::from(b'\\') => Backslashes(count + 1, quoted),
c if c == u16::from(b'"') => {
for _ in 0..count/2 {
push(&mut arg, u16::from(b'\\'), quoted);
}
if count & 1 != 0 {
push(&mut arg, u16::from(b'"'), quoted);
InArg(quoted)
} else if quoted {
return (Some(arg), &line[i+1..]);
} else {
InArg(quoted)
}
},
c => {
for _ in 0..count {
push(&mut arg, u16::from(b'\\'), quoted);
}
push(&mut arg, c, quoted);
InArg(quoted)
},
},
}
}
let arg = match state {
BetweenArgs => None,
OnQuote | InArg(..) => Some(arg),
Backslashes(count, quoted) => {
for _ in 0..count {
push(&mut arg, u16::from(b'\\'), quoted);
}
Some(arg)
},
};
(arg, &line[..0])
}