on master help sign in
Fix shellwords delimiter handling (#4098)
4ff5feeA-Walrus
helix-core/src/shellwords.rs+47−16
@@ -3,8 +3,9 @@ use std::borrow::Cow;
3/// Get the vec of escaped / quoted / doublequoted filenames from the input str
3/// Get the vec of escaped / quoted / doublequoted filenames from the input str
4pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
4pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
5 enum State {
5 enum State {
6− Normal,
6+ OnWhitespace,
7− NormalEscaped,
7+ Unquoted,
8+ UnquotedEscaped,
8 Quoted,
9 Quoted,
9 QuoteEscaped,
10 QuoteEscaped,
10 Dquoted,
11 Dquoted,
@@ -13,7 +14,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
13
14
14 use State::*;
15 use State::*;
15
16
16− let mut state = Normal;
17+ let mut state = Unquoted;
17 let mut args: Vec<Cow<str>> = Vec::new();
18 let mut args: Vec<Cow<str>> = Vec::new();
18 let mut escaped = String::with_capacity(input.len());
19 let mut escaped = String::with_capacity(input.len());
19
20
@@ -22,31 +23,47 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
22
23
23 for (i, c) in input.char_indices() {
24 for (i, c) in input.char_indices() {
24 state = match state {
25 state = match state {
25− Normal => match c {
26+ OnWhitespace => match c {
27+ '"' => {
28+ end = i;
29+ Dquoted
30+ }
31+ '\'' => {
32+ end = i;
33+ Quoted
34+ }
26 '\\' => {
35 '\\' => {
27 if cfg!(unix) {
36 if cfg!(unix) {
28 escaped.push_str(&input[start..i]);
37 escaped.push_str(&input[start..i]);
29 start = i + 1;
38 start = i + 1;
30− NormalEscaped
39+ UnquotedEscaped
31 } else {
40 } else {
32− Normal
41+ OnWhitespace
33 }
42 }
34 }
43 }
35− '"' => {
44+ c if c.is_ascii_whitespace() => {
36 end = i;
45 end = i;
37− Dquoted
46+ OnWhitespace
38 }
47 }
48+ _ => Unquoted,
49+ },
50+ Unquoted => match c {
39− '\'' => {
51+ '\\' => {
40− end = i;
52+ if cfg!(unix) {
41− Quoted
53+ escaped.push_str(&input[start..i]);
54+ start = i + 1;
55+ UnquotedEscaped
56+ } else {
57+ Unquoted
58+ }
42 }
59 }
43 c if c.is_ascii_whitespace() => {
60 c if c.is_ascii_whitespace() => {
44 end = i;
61 end = i;
45− Normal
62+ OnWhitespace
46 }
63 }
47− _ => Normal,
64+ _ => Unquoted,
48 },
65 },
49− NormalEscaped => Normal,
66+ UnquotedEscaped => Unquoted,
50 Quoted => match c {
67 Quoted => match c {
51 '\\' => {
68 '\\' => {
52 if cfg!(unix) {
69 if cfg!(unix) {
@@ -59,7 +76,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
59 }
76 }
60 '\'' => {
77 '\'' => {
61 end = i;
78 end = i;
62− Normal
79+ OnWhitespace
63 }
80 }
64 _ => Quoted,
81 _ => Quoted,
65 },
82 },
@@ -76,7 +93,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
76 }
93 }
77 '"' => {
94 '"' => {
78 end = i;
95 end = i;
79− Normal
96+ OnWhitespace
80 }
97 }
81 _ => Dquoted,
98 _ => Dquoted,
82 },
99 },
@@ -195,4 +212,18 @@ mod test {
195 ];
212 ];
196 assert_eq!(expected, result);
213 assert_eq!(expected, result);
197 }
214 }
215+
216+ #[test]
217+ fn test_lists() {
218+ let input =
219+ r#":set statusline.center ["file-type","file-encoding"] '["list", "in", "qoutes"]'"#;
220+ let result = shellwords(input);
221+ let expected = vec![
222+ Cow::from(":set"),
223+ Cow::from("statusline.center"),
224+ Cow::from(r#"["file-type","file-encoding"]"#),
225+ Cow::from(r#"["list", "in", "qoutes"]"#),
226+ ];
227+ assert_eq!(expected, result);
228+ }
198}
229}