#![crate_type="lib"]
#![crate_name="cmdline_words_parser"]
#![cfg_attr(feature="no_std", no_std)]
#[cfg(feature="no_std")]
mod std {
pub use core::marker;
pub use core::mem;
pub use core::str;
}
pub trait StrExt
{
type OutSlice: ?Sized + StrExtOut;
fn parse_cmdline_words(&mut self) -> PosixShellWords<Self::OutSlice>;
}
impl StrExt for str
{
type OutSlice = str;
fn parse_cmdline_words(&mut self) -> PosixShellWords<str> {
PosixShellWords::new(unsafe { ::std::mem::transmute(self) })
}
}
impl StrExt for [u8]
{
type OutSlice = [u8];
fn parse_cmdline_words(&mut self) -> PosixShellWords<[u8]> {
PosixShellWords::new(self)
}
}
#[cfg(all(not(feature="no_std"), unix))]
impl StrExt for ::std::ffi::OsStr
{
type OutSlice = ::std::ffi::OsStr;
fn parse_cmdline_words(&mut self) -> PosixShellWords<Self::OutSlice> {
let _: &[u8] = <_ as ::std::os::unix::ffi::OsStrExt>::as_bytes(self);
PosixShellWords::new(unsafe { ::std::mem::transmute(self) })
}
}
#[cfg(not(feature="no_std"))]
impl StrExt for String {
type OutSlice = str;
fn parse_cmdline_words(&mut self) -> PosixShellWords<Self::OutSlice> {
PosixShellWords::new(unsafe { &mut **self.as_mut_vec() })
}
}
pub trait StrExtOut {
fn from_bytes(bytes: &[u8]) -> Option<&Self>;
}
impl StrExtOut for str {
fn from_bytes(bytes: &[u8]) -> Option<&Self> {
::std::str::from_utf8(bytes).ok()
}
}
impl StrExtOut for [u8] {
fn from_bytes(bytes: &[u8]) -> Option<&Self> {
Some(bytes)
}
}
#[cfg(all(not(feature="no_std"), unix))]
impl StrExtOut for ::std::ffi::OsStr {
fn from_bytes(bytes: &[u8]) -> Option<&Self> {
Some( unsafe { ::std::mem::transmute(bytes) } )
}
}
enum PosixEscapeMode
{
Outer,
OuterSlash,
SingleQuote,
SingleQuoteSlash,
DoubleQuote,
DoubleQuoteSlash,
}
fn split_off_front_inplace_mut<'a, T>(slice: &mut &'a mut [T], idx: usize) -> &'a mut [T] {
let (ret, tail) = ::std::mem::replace(slice, &mut []).split_at_mut(idx);
*slice = tail;
ret
}
pub struct PosixShellWords<'a,T:?Sized+StrExtOut>(&'a mut [u8], ::std::marker::PhantomData<T>);
impl<'a, T: ?Sized + StrExtOut> PosixShellWords<'a, T>
{
fn new(input_bytes: &mut [u8]) -> PosixShellWords<T> {
PosixShellWords(input_bytes, ::std::marker::PhantomData::<T>)
}
}
impl<'a, T: ?Sized + StrExtOut + 'a> Iterator for PosixShellWords<'a, T>
{
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
if self.0.len() == 0 {
return None;
}
let mut outpos = 0;
let mut endpos = self.0.len();
let mut mode = PosixEscapeMode::Outer;
for i in 0 .. self.0.len()
{
let byte = self.0[i];
let out = match mode
{
PosixEscapeMode::Outer => match byte
{
b' ' => { endpos = i; break; },
b'\t' | b'\n' | b'\r' => { endpos = i; break; },
b'\\' => {
mode = PosixEscapeMode::OuterSlash;
None
},
b'\'' => {
mode = PosixEscapeMode::SingleQuote;
None
},
b'"' => {
mode = PosixEscapeMode::DoubleQuote;
None
},
v @ _ => Some(v),
},
PosixEscapeMode::OuterSlash => {
mode = PosixEscapeMode::Outer;
match byte
{
v @ b' ' => Some(v),
v @ b'\t' => Some(v),
v @ b'\n' => Some(v),
v @ b'\r' => Some(v),
v @ b'\'' => Some(v),
v @ b'\"' => Some(v),
v @ b'\\' => Some(v),
b'n' => Some(b'\n'),
b'r' => Some(b'\r'),
b't' => Some(b'\t'),
_ => None, }},
PosixEscapeMode::SingleQuote => match byte
{
b'\\' => {
mode = PosixEscapeMode::SingleQuoteSlash;
None
},
b'\'' => {
mode = PosixEscapeMode::Outer;
None
},
v @ _ => Some(v),
},
PosixEscapeMode::SingleQuoteSlash => {
mode = PosixEscapeMode::SingleQuote;
match byte
{
v @ b'\'' => Some(v),
v @ b'\\' => Some(v),
_ => None, }},
PosixEscapeMode::DoubleQuote => match byte
{
b'\\' => {
mode = PosixEscapeMode::DoubleQuoteSlash;
None
},
b'"' => {
mode = PosixEscapeMode::Outer;
None
},
v @ _ => Some(v),
},
PosixEscapeMode::DoubleQuoteSlash => {
mode = PosixEscapeMode::DoubleQuote;
match byte
{
v @ b'\'' => Some(v),
v @ b'\"' => Some(v),
v @ b'\\' => Some(v),
b'n' => Some(b'\n'),
b'r' => Some(b'\r'),
b't' => Some(b'\t'),
_ => None, }},
};
if let Some(b) = out {
if outpos != i {
assert!(outpos < i);
self.0[i] = 0; self.0[outpos] = b;
}
outpos += 1;
}
}
while endpos < self.0.len() && self.0[endpos] == b' ' {
self.0[endpos] = 0;
endpos += 1;
}
let ret = &split_off_front_inplace_mut(&mut self.0, endpos)[..outpos];
Some( T::from_bytes(ret).expect("POSIX Word spliting caused UTF-8 inconsistency") )
}
}