diff --git a/src/parser/shell_expand/mod.rs b/src/parser/shell_expand/mod.rs index fcc483e9212e3155a6cbf25c6f479a745123d617..f549ccfe2cdd42c3c1095a9ee42cc38167d91b95 100644 --- a/src/parser/shell_expand/mod.rs +++ b/src/parser/shell_expand/mod.rs @@ -5,12 +5,10 @@ use self::unicode_segmentation::UnicodeSegmentation; mod braces; -mod process; mod ranges; mod words; use self::braces::BraceToken; -use self::process::{CommandExpander, CommandToken}; use self::ranges::parse_range; use self::words::{WordIterator, WordToken}; @@ -28,18 +26,16 @@ pub struct ExpanderFunctions<'f> { fn expand_process(current: &mut String, command: &str, quoted: bool, index: Index, expand_func: &ExpanderFunctions) { - let mut expanded = String::with_capacity(command.len()); - for token in CommandExpander::new(command) { - match token { - CommandToken::Normal(string) => expanded.push_str(string), - CommandToken::Variable(var) => { - if let Some(result) = (expand_func.variable)(var, quoted) { - expanded.push_str(&result); - } - } - } + let mut tokens = Vec::new(); + let mut contains_brace = false; + + for token in WordIterator::new(command, false) { + if let WordToken::Brace(_) = token { contains_brace = true; } + tokens.push(token); } + let expanded = expand_tokens(tokens, expand_func, false, contains_brace).join(" "); + if let Some(result) = (expand_func.command)(&expanded, quoted) { slice_string(current, &result, index); } @@ -123,25 +119,31 @@ fn slice_string(output: &mut String, expanded: &str, index: Index) { } } -#[allow(cyclomatic_complexity)] /// Performs shell expansions to an input string, efficiently returning the final expanded form. /// Shells must provide their own batteries for expanding tilde and variable words. pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_quoting: bool) -> Vec<String> { - let mut expanded_words = Vec::new(); - let mut output = String::new(); let mut token_buffer = Vec::new(); let mut contains_brace = false; - for word in WordIterator::new(original) { + for word in WordIterator::new(original, true) { if let WordToken::Brace(_) = word { contains_brace = true; } token_buffer.push(word); } + expand_tokens(token_buffer, expand_func, reverse_quoting, contains_brace) +} + +#[allow(cyclomatic_complexity)] +fn expand_tokens(mut token_buffer: Vec<WordToken>, expand_func: &ExpanderFunctions, + reverse_quoting: bool, contains_brace: bool) -> Vec<String> +{ + let mut output = String::new(); + let mut expanded_words = Vec::new(); + if !token_buffer.is_empty() { if contains_brace { let mut tokens: Vec<BraceToken> = Vec::new(); let mut expanders: Vec<Vec<String>> = Vec::new(); - let mut current = String::new(); for word in token_buffer.drain(..) { match word { @@ -150,21 +152,21 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu Index::None => (), Index::All => { let expanded = array_expand(&elements, expand_func); - current.push_str(&expanded.join(" ")); + output.push_str(&expanded.join(" ")); }, Index::ID(id) => { let expanded = array_nth(&elements, expand_func, id); - current.push_str(&expanded); + output.push_str(&expanded); }, Index::Range(start, end) => { let expanded = array_range(&elements, expand_func, start, end); - current.push_str(&expanded.join(" ")); + output.push_str(&expanded.join(" ")); } }; }, WordToken::ArrayVariable(array, _, index) => { if let Some(array) = (expand_func.array)(array, index) { - current.push_str(&array.join(" ")); + output.push_str(&array.join(" ")); } }, WordToken::ArrayProcess(command, quoted, index) => { @@ -175,12 +177,12 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu let mut temp = String::new(); expand_process(&mut temp, command, quoted, Index::All, expand_func); let temp = temp.split_whitespace().collect::<Vec<&str>>(); - current.push_str(&temp.join(" ")); + output.push_str(&temp.join(" ")); }, Index::ID(id) => { let mut temp = String::new(); expand_process(&mut temp, command, quoted, Index::All, expand_func); - current.push_str(temp.split_whitespace().nth(id).unwrap_or_default()); + output.push_str(temp.split_whitespace().nth(id).unwrap_or_default()); }, Index::Range(start, end) => { let mut temp = String::new(); @@ -192,18 +194,18 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu IndexPosition::CatchAll => temp.split_whitespace() .skip(start).collect::<Vec<&str>>() }; - current.push_str(&temp.join(" ")); + output.push_str(&temp.join(" ")); } } }, WordToken::ArrayMethod(array_method) => { - array_method.handle(&mut current, expand_func); + array_method.handle(&mut output, expand_func); }, WordToken::StringMethod(method, variable, pattern, index) => { let pattern = &expand_string(pattern, expand_func, false).join(" "); match method { "join" => if let Some(array) = (expand_func.array)(variable, Index::All) { - slice_string(&mut current, &array.join(pattern), index); + slice_string(&mut output, &array.join(pattern), index); }, _ => { let stderr = io::stderr(); @@ -213,16 +215,16 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu } }, WordToken::Brace(nodes) => - expand_brace(&mut current, &mut expanders, &mut tokens, nodes, expand_func, reverse_quoting), - WordToken::Normal(text) => current.push_str(text), + expand_brace(&mut output, &mut expanders, &mut tokens, nodes, expand_func, reverse_quoting), + WordToken::Normal(text) => output.push_str(text), WordToken::Whitespace(_) => unreachable!(), - WordToken::Tilde(text) => current.push_str(match (expand_func.tilde)(text) { + WordToken::Tilde(text) => output.push_str(match (expand_func.tilde)(text) { Some(ref expanded) => expanded, None => text, }), WordToken::Process(command, quoted, index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; - expand_process(&mut current, command, quoted, index, expand_func); + expand_process(&mut output, command, quoted, index, expand_func); }, WordToken::Variable(text, quoted, index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; @@ -231,16 +233,16 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu None => continue }; - slice_string(&mut current, &expanded, index); + slice_string(&mut output, &expanded, index); }, } } if expanders.is_empty() { - expanded_words.push(current); + expanded_words.push(output); } else { - if !current.is_empty() { - tokens.push(BraceToken::Normal(current)); + if !output.is_empty() { + tokens.push(BraceToken::Normal(output)); } for word in braces::expand_braces(&tokens, expanders) { expanded_words.push(word); diff --git a/src/parser/shell_expand/process.rs b/src/parser/shell_expand/process.rs deleted file mode 100644 index 09bb1cd5efc46379cc0065f45097d432115ca8b1..0000000000000000000000000000000000000000 --- a/src/parser/shell_expand/process.rs +++ /dev/null @@ -1,92 +0,0 @@ -// TODO: Also Expand Arrays - -/// Bit flags used by `VariableIterator`'s and `CommandExpander`'s flags fields. -const BACK: u8 = 1; -const VAR_FOUND: u8 = 2; -const SQUOTE: u8 = 4; - -/// Commands need to have variables expanded before they are submitted. This custom `Iterator` structure is -/// responsible for slicing the variables from within commands so that they can be expanded beforehand. -pub struct CommandExpander<'a> { - data: &'a str, - read: usize, - flags: u8, -} - -impl<'a> CommandExpander<'a> { - pub fn new(data: &'a str) -> CommandExpander<'a> { - CommandExpander { data: data, read: 0, flags: 0 } - } -} - -pub enum CommandToken<'a> { - Normal(&'a str), - Variable(&'a str), -} - -impl<'a> Iterator for CommandExpander<'a> { - type Item = CommandToken<'a>; - - fn next(&mut self) -> Option<CommandToken<'a>> { - let start = self.read; - let mut iterator = self.data.bytes().skip(self.read); - - while let Some(character) = iterator.next() { - self.read += 1; - match character { - b'\\' => { self.flags ^= BACK; continue }, - b'\'' if self.flags & BACK == 0 => self.flags ^= SQUOTE, - b'$' if self.flags & (VAR_FOUND + BACK + SQUOTE) == 0 => { - if let Some(character) = self.data.bytes().nth(self.read) { - if character == b'(' { continue } - } - - self.flags |= VAR_FOUND; - if start != self.read { - return Some(CommandToken::Normal(&self.data[start..self.read-1])); - } - }, - _ if self.flags & VAR_FOUND != 0 => { - self.flags ^= VAR_FOUND; - if character == b'{' { - // Slice the braced variable from the command string. - while let Some(character) = iterator.next() { - self.read += 1; - match character { - b'\\' => { self.flags ^= BACK; self.read += 1; continue }, - b'}' if self.flags & BACK == 0 => { - return Some(CommandToken::Variable(&self.data[start+1..self.read-1])) - }, - _ => () - } - self.flags &= 255 ^ BACK; - } - } else { - // Slice the non-braced variable from the command string. - for character in iterator { - match character { - b'$' => { - self.flags |= VAR_FOUND; - self.read += 1; - return Some(CommandToken::Variable(&self.data[start..self.read-1])) - } - b'{' | b'}' | b'(' | b')' | b' ' | b':' | b',' | b'@' | b'#' | b'\'' | b'"' => - return Some(CommandToken::Variable(&self.data[start..self.read])), - _ => () - } - self.read += 1; - } - - return Some(CommandToken::Variable(&self.data[start..self.read])); - } - }, - _ => () - } - self.flags &= 255 ^ BACK; - } - - if start == self.read { None } else { Some(CommandToken::Normal(&self.data[start..])) } - } -} - -// TODO: Write Tests diff --git a/src/parser/shell_expand/words.rs b/src/parser/shell_expand/words.rs index 7df41bba273620ba31e233f616fa1d07abc60b22..069aeb5c253c9d1137a30b3b2732abb69ea0e234 100644 --- a/src/parser/shell_expand/words.rs +++ b/src/parser/shell_expand/words.rs @@ -14,6 +14,7 @@ use super::ranges::parse_index_range; const BACKSL: u8 = 1; const SQUOTE: u8 = 2; const DQUOTE: u8 = 4; +const EXPAND_PROCESSES: u8 = 8; #[derive(Debug, PartialEq, Copy, Clone)] pub enum Index { @@ -217,8 +218,9 @@ pub struct WordIterator<'a> { } impl<'a> WordIterator<'a> { - pub fn new(data: &'a str) -> WordIterator<'a> { - WordIterator { data: data, read: 0, flags: 0 } + pub fn new(data: &'a str, expand_processes: bool) -> WordIterator<'a> { + let flags = if expand_processes { EXPAND_PROCESSES } else { 0 }; + WordIterator { data: data, read: 0, flags: flags } } // Contains the grammar for collecting whitespace characters @@ -679,7 +681,11 @@ impl<'a> Iterator for WordIterator<'a> { match iterator.next() { Some(b'[') => { self.read += 2; - return Some(self.array_process(&mut iterator)); + return if self.flags & EXPAND_PROCESSES != 0 { + Some(self.array_process(&mut iterator)) + } else { + Some(WordToken::Normal(&self.data[start..self.read])) + } }, // Some(b'{') => { // self.read += 2; @@ -695,7 +701,11 @@ impl<'a> Iterator for WordIterator<'a> { match iterator.next() { Some(b'(') => { self.read += 2; - return Some(self.process(&mut iterator)); + return if self.flags & EXPAND_PROCESSES != 0 { + Some(self.process(&mut iterator)) + } else { + Some(WordToken::Normal(&self.data[start..self.read])) + } }, Some(b'{') => { self.read += 2; @@ -760,7 +770,7 @@ mod tests { fn compare(input: &str, expected: Vec<WordToken>) { let mut correct = 0; - for (actual, expected) in WordIterator::new(input).zip(expected.iter()) { + for (actual, expected) in WordIterator::new(input, true).zip(expected.iter()) { assert_eq!(actual, *expected, "{:?} != {:?}", actual, expected); correct += 1; }