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
pull/4299/head
A-Walrus 2 years ago committed by GitHub
parent 9af7c1c9f3
commit 4ff5feeb0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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<Cow<'_, str>> {
enum State {
Normal,
NormalEscaped,
OnWhitespace,
Unquoted,
UnquotedEscaped,
Quoted,
QuoteEscaped,
Dquoted,
@ -13,7 +14,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
use State::*;
let mut state = Normal;
let mut state = Unquoted;
let mut args: Vec<Cow<str>> = Vec::new();
let mut escaped = String::with_capacity(input.len());
@ -22,31 +23,47 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
for (i, c) in input.char_indices() {
state = match state {
Normal => match c {
OnWhitespace => match c {
'"' => {
end = i;
Dquoted
}
'\'' => {
end = i;
Quoted
}
'\\' => {
if cfg!(unix) {
escaped.push_str(&input[start..i]);
start = i + 1;
NormalEscaped
UnquotedEscaped
} else {
Normal
OnWhitespace
}
}
'"' => {
c if c.is_ascii_whitespace() => {
end = i;
Dquoted
OnWhitespace
}
_ => Unquoted,
},
Unquoted => match c {
'\\' => {
if cfg!(unix) {
escaped.push_str(&input[start..i]);
start = i + 1;
UnquotedEscaped
} else {
Unquoted
}
'\'' => {
end = i;
Quoted
}
c if c.is_ascii_whitespace() => {
end = i;
Normal
OnWhitespace
}
_ => Normal,
_ => Unquoted,
},
NormalEscaped => Normal,
UnquotedEscaped => Unquoted,
Quoted => match c {
'\\' => {
if cfg!(unix) {
@ -59,7 +76,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
}
'\'' => {
end = i;
Normal
OnWhitespace
}
_ => Quoted,
},
@ -76,7 +93,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
}
'"' => {
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);
}
}

Loading…
Cancel
Save