From 4ff5feeb0c6c1ce012aa1179a44f35b6f0df4da1 Mon Sep 17 00:00:00 2001 From: A-Walrus <58790821+A-Walrus@users.noreply.github.com> Date: Fri, 21 Oct 2022 04:06:57 +0300 Subject: [PATCH] Fix shellwords delimiter handling (#4098) * Fix shellwords delimiter handling This allows commands such as `:set statusline.center ["file-type"]` to work. Before the quotes within the list would mess it up. Also added a test to ensure correct behavior * Rename Delimiter -> OnWhitespace --- helix-core/src/shellwords.rs | 67 ++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/helix-core/src/shellwords.rs b/helix-core/src/shellwords.rs index 4323039a..afc83496 100644 --- a/helix-core/src/shellwords.rs +++ b/helix-core/src/shellwords.rs @@ -3,8 +3,9 @@ use std::borrow::Cow; /// Get the vec of escaped / quoted / doublequoted filenames from the input str pub fn shellwords(input: &str) -> Vec> { enum State { - Normal, - NormalEscaped, + OnWhitespace, + Unquoted, + UnquotedEscaped, Quoted, QuoteEscaped, Dquoted, @@ -13,7 +14,7 @@ pub fn shellwords(input: &str) -> Vec> { use State::*; - let mut state = Normal; + let mut state = Unquoted; let mut args: Vec> = Vec::new(); let mut escaped = String::with_capacity(input.len()); @@ -22,16 +23,7 @@ pub fn shellwords(input: &str) -> Vec> { for (i, c) in input.char_indices() { state = match state { - Normal => match c { - '\\' => { - if cfg!(unix) { - escaped.push_str(&input[start..i]); - start = i + 1; - NormalEscaped - } else { - Normal - } - } + OnWhitespace => match c { '"' => { end = i; Dquoted @@ -40,13 +32,38 @@ pub fn shellwords(input: &str) -> Vec> { end = i; Quoted } + '\\' => { + if cfg!(unix) { + escaped.push_str(&input[start..i]); + start = i + 1; + UnquotedEscaped + } else { + OnWhitespace + } + } c if c.is_ascii_whitespace() => { end = i; - Normal + OnWhitespace } - _ => Normal, + _ => Unquoted, }, - NormalEscaped => Normal, + Unquoted => match c { + '\\' => { + if cfg!(unix) { + escaped.push_str(&input[start..i]); + start = i + 1; + UnquotedEscaped + } else { + Unquoted + } + } + c if c.is_ascii_whitespace() => { + end = i; + OnWhitespace + } + _ => Unquoted, + }, + UnquotedEscaped => Unquoted, Quoted => match c { '\\' => { if cfg!(unix) { @@ -59,7 +76,7 @@ pub fn shellwords(input: &str) -> Vec> { } '\'' => { end = i; - Normal + OnWhitespace } _ => Quoted, }, @@ -76,7 +93,7 @@ pub fn shellwords(input: &str) -> Vec> { } '"' => { end = i; - Normal + OnWhitespace } _ => Dquoted, }, @@ -195,4 +212,18 @@ mod test { ]; assert_eq!(expected, result); } + + #[test] + fn test_lists() { + let input = + r#":set statusline.center ["file-type","file-encoding"] '["list", "in", "qoutes"]'"#; + let result = shellwords(input); + let expected = vec![ + Cow::from(":set"), + Cow::from("statusline.center"), + Cow::from(r#"["file-type","file-encoding"]"#), + Cow::from(r#"["list", "in", "qoutes"]"#), + ]; + assert_eq!(expected, result); + } }