Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose force_quotes on Windows. #77728

Merged
merged 1 commit into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions library/std/src/sys/windows/ext/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ pub trait CommandExt: Sealed {
/// [1]: https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
fn creation_flags(&mut self, flags: u32) -> &mut process::Command;

/// Forces all arguments to be wrapped in quote (`"`) characters.
///
/// This is useful for passing arguments to [MSYS2/Cygwin][1] based
/// executables: these programs will expand unquoted arguments containing
/// wildcard characters (`?` and `*`) by searching for any file paths
/// matching the wildcard pattern.
///
/// Adding quotes has no effect when passing arguments to programs
/// that use [msvcrt][2]. This includes programs built with both
/// MinGW and MSVC.
///
/// [1]: <https://github.com/msys2/MSYS2-packages/issues/2176>
/// [2]: <https://msdn.microsoft.com/en-us/library/17w5ykft.aspx>
#[unstable(feature = "windows_process_extensions_force_quotes", issue = "82227")]
fn force_quotes(&mut self, enabled: bool) -> &mut process::Command;
}

#[stable(feature = "windows_process_extensions", since = "1.16.0")]
Expand All @@ -113,4 +129,9 @@ impl CommandExt for process::Command {
self.as_inner_mut().creation_flags(flags);
self
}

fn force_quotes(&mut self, enabled: bool) -> &mut process::Command {
self.as_inner_mut().force_quotes(enabled);
self
}
}
12 changes: 9 additions & 3 deletions library/std/src/sys/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub struct Command {
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
force_quotes_enabled: bool,
}

pub enum Stdio {
Expand Down Expand Up @@ -109,6 +110,7 @@ impl Command {
stdin: None,
stdout: None,
stderr: None,
force_quotes_enabled: false,
}
}

Expand All @@ -134,6 +136,10 @@ impl Command {
self.flags = flags;
}

pub fn force_quotes(&mut self, enabled: bool) {
self.force_quotes_enabled = enabled;
}

pub fn get_program(&self) -> &OsStr {
&self.program
}
Expand Down Expand Up @@ -181,7 +187,7 @@ impl Command {
si.dwFlags = c::STARTF_USESTDHANDLES;

let program = program.as_ref().unwrap_or(&self.program);
let mut cmd_str = make_command_line(program, &self.args)?;
let mut cmd_str = make_command_line(program, &self.args, self.force_quotes_enabled)?;
cmd_str.push(0); // add null terminator

// stolen from the libuv code.
Expand Down Expand Up @@ -467,7 +473,7 @@ fn zeroed_process_information() -> c::PROCESS_INFORMATION {

// Produces a wide string *without terminating null*; returns an error if
// `prog` or any of the `args` contain a nul.
fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> {
fn make_command_line(prog: &OsStr, args: &[OsString], force_quotes: bool) -> io::Result<Vec<u16>> {
// Encode the command and arguments in a command line string such
// that the spawned process may recover them using CommandLineToArgvW.
let mut cmd: Vec<u16> = Vec::new();
Expand All @@ -476,7 +482,7 @@ fn make_command_line(prog: &OsStr, args: &[OsString]) -> io::Result<Vec<u16>> {
append_arg(&mut cmd, prog, true)?;
for arg in args {
cmd.push(' ' as u16);
append_arg(&mut cmd, arg, false)?;
append_arg(&mut cmd, arg, force_quotes)?;
}
return Ok(cmd);

Expand Down
26 changes: 19 additions & 7 deletions library/std/src/sys/windows/process/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,41 @@ use crate::ffi::{OsStr, OsString};

#[test]
fn test_make_command_line() {
fn test_wrapper(prog: &str, args: &[&str]) -> String {
fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {
let command_line = &make_command_line(
OsStr::new(prog),
&args.iter().map(|a| OsString::from(a)).collect::<Vec<OsString>>(),
force_quotes,
)
.unwrap();
String::from_utf16(command_line).unwrap()
}

assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"]), "\"prog\" aaa bbb ccc");
assert_eq!(test_wrapper("prog", &["aaa", "bbb", "ccc"], false), "\"prog\" aaa bbb ccc");

assert_eq!(
test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]),
test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"], false),
"\"C:\\Program Files\\blah\\blah.exe\" aaa"
);
assert_eq!(
test_wrapper("C:\\Program Files\\test", &["aa\"bb"]),
test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], false),
"\"C:\\Program Files\\blah\\blah.exe\" aaa v*"
);
assert_eq!(
test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa", "v*"], true),
"\"C:\\Program Files\\blah\\blah.exe\" \"aaa\" \"v*\""
);
assert_eq!(
test_wrapper("C:\\Program Files\\test", &["aa\"bb"], false),
"\"C:\\Program Files\\test\" aa\\\"bb"
);
assert_eq!(test_wrapper("echo", &["a b c"]), "\"echo\" \"a b c\"");
assert_eq!(test_wrapper("echo", &["\" \\\" \\", "\\"]), "\"echo\" \"\\\" \\\\\\\" \\\\\" \\");
assert_eq!(test_wrapper("echo", &["a b c"], false), "\"echo\" \"a b c\"");
assert_eq!(
test_wrapper("echo", &["\" \\\" \\", "\\"], false),
"\"echo\" \"\\\" \\\\\\\" \\\\\" \\"
);
assert_eq!(
test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]),
test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[], false),
"\"\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}\""
);
}