util: Fix shell builder quoting regressions (#44685)
Follow up to https://github.com/zed-industries/zed/pull/42382 Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
@@ -702,7 +702,10 @@ impl ShellKind {
|
||||
.map(|quoted| Cow::Owned(self.prepend_command_prefix("ed).into_owned()));
|
||||
}
|
||||
}
|
||||
self.try_quote(arg)
|
||||
self.try_quote(arg).map(|quoted| match quoted {
|
||||
unquoted @ Cow::Borrowed(_) => unquoted,
|
||||
Cow::Owned(quoted) => Cow::Owned(self.prepend_command_prefix("ed).into_owned()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn split(&self, input: &str) -> Option<Vec<String>> {
|
||||
@@ -916,7 +919,7 @@ mod tests {
|
||||
.try_quote_prefix_aware("'uname'")
|
||||
.unwrap()
|
||||
.into_owned(),
|
||||
"\"'uname'\"".to_string()
|
||||
"^\"'uname'\"".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
shell_kind.try_quote("^uname").unwrap().into_owned(),
|
||||
@@ -949,7 +952,7 @@ mod tests {
|
||||
.try_quote_prefix_aware("'uname a'")
|
||||
.unwrap()
|
||||
.into_owned(),
|
||||
"\"'uname a'\"".to_string()
|
||||
"^\"'uname a'\"".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
shell_kind.try_quote("^'uname a'").unwrap().into_owned(),
|
||||
|
||||
@@ -80,27 +80,23 @@ impl ShellBuilder {
|
||||
task_args: &[String],
|
||||
) -> (String, Vec<String>) {
|
||||
if let Some(task_command) = task_command {
|
||||
let task_command = self.kind.prepend_command_prefix(&task_command);
|
||||
let task_command = if !task_args.is_empty() {
|
||||
match self.kind.try_quote_prefix_aware(&task_command) {
|
||||
Some(task_command) => task_command,
|
||||
Some(task_command) => task_command.into_owned(),
|
||||
None => task_command,
|
||||
}
|
||||
} else {
|
||||
task_command
|
||||
};
|
||||
let mut combined_command =
|
||||
task_args
|
||||
.iter()
|
||||
.fold(task_command.into_owned(), |mut command, arg| {
|
||||
command.push(' ');
|
||||
let shell_variable = self.kind.to_shell_variable(arg);
|
||||
command.push_str(&match self.kind.try_quote(&shell_variable) {
|
||||
Some(shell_variable) => shell_variable,
|
||||
None => Cow::Owned(shell_variable),
|
||||
});
|
||||
command
|
||||
});
|
||||
let mut combined_command = task_args.iter().fold(task_command, |mut command, arg| {
|
||||
command.push(' ');
|
||||
let shell_variable = self.kind.to_shell_variable(arg);
|
||||
command.push_str(&match self.kind.try_quote(&shell_variable) {
|
||||
Some(shell_variable) => shell_variable,
|
||||
None => Cow::Owned(shell_variable),
|
||||
});
|
||||
command
|
||||
});
|
||||
if self.redirect_stdin {
|
||||
match self.kind {
|
||||
ShellKind::Fish => {
|
||||
@@ -134,6 +130,90 @@ impl ShellBuilder {
|
||||
(self.program, self.args)
|
||||
}
|
||||
|
||||
// This should not exist, but our task infra is broken beyond repair right now
|
||||
#[doc(hidden)]
|
||||
pub fn build_no_quote(
|
||||
mut self,
|
||||
task_command: Option<String>,
|
||||
task_args: &[String],
|
||||
) -> (String, Vec<String>) {
|
||||
if let Some(task_command) = task_command {
|
||||
let mut combined_command = task_args.iter().fold(task_command, |mut command, arg| {
|
||||
command.push(' ');
|
||||
command.push_str(&self.kind.to_shell_variable(arg));
|
||||
command
|
||||
});
|
||||
if self.redirect_stdin {
|
||||
match self.kind {
|
||||
ShellKind::Fish => {
|
||||
combined_command.insert_str(0, "begin; ");
|
||||
combined_command.push_str("; end </dev/null");
|
||||
}
|
||||
ShellKind::Posix
|
||||
| ShellKind::Nushell
|
||||
| ShellKind::Csh
|
||||
| ShellKind::Tcsh
|
||||
| ShellKind::Rc
|
||||
| ShellKind::Xonsh
|
||||
| ShellKind::Elvish => {
|
||||
combined_command.insert(0, '(');
|
||||
combined_command.push_str(") </dev/null");
|
||||
}
|
||||
ShellKind::PowerShell | ShellKind::Pwsh => {
|
||||
combined_command.insert_str(0, "$null | & {");
|
||||
combined_command.push_str("}");
|
||||
}
|
||||
ShellKind::Cmd => {
|
||||
combined_command.push_str("< NUL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.args
|
||||
.extend(self.kind.args_for_shell(self.interactive, combined_command));
|
||||
}
|
||||
|
||||
(self.program, self.args)
|
||||
}
|
||||
|
||||
/// Builds a command with the given task command and arguments.
|
||||
///
|
||||
/// Prefer this over manually constructing a command with the output of `Self::build`,
|
||||
/// as this method handles `cmd` weirdness on windows correctly.
|
||||
pub fn build_command(
|
||||
self,
|
||||
mut task_command: Option<String>,
|
||||
task_args: &[String],
|
||||
) -> smol::process::Command {
|
||||
#[cfg(windows)]
|
||||
let kind = self.kind;
|
||||
if task_args.is_empty() {
|
||||
task_command = task_command
|
||||
.as_ref()
|
||||
.map(|cmd| self.kind.try_quote_prefix_aware(&cmd).map(Cow::into_owned))
|
||||
.unwrap_or(task_command);
|
||||
}
|
||||
let (program, args) = self.build(task_command, task_args);
|
||||
|
||||
let mut child = crate::command::new_smol_command(program);
|
||||
|
||||
#[cfg(windows)]
|
||||
if kind == ShellKind::Cmd {
|
||||
use smol::process::windows::CommandExt;
|
||||
|
||||
for arg in args {
|
||||
child.raw_arg(arg);
|
||||
}
|
||||
} else {
|
||||
child.args(args);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
child.args(args);
|
||||
|
||||
child
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> ShellKind {
|
||||
self.kind
|
||||
}
|
||||
@@ -166,7 +246,7 @@ mod test {
|
||||
vec![
|
||||
"-i",
|
||||
"-c",
|
||||
"^echo '$env.hello' '$env.world' nothing '--($env.something)' '$' '${test'"
|
||||
"echo '$env.hello' '$env.world' nothing '--($env.something)' '$' '${test'"
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -181,7 +261,7 @@ mod test {
|
||||
.build(Some("echo".into()), &["nothing".to_string()]);
|
||||
|
||||
assert_eq!(program, "nu");
|
||||
assert_eq!(args, vec!["-i", "-c", "(^echo nothing) </dev/null"]);
|
||||
assert_eq!(args, vec!["-i", "-c", "(echo nothing) </dev/null"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -196,4 +276,23 @@ mod test {
|
||||
assert_eq!(program, "fish");
|
||||
assert_eq!(args, vec!["-i", "-c", "begin; echo test; end </dev/null"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_quote_sole_command_only() {
|
||||
let shell = Shell::Program("fish".to_owned());
|
||||
let shell_builder = ShellBuilder::new(&shell, false);
|
||||
|
||||
let (program, args) = shell_builder.build(Some("echo".into()), &[]);
|
||||
|
||||
assert_eq!(program, "fish");
|
||||
assert_eq!(args, vec!["-i", "-c", "echo"]);
|
||||
|
||||
let shell = Shell::Program("fish".to_owned());
|
||||
let shell_builder = ShellBuilder::new(&shell, false);
|
||||
|
||||
let (program, args) = shell_builder.build(Some("echo oo".into()), &[]);
|
||||
|
||||
assert_eq!(program, "fish");
|
||||
assert_eq!(args, vec!["-i", "-c", "echo oo"]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user