use std::fmt;
#[must_use]
pub struct CommandLineWParser<'argsline> {
line: &'argsline [u16],
}
impl<'argsline> CommandLineWParser<'argsline> {
#[inline]
#[must_use]
pub fn new(command_line_args_ucs2: &'argsline [u16]) -> Self {
Self {
line: command_line_args_ucs2,
}
}
}
impl<'a> fmt::Debug for CommandLineWParser<'a> {
#[cold]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
String::from_utf16_lossy(self.line).fmt(f)
}
}
#[derive(Debug)]
enum State {
BetweenArgs,
InArg(bool),
OnQuote,
Backslashes(usize, bool),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CharCode {
Quoted(u16),
Unquoted(u16),
}
impl<'argsline> CommandLineWParser<'argsline> {
pub fn accumulate_next<CharacterAccumulator>(&mut self, mut push: CharacterAccumulator) -> bool
where CharacterAccumulator: FnMut(CharCode)
{
use self::State::*;
let mut state = BetweenArgs;
for (i, &cu) in self.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(CharCode::Unquoted(c));
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' ') => {
self.line = &self.line[i+1..];
return true;
},
c => {
push(if quoted { CharCode::Quoted(c) } else { CharCode::Unquoted(c) });
InArg(quoted)
},
},
OnQuote => match cu {
c if c == u16::from(b'"') => {
push(CharCode::Quoted(u16::from(b'"')));
InArg(false)
},
c if c == u16::from(b' ') => {
self.line = &self.line[i+1..];
return true;
},
c => {
push(CharCode::Unquoted(c));
InArg(false)
},
},
Backslashes(count, quoted) => match cu {
c if c == u16::from(b'\\') => Backslashes(count + 1, quoted),
c if c == u16::from(b'"') => {
let b = if quoted { CharCode::Quoted(u16::from(b'\\')) } else { CharCode::Unquoted(u16::from(b'\\')) };
for _ in 0..count/2 {
push(b);
}
if count & 1 != 0 {
let c = u16::from(b'"');
push(if quoted { CharCode::Quoted(c) } else { CharCode::Unquoted(c) });
InArg(quoted)
} else if quoted {
self.line = &self.line[i+1..];
return true;
} else {
InArg(quoted)
}
},
c => {
let b = if quoted { CharCode::Quoted(u16::from(b'\\')) } else { CharCode::Unquoted(u16::from(b'\\')) };
for _ in 0..count {
push(b);
}
push(if quoted { CharCode::Quoted(c) } else { CharCode::Unquoted(c) });
InArg(quoted)
},
},
}
}
let arg = match state {
BetweenArgs => false,
OnQuote | InArg(..) => true,
Backslashes(count, quoted) => {
let b = if quoted { CharCode::Quoted(u16::from(b'\\')) } else { CharCode::Unquoted(u16::from(b'\\')) };
for _ in 0..count {
push(b);
}
true
},
};
self.line = &self.line[..0];
arg
}
}