From de764a7ccbbcaf90db88569ce7a8b5e2214cfcd8 Mon Sep 17 00:00:00 2001
From: Chris Denton <christophersdenton@gmail.com>
Date: Thu, 16 Dec 2021 17:21:34 +0000
Subject: [PATCH] Quote bat script command line

---
 library/std/src/process/tests.rs       | 19 +++++++++++++++++++
 library/std/src/sys/windows/process.rs | 16 ++++++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs
index 67b747e410732..630a8a4c47ad0 100644
--- a/library/std/src/process/tests.rs
+++ b/library/std/src/process/tests.rs
@@ -416,3 +416,22 @@ fn env_empty() {
     let p = Command::new("cmd").args(&["/C", "exit 0"]).env_clear().spawn();
     assert!(p.is_ok());
 }
+
+// See issue #91991
+#[test]
+#[cfg(windows)]
+fn run_bat_script() {
+    let tempdir = crate::sys_common::io::test::tmpdir();
+    let script_path = tempdir.join("hello.cmd");
+
+    crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
+    let output = Command::new(&script_path)
+        .arg("fellow Rustaceans")
+        .stdout(crate::process::Stdio::piped())
+        .spawn()
+        .unwrap()
+        .wait_with_output()
+        .unwrap();
+    assert!(output.status.success());
+    assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
+}
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 66b210ce1bfb3..e84dfbce4a754 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -704,6 +704,19 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu
     // 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();
+
+    // CreateFileW has special handling for .bat and .cmd files, which means we
+    // need to add an extra pair of quotes surrounding the whole command line
+    // so they are properly passed on to the script.
+    // See issue #91991.
+    let is_batch_file = Path::new(prog)
+        .extension()
+        .map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat"))
+        .unwrap_or(false);
+    if is_batch_file {
+        cmd.push(b'"' as u16);
+    }
+
     // Always quote the program name so CreateProcess doesn't interpret args as
     // part of the name if the binary wasn't found first time.
     append_arg(&mut cmd, prog, Quote::Always)?;
@@ -715,6 +728,9 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu
         };
         append_arg(&mut cmd, arg, quote)?;
     }
+    if is_batch_file {
+        cmd.push(b'"' as u16);
+    }
     return Ok(cmd);
 
     fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, quote: Quote) -> io::Result<()> {