diff --git a/ci/run_benchmark.sh b/ci/run_benchmark.sh index bc7b80809073eeea27868187a099eb5f55b95534..703136dc6a2d8d655072fb85742add7d4e2ade68 100755 --- a/ci/run_benchmark.sh +++ b/ci/run_benchmark.sh @@ -30,15 +30,12 @@ for suite in ./target/criterion/*; do for test in $suite/*/*/change/estimates.json; do estimate=$(cat "$test" | jq -r "$JQ_FILTER" -c) - case "$estimate" in - -*);; - *) - inner="<failure message=\"Performance Regressed\" type=\"WARNING\">\ - Performance regressed by $estimate in $test\ - </failure>" - worse=$((worse+1)) - ;; - esac + if echo "$estimate" | grep -Eq '^[0-9]+\.?[0-9]*$'; then + inner="<failure message=\"Performance Regressed\" type=\"WARNING\">\ + Performance regressed by $estimate in $test\ + </failure>" + worse=$((worse+1)) + fi testcases="$testcases<testcase id=\"$(echo "$test" | cut -d'/' -f 6)\" name=\"$(echo "$test" | cut -d'/' -f 6)\">$inner</testcase>" tests=$((tests+1)) done diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs index 7370379a4c5c186d8ee1cfff7bb25e726ac9aae9..97a177469494bee37612d1ea03e756f6c53e791a 100644 --- a/src/lib/parser/shell_expand/mod.rs +++ b/src/lib/parser/shell_expand/mod.rs @@ -56,7 +56,7 @@ pub(crate) trait Expander: Sized { fn expand_process<E: Expander>( current: &mut small::String, command: &str, - selection: Select, + selection: &Select, expander: &E, quoted: bool, ) { @@ -66,7 +66,7 @@ fn expand_process<E: Expander>( } else if quoted { let output: &str = if let Some(pos) = output.rfind(|x| x != '\n') { &output[..=pos] } else { &output }; - slice(current, output, selection) + slice(current, output, &selection) } else { // The following code should produce the same result as // slice(current, @@ -107,7 +107,7 @@ fn expand_process<E: Expander>( slice( current, str::from_utf8_unchecked(&bytes[..left_mut_pos]).trim_end(), - selection, + &selection, ); // `output` is not a valid utf8 string now. // We drop it here to prevent accidental usage afterward. @@ -130,11 +130,7 @@ fn expand_brace<E: Expander>( nodes.iter().flat_map(|node| expand_string_no_glob(node, expand_func, reverse_quoting)) { match parse_range(&word) { - Some(elements) => { - for word in elements { - temp.push(word) - } - } + Some(elements) => temp.extend(elements), None => temp.push(word), } } @@ -153,13 +149,13 @@ fn expand_brace<E: Expander>( fn array_expand<E: Expander>( elements: &[&str], expand_func: &E, - selection: Select, + selection: &Select, ) -> types::Array { match selection { Select::None => types::Array::new(), Select::All => elements.iter().flat_map(|e| expand_string(e, expand_func, false)).collect(), - Select::Index(index) => array_nth(elements, expand_func, index).into_iter().collect(), - Select::Range(range) => array_range(elements, expand_func, range), + Select::Index(index) => array_nth(elements, expand_func, *index).into_iter().collect(), + Select::Range(range) => array_range(elements, expand_func, *range), Select::Key(_) => types::Array::new(), } } @@ -177,27 +173,26 @@ fn array_range<E: Expander>(elements: &[&str], expand_func: &E, range: Range) -> .iter() .flat_map(|e| expand_string(e, expand_func, false)) .collect::<types::Array>(); - let len = expanded.len(); - if let Some((start, length)) = range.bounds(len) { + if let Some((start, length)) = range.bounds(expanded.len()) { expanded.into_iter().skip(start).take(length).collect() } else { types::Array::new() } } -fn slice<S: AsRef<str>>(output: &mut small::String, expanded: S, selection: Select) { +fn slice<S: AsRef<str>>(output: &mut small::String, expanded: S, selection: &Select) { match selection { - Select::None => (), Select::All => output.push_str(expanded.as_ref()), Select::Index(Index::Forward(id)) => { - if let Some(character) = UnicodeSegmentation::graphemes(expanded.as_ref(), true).nth(id) + if let Some(character) = + UnicodeSegmentation::graphemes(expanded.as_ref(), true).nth(*id) { output.push_str(character); } } Select::Index(Index::Backward(id)) => { if let Some(character) = - UnicodeSegmentation::graphemes(expanded.as_ref(), true).rev().nth(id) + UnicodeSegmentation::graphemes(expanded.as_ref(), true).rev().nth(*id) { output.push_str(character); } @@ -209,7 +204,7 @@ fn slice<S: AsRef<str>>(output: &mut small::String, expanded: S, selection: Sele output.push_str(&substring); } } - Select::Key(_) => (), + Select::Key(_) | Select::None => (), } } @@ -223,54 +218,44 @@ pub(crate) fn expand_string<E: Expander>( ) -> types::Array { let mut token_buffer = Vec::new(); let mut contains_brace = false; - let mut word_iterator = WordIterator::new(original, expand_func, true); - - loop { - match word_iterator.next() { - Some(word) => { - match word { - WordToken::Brace(_) => { - contains_brace = true; - token_buffer.push(word); - } - WordToken::ArrayVariable(data, contains_quote, selection) => { - if let Select::Key(key) = selection { - if key.contains(' ') { - for index in key.split(' ') { - let select = index.parse::<Select>().unwrap_or(Select::None); - token_buffer.push(WordToken::ArrayVariable( - data, - contains_quote, - select, - )); - token_buffer.push(WordToken::Whitespace(" ")); - } - token_buffer.pop(); // Pop out the last unneeded whitespace token - } else { - token_buffer.push(WordToken::ArrayVariable( - data, - contains_quote, - Select::Key(key), - )); - } - } else { + + for word in WordIterator::new(original, expand_func, true) { + match word { + WordToken::Brace(_) => { + contains_brace = true; + token_buffer.push(word); + } + WordToken::ArrayVariable(data, contains_quote, selection) => { + if let Select::Key(key) = selection { + if key.contains(' ') { + for index in key.split(' ') { + let select = index.parse::<Select>().unwrap_or(Select::None); token_buffer.push(WordToken::ArrayVariable( data, contains_quote, - selection, + select, )); + token_buffer.push(WordToken::Whitespace(" ")); } + token_buffer.pop(); // Pop out the last unneeded whitespace token + } else { + token_buffer.push(WordToken::ArrayVariable( + data, + contains_quote, + Select::Key(key), + )); } - _ => token_buffer.push(word), + } else { + token_buffer.push(WordToken::ArrayVariable(data, contains_quote, selection)); } } - None if original.is_empty() => { - token_buffer.push(WordToken::Normal("".into(), true, false)); - break; - } - None => break, + _ => token_buffer.push(word), } } + + if original.is_empty() { + token_buffer.push(WordToken::Normal("".into(), true, false)); + } expand_tokens(&token_buffer, expand_func, reverse_quoting, contains_brace) } @@ -281,22 +266,15 @@ fn expand_string_no_glob<E: Expander>( ) -> types::Array { let mut token_buffer = Vec::new(); let mut contains_brace = false; - let mut word_iterator = WordIterator::new(original, expand_func, false); - loop { - match word_iterator.next() { - Some(word) => { - if let WordToken::Brace(_) = word { - contains_brace = true; - } - token_buffer.push(word); - } - None if original.is_empty() => { - token_buffer.push(WordToken::Normal("".into(), true, false)); - break; - } - None => break, + for word in WordIterator::new(original, expand_func, false) { + if let WordToken::Brace(_) = word { + contains_brace = true; } + token_buffer.push(word); + } + if original.is_empty() { + token_buffer.push(WordToken::Normal("".into(), true, false)); } expand_tokens(&token_buffer, expand_func, reverse_quoting, contains_brace) } @@ -314,7 +292,7 @@ fn expand_braces<E: Expander>( for word in word_tokens { match *word { WordToken::Array(ref elements, ref index) => { - output.push_str(&array_expand(elements, expand_func, index.clone()).join(" ")); + output.push_str(&array_expand(elements, expand_func, &index).join(" ")); } WordToken::ArrayVariable(array, _, ref index) => { if let Some(array) = expand_func.array(array, index.clone()) { @@ -322,26 +300,25 @@ fn expand_braces<E: Expander>( } } WordToken::ArrayProcess(command, _, ref index) => match *index { - Select::None => (), Select::All => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); let temp = temp.split_whitespace().collect::<Vec<&str>>(); output.push_str(&temp.join(" ")); } Select::Index(Index::Forward(id)) => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); output.push_str(temp.split_whitespace().nth(id).unwrap_or_default()); } Select::Index(Index::Backward(id)) => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); output.push_str(temp.split_whitespace().rev().nth(id).unwrap_or_default()); } Select::Range(range) => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); let len = temp.split_whitespace().count(); if let Some((start, length)) = range.bounds(len) { let res = @@ -349,7 +326,7 @@ fn expand_braces<E: Expander>( output.push_str(&res.join(" ")); } } - Select::Key(_) => (), + Select::Key(_) | Select::None => (), }, WordToken::ArrayMethod(ref method) => { method.handle(&mut output, expand_func); @@ -368,7 +345,7 @@ fn expand_braces<E: Expander>( WordToken::Whitespace(whitespace) => output.push_str(whitespace), WordToken::Process(command, quoted, ref index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; - expand_process(&mut output, command, index.clone(), expand_func, quoted); + expand_process(&mut output, command, &index, expand_func, quoted); } WordToken::Variable(text, quoted, ref index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; @@ -377,7 +354,7 @@ fn expand_braces<E: Expander>( None => continue, }; - slice(&mut output, expanded, index.clone()); + slice(&mut output, expanded, &index); } WordToken::Normal(ref text, _, tilde) => { expand(&mut output, &mut expanded_words, expand_func, text.as_ref(), false, tilde); @@ -436,7 +413,7 @@ fn expand_single_array_token<E: Expander>( let mut output = small::String::new(); match *token { WordToken::Array(ref elements, ref index) => { - Some(array_expand(elements, expand_func, index.clone())) + Some(array_expand(elements, expand_func, &index)) } WordToken::ArrayVariable(array, quoted, ref index) => { match expand_func.array(array, index.clone()) { @@ -448,21 +425,20 @@ fn expand_single_array_token<E: Expander>( } } WordToken::ArrayProcess(command, _, ref index) => match *index { - Select::None => Some(types::Array::new()), Select::All => { - expand_process(&mut output, command, Select::All, expand_func, false); + expand_process(&mut output, command, &Select::All, expand_func, false); Some(output.split_whitespace().map(From::from).collect::<types::Array>()) } Select::Index(Index::Forward(id)) => { - expand_process(&mut output, command, Select::All, expand_func, false); + expand_process(&mut output, command, &Select::All, expand_func, false); Some(output.split_whitespace().nth(id).map(Into::into).into_iter().collect()) } Select::Index(Index::Backward(id)) => { - expand_process(&mut output, command, Select::All, expand_func, false); + expand_process(&mut output, command, &Select::All, expand_func, false); Some(output.split_whitespace().rev().nth(id).map(Into::into).into_iter().collect()) } Select::Range(range) => { - expand_process(&mut output, command, Select::All, expand_func, false); + expand_process(&mut output, command, &Select::All, expand_func, false); if let Some((start, length)) = range.bounds(output.split_whitespace().count()) { Some( output @@ -476,7 +452,7 @@ fn expand_single_array_token<E: Expander>( Some(types::Array::new()) } } - Select::Key(_) => Some(types::Array::new()), + Select::Key(_) | Select::None => Some(types::Array::new()), }, WordToken::ArrayMethod(ref array_method) => Some(array_method.handle_as_array(expand_func)), _ => None, @@ -499,7 +475,7 @@ fn expand_single_string_token<E: Expander>( WordToken::Whitespace(text) => output.push_str(text), WordToken::Process(command, quoted, ref index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; - expand_process(&mut output, command, index.clone(), expand_func, quoted); + expand_process(&mut output, command, &index, expand_func, quoted); } WordToken::Variable(text, quoted, ref index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; @@ -513,7 +489,7 @@ fn expand_single_string_token<E: Expander>( } }; - slice(&mut output, expanded, index.clone()); + slice(&mut output, expanded, &index); } WordToken::Arithmetic(s) => expand_arithmetic(&mut output, s, expand_func), _ => unreachable!(), @@ -608,7 +584,7 @@ pub(crate) fn expand_tokens<E: Expander>( for word in token_buffer { match *word { WordToken::Array(ref elements, ref index) => { - output.push_str(&array_expand(elements, expand_func, index.clone()).join(" ")); + output.push_str(&array_expand(elements, expand_func, &index).join(" ")); } WordToken::ArrayVariable(array, _, ref index) => { if let Some(array) = expand_func.array(array, index.clone()) { @@ -616,26 +592,25 @@ pub(crate) fn expand_tokens<E: Expander>( } } WordToken::ArrayProcess(command, _, ref index) => match index.clone() { - Select::None => (), Select::All => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); let temp = temp.split_whitespace().collect::<Vec<&str>>(); output.push_str(&temp.join(" ")); } Select::Index(Index::Forward(id)) => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); output.push_str(temp.split_whitespace().nth(id).unwrap_or_default()); } Select::Index(Index::Backward(id)) => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); output.push_str(temp.split_whitespace().rev().nth(id).unwrap_or_default()); } Select::Range(range) => { let mut temp = small::String::new(); - expand_process(&mut temp, command, Select::All, expand_func, false); + expand_process(&mut temp, command, &Select::All, expand_func, false); if let Some((start, length)) = range.bounds(temp.split_whitespace().count()) { let temp = temp @@ -646,7 +621,7 @@ pub(crate) fn expand_tokens<E: Expander>( output.push_str(&temp.join(" ")) } } - Select::Key(_) => (), + Select::Key(_) | Select::None => (), }, WordToken::ArrayMethod(ref method) => { method.handle(&mut output, expand_func); @@ -670,7 +645,7 @@ pub(crate) fn expand_tokens<E: Expander>( } WordToken::Process(command, quoted, ref index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; - expand_process(&mut output, command, index.clone(), expand_func, quoted); + expand_process(&mut output, command, &index, expand_func, quoted); } WordToken::Variable(text, quoted, ref index) => { let quoted = if reverse_quoting { !quoted } else { quoted }; @@ -679,7 +654,7 @@ pub(crate) fn expand_tokens<E: Expander>( None => continue, }; - slice(&mut output, expanded, index.clone()); + slice(&mut output, expanded, &index); } WordToken::Arithmetic(s) => expand_arithmetic(&mut output, s, expand_func), } @@ -769,7 +744,7 @@ mod test { fn expand_process_quoted() { let mut output = small::String::new(); let line = " Mary had\ta little \n\t lamb\t"; - expand_process(&mut output, line, Select::All, &CommandExpander, true); + expand_process(&mut output, line, &Select::All, &CommandExpander, true); assert_eq!(output.as_str(), line); } @@ -777,7 +752,7 @@ mod test { fn expand_process_unquoted() { let mut output = small::String::new(); let line = " Mary had\ta\u{2009}little \n\t lamb 😉😉\t"; - expand_process(&mut output, line, Select::All, &CommandExpander, false); + expand_process(&mut output, line, &Select::All, &CommandExpander, false); assert_eq!(output.as_str(), "Mary had a little lamb 😉😉"); } diff --git a/src/lib/parser/shell_expand/words/methods/strings.rs b/src/lib/parser/shell_expand/words/methods/strings.rs index d6b618a7d0683d357008c65da1615753a8bc2db3..81d42da5d5f8e34b450becfddd9d080c692774a1 100644 --- a/src/lib/parser/shell_expand/words/methods/strings.rs +++ b/src/lib/parser/shell_expand/words/methods/strings.rs @@ -228,12 +228,12 @@ impl<'a> StringMethod<'a> { "join" => { let pattern = pattern.join(" "); if let Some(array) = expand.array(variable, Select::All) { - slice(output, array.join(&pattern), self.selection.clone()); + slice(output, array.join(&pattern), &self.selection); } else if is_expression(variable) { slice( output, expand_string(variable, expand, false).join(&pattern), - self.selection.clone(), + &self.selection, ); } } diff --git a/src/lib/parser/shell_expand/words/mod.rs b/src/lib/parser/shell_expand/words/mod.rs index fb0ca34bb2ac2e34e23bd8fe9315076fbe9ad917..c6f8668fd6731dc66623502ab8ac5766d1c14526 100644 --- a/src/lib/parser/shell_expand/words/mod.rs +++ b/src/lib/parser/shell_expand/words/mod.rs @@ -678,115 +678,113 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> { let mut tilde = false; loop { - if let Some(character) = iterator.next() { - match character { - _ if self.flags.contains(Flags::BACKSL) => { - self.read += 1; - self.flags ^= Flags::BACKSL; - break; - } - b'\\' => { - if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) { - start += 1; - } - self.read += 1; - self.flags ^= Flags::BACKSL; - break; - } - b'\'' if !self.flags.contains(Flags::DQUOTE) => { - start += 1; - self.read += 1; - self.flags ^= Flags::SQUOTE; - break; - } - b'"' if !self.flags.contains(Flags::SQUOTE) => { + match iterator.next()? { + _ if self.flags.contains(Flags::BACKSL) => { + self.read += 1; + self.flags ^= Flags::BACKSL; + break; + } + b'\\' => { + if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) { start += 1; - self.read += 1; - if self.flags.contains(Flags::DQUOTE) { - self.flags -= Flags::DQUOTE; - return self.next(); - } - self.flags |= Flags::DQUOTE; - break; - } - b' ' if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) => { - return Some(self.whitespaces(&mut iterator)); } - b'~' if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) => { - tilde = true; - self.read += 1; - break; - } - b'{' if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) => { - self.read += 1; - return Some(self.braces(&mut iterator)); + self.read += 1; + self.flags ^= Flags::BACKSL; + break; + } + b'\'' if !self.flags.contains(Flags::DQUOTE) => { + start += 1; + self.read += 1; + self.flags ^= Flags::SQUOTE; + break; + } + b'"' if !self.flags.contains(Flags::SQUOTE) => { + start += 1; + self.read += 1; + if self.flags.contains(Flags::DQUOTE) { + self.flags -= Flags::DQUOTE; + return self.next(); } - b'[' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => { - if self.glob_check(&mut iterator) { - glob = self.do_glob; - } else { - return Some(self.array(&mut iterator)); - } + self.flags |= Flags::DQUOTE; + break; + } + b' ' if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) => { + return Some(self.whitespaces(&mut iterator)); + } + b'~' if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) => { + tilde = true; + self.read += 1; + break; + } + b'{' if !self.flags.intersects(Flags::DQUOTE | Flags::SQUOTE) => { + self.read += 1; + return Some(self.braces(&mut iterator)); + } + b'[' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => { + if self.glob_check(&mut iterator) { + glob = self.do_glob; + } else { + return Some(self.array(&mut iterator)); } - b'@' if !self.flags.contains(Flags::SQUOTE) => match iterator.next() { + } + b'@' if !self.flags.contains(Flags::SQUOTE) => { + return match iterator.next() { Some(b'(') => { self.read += 2; - return Some(self.array_process(&mut iterator)); + Some(self.array_process(&mut iterator)) } Some(b'{') => { self.read += 2; - return Some(self.braced_array_variable(&mut iterator)); + Some(self.braced_array_variable(&mut iterator)) } Some(b' ') | None => { self.read += 1; let output = &self.data[start..self.read]; - return Some(WordToken::Normal(output.into(), glob, tilde)); + Some(WordToken::Normal(output.into(), glob, tilde)) } _ => { self.read += 1; - return Some(self.array_variable(&mut iterator)); + Some(self.array_variable(&mut iterator)) } - }, - b'$' if !self.flags.contains(Flags::SQUOTE) => { - match iterator.next() { - Some(b'(') => { - self.read += 2; - return if self.data.as_bytes()[self.read] == b'(' { - // Pop the incoming left paren - let _ = iterator.next(); - self.read += 1; - Some(self.arithmetic_expression(&mut iterator)) - } else { - Some(self.process(&mut iterator)) - }; - } - Some(b'{') => { - self.read += 2; - return Some(self.braced_variable(&mut iterator)); - } - Some(b' ') | None => { - self.read += 1; - let output = &self.data[start..self.read]; - return Some(WordToken::Normal(output.into(), glob, tilde)); - } - _ => { + } + } + b'$' if !self.flags.contains(Flags::SQUOTE) => { + return match iterator.next() { + Some(b'(') => { + self.read += 2; + if self.data.as_bytes()[self.read] == b'(' { + // Pop the incoming left paren + let _ = iterator.next(); self.read += 1; - return Some(self.variable(&mut iterator)); + Some(self.arithmetic_expression(&mut iterator)) + } else { + Some(self.process(&mut iterator)) } } - } - b'*' | b'?' => { - self.read += 1; - glob = self.do_glob; - break; - } - _ => { - self.read += 1; - break; - } + Some(b'{') => { + self.read += 2; + Some(self.braced_variable(&mut iterator)) + } + Some(b' ') | None => { + self.read += 1; + let output = &self.data[start..self.read]; + Some(WordToken::Normal(output.into(), glob, tilde)) + } + _ => { + self.read += 1; + Some(self.variable(&mut iterator)) + } + }; + } + b'*' | b'?' => { + self.read += 1; + glob = self.do_glob; + break; + } + _ => { + self.read += 1; + break; } - } else { - return None; } } while let Some(character) = iterator.next() { @@ -849,8 +847,8 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> { return Some(WordToken::Normal(output.into(), glob, tilde)); } } - let output = &self.data[start..self.read]; - if output != "" { + if self.read != start { + let output = &self.data[start..self.read]; return Some(WordToken::Normal(unescape(output), glob, tilde)); } else { return self.next(); diff --git a/src/lib/parser/statement/case.rs b/src/lib/parser/statement/case.rs index e0daf2f6c1fe3565c00da073e1118d664b60575f..e45248780a285ab9b37abefe0e5b3317a659daad 100644 --- a/src/lib/parser/statement/case.rs +++ b/src/lib/parser/statement/case.rs @@ -67,20 +67,15 @@ pub(crate) fn parse_case( } conditional = Some(string); } - Some(inner) => { - if argument.is_none() { - argument = Some(inner); - continue; - } else { - return Err(CaseError::ExtraVar(inner)); - } + Some(inner) if argument.is_none() => { + argument = Some(inner); + continue; } + Some(inner) => return Err(CaseError::ExtraVar(inner)), None => (), } - break; + return Ok((argument, binding, conditional)); } - - Ok((argument, binding, conditional)) } #[cfg(test)] diff --git a/src/lib/parser/statement/parse.rs b/src/lib/parser/statement/parse.rs index 9ac851a1ce490460328f69cd7d856d9e51424b44..221ac8131505c8fd5078d1bc58b3d7b048a971af 100644 --- a/src/lib/parser/statement/parse.rs +++ b/src/lib/parser/statement/parse.rs @@ -199,12 +199,8 @@ pub(crate) fn parse(code: &str) -> Statement { _ if cmd.starts_with("time ") => { // Ignore embedded time calls let mut timed = cmd[4..].trim_start(); - loop { - if timed.starts_with("time ") { - timed = timed[4..].trim_start(); - continue; - } - break; + while timed.starts_with("time ") { + timed = timed[4..].trim_start(); } Statement::Time(Box::new(parse(timed))) } diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs index ab108096df79b5a37cb470dc24c8b094a5de5c4c..a3b65645be6c6b86fca9aa6a42485259ca58a5dc 100644 --- a/src/lib/shell/assignments.rs +++ b/src/lib/shell/assignments.rs @@ -168,7 +168,7 @@ impl VariableStore for Shell { } } } else { - self.overwrite(key, operator, rhs) + self.overwrite(&key, operator, rhs) } })?; } @@ -185,7 +185,7 @@ impl VariableStore for Shell { let actions = AssignmentActions::new(keys, op, vals); if let Err(why) = self.calculate(actions).and_then(|apply| { for (key, value) in apply { - self.assign(key, value)? + self.assign(&key, value)? } Ok(()) }) { diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs index 35eb6bb4ea578a2b16c5308b63b2979e4b774fe4..c0fc0e71c0060992da008bc4650f34540d0086f3 100644 --- a/src/lib/shell/flow.rs +++ b/src/lib/shell/flow.rs @@ -2,6 +2,7 @@ use super::{ flags::*, flow_control::{insert_statement, Case, ElseIf, Function, Statement}, job_control::JobControl, + signals, status::*, Shell, }; @@ -158,22 +159,18 @@ impl FlowLogic for Shell { statements: Vec<Statement>, ) -> Condition { loop { - let expression = { - self.execute_statements(expression.clone()); - self.previous_status == 0 - }; - if expression { - // Cloning is needed so the statement can be re-iterated again if needed. - match self.execute_statements(statements.clone()) { - Condition::Break => break, - Condition::SigInt => return Condition::SigInt, - _ => (), - } - } else { - break; + self.execute_statements(expression.clone()); + if self.previous_status != 0 { + return Condition::NoOp; + } + + // Cloning is needed so the statement can be re-iterated again if needed. + match self.execute_statements(statements.clone()) { + Condition::Break => return Condition::NoOp, + Condition::SigInt => return Condition::SigInt, + _ => (), } } - Condition::NoOp } fn execute_statement(&mut self, statement: Statement) -> Condition { @@ -310,7 +307,7 @@ impl FlowLogic for Shell { }, _ => {} } - if let Some(signal) = self.next_signal() { + if let Some(signal) = signals::SignalHandler.next() { if self.handle_signal(signal) { self.exit(get_signal_code(signal)); } diff --git a/src/lib/shell/fork.rs b/src/lib/shell/fork.rs index abc39ca2c96f5ef336c445c938d94b4c2416de25..7dd42fd8a8087941d21bcaf3ca61bc36922b57ac 100644 --- a/src/lib/shell/fork.rs +++ b/src/lib/shell/fork.rs @@ -7,10 +7,8 @@ use std::{ }; pub fn wait_for_child(pid: u32) -> io::Result<u8> { - let mut status; - loop { - status = 0; + let mut status = 0; if let Err(errno) = sys::waitpid(pid as i32, &mut status, sys::WUNTRACED) { break if errno == sys::ECHILD { Ok(sys::wexitstatus(status) as u8) diff --git a/src/lib/shell/job.rs b/src/lib/shell/job.rs index 5a27a64ed36ec60970f1a6aad636f7927324e7d9..e0d1dda79a475936e87b4375cf26e98cff597bf2 100644 --- a/src/lib/shell/job.rs +++ b/src/lib/shell/job.rs @@ -121,14 +121,7 @@ impl TeeItem { return Ok(()); } for file in sinks.iter_mut() { - let mut total = 0; - loop { - let wrote = file.write(&buf[total..len])?; - total += wrote; - if total == len { - break; - } - } + file.write_all(&buf[..len])?; } } } diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index d5aa809a72ebe2688752606a9df449dce6274798..a3a3ad5ebb98c08c3132e777f480ad131cc59dea 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -380,16 +380,6 @@ impl Shell { } } - pub(crate) fn next_signal(&self) -> Option<i32> { - match signals::PENDING.swap(0, Ordering::SeqCst) as u8 { - 0 => None, - signals::SIGINT => Some(sys::SIGINT), - signals::SIGHUP => Some(sys::SIGHUP), - signals::SIGTERM => Some(sys::SIGTERM), - _ => unreachable!(), - } - } - pub(crate) fn new(is_library: bool) -> Shell { let mut shell = Shell { builtins: BUILTINS, @@ -412,7 +402,7 @@ impl Shell { shell } - pub fn assign(&mut self, key: Key, value: Value) -> Result<(), String> { + pub fn assign(&mut self, key: &Key, value: Value) -> Result<(), String> { match (&key.kind, &value) { (Primitive::Indexed(ref index_name, ref index_kind), Value::Str(_)) => { let index = value_check(self, index_name, index_kind) @@ -466,7 +456,7 @@ impl Shell { } } - pub fn overwrite(&mut self, key: Key, operator: Operator, rhs: Value) -> Result<(), String> { + pub fn overwrite(&mut self, key: &Key, operator: Operator, rhs: Value) -> Result<(), String> { let lhs = self .variables .get_mut(key.name) diff --git a/src/lib/shell/pipe_exec/job_control.rs b/src/lib/shell/pipe_exec/job_control.rs index cffb80ffd9b122f162704630e0688b18f7330671..3f7764b2bc424bc6d2d992f19f5214807baa8726 100644 --- a/src/lib/shell/pipe_exec/job_control.rs +++ b/src/lib/shell/pipe_exec/job_control.rs @@ -151,10 +151,9 @@ impl JobControl for Shell { fn watch_foreground(&mut self, pid: i32, command: &str) -> i32 { let mut signaled = 0; let mut exit_status = 0; - let mut status; loop { - status = 0; + let mut status = 0; match waitpid(pid, &mut status, WUNTRACED) { Err(errno) => match errno { ECHILD if signaled == 0 => break exit_status, @@ -204,24 +203,13 @@ impl JobControl for Shell { /// Waits until all running background tasks have completed, and listens for signals in the /// event that a signal is sent to kill the running tasks. fn wait_for_background(&mut self) { - let sigcode; - 'event: loop { - for process in self.background.lock().unwrap().iter() { - if let ProcessState::Running = process.state { - while let Some(signal) = self.next_signal() { - if signal != sys::SIGTSTP { - self.background_send(signal); - sigcode = get_signal_code(signal); - break 'event; - } - } - sleep(Duration::from_millis(100)); - continue 'event; - } + while self.background.lock().unwrap().iter().any(|p| p.state == ProcessState::Running) { + if let Some(signal) = signals::SignalHandler.find(|&s| s != sys::SIGTSTP) { + self.background_send(signal); + self.exit(get_signal_code(signal)); } - return; + sleep(Duration::from_millis(100)); } - self.exit(sigcode); } fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 { diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs index f399c4565f71238562a7bac66270be596c6a6e71..060c143f804d0b4513364110d3df734686ce772a 100644 --- a/src/lib/shell/pipe_exec/mod.rs +++ b/src/lib/shell/pipe_exec/mod.rs @@ -498,27 +498,10 @@ impl PipelineExecution for Shell { redir(file.as_raw_fd(), sys::STDOUT_FILENO) } - fn read_and_write<R: io::Read>(src: &mut R, stdout: &mut io::StdoutLock) -> io::Result<()> { - let mut buf = [0; 4096]; - loop { - let len = src.read(&mut buf)?; - if len == 0 { - return Ok(()); - }; - let mut total = 0; - loop { - let wrote = stdout.write(&buf[total..len])?; - total += wrote; - if total == len { - break; - } - } - } - }; let stdout = io::stdout(); let mut stdout = stdout.lock(); for file in stdin.iter_mut().chain(sources) { - if let Err(why) = read_and_write(file, &mut stdout) { + if let Err(why) = std::io::copy(file, &mut stdout) { eprintln!("ion: error in multiple input redirect process: {:?}", why); return FAILURE; } @@ -981,13 +964,11 @@ fn set_process_group(pgid: &mut u32, pid: u32) -> bool { } pub fn wait_for_interrupt(pid: u32) -> io::Result<()> { - let mut status; - loop { - status = 0; + let mut status = 0; match sys::waitpid(pid as i32, &mut status, sys::WUNTRACED) { Ok(_) => break Ok(()), - Err(errno) if errno == sys::EINTR => continue, + Err(sys::EINTR) => continue, Err(errno) => break Err(io::Error::from_raw_os_error(errno)), } } diff --git a/src/lib/shell/signals.rs b/src/lib/shell/signals.rs index e3a444ab89ce87844080bf57ca77145503ef4dd9..611f12a6340f6195ebabdb0ef3a3c3b0271fc4a1 100644 --- a/src/lib/shell/signals.rs +++ b/src/lib/shell/signals.rs @@ -4,7 +4,7 @@ //! children of the shell. // use std::sync::atomic::{ATOMIC_U8_INIT, AtomicU8}; -use std::sync::atomic::AtomicUsize; +use std::sync::atomic::{AtomicUsize, Ordering}; use crate::sys; @@ -35,3 +35,17 @@ impl SignalHandler { impl Drop for SignalHandler { fn drop(&mut self) { unblock(); } } + +impl Iterator for SignalHandler { + type Item = i32; + + fn next(&mut self) -> Option<Self::Item> { + match PENDING.swap(0, Ordering::SeqCst) as u8 { + 0 => None, + SIGINT => Some(sys::SIGINT), + SIGHUP => Some(sys::SIGHUP), + SIGTERM => Some(sys::SIGTERM), + _ => unreachable!(), + } + } +} diff --git a/src/lib/shell/variables/mod.rs b/src/lib/shell/variables/mod.rs index dbe659c7bdbef6f59875a372fe5ec3934bcbc057..09395c5114085f9a7a765fa82125f7f1564d15c8 100644 --- a/src/lib/shell/variables/mod.rs +++ b/src/lib/shell/variables/mod.rs @@ -176,7 +176,7 @@ impl Default for Variables { Value::Array(array!["no_such_command", "whitespace", "duplicates"]), ); - map.insert("CDPATH".into(), Value::Array(array![])); + map.insert("CDPATH".into(), Value::Array(Array::new())); // Initialize the HOME variable sys_env::home_dir().map_or_else(