From 4c9b1984bf1494d32715edb610d8c872e14ff115 Mon Sep 17 00:00:00 2001
From: Melody Horn <melody@boringcactus.com>
Date: Fri, 2 Apr 2021 21:14:12 -0600
Subject: implement `shell` function, partially

---
 src/makefile/functions.rs | 36 +++++++++++++++++++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/src/makefile/functions.rs b/src/makefile/functions.rs
index a61a0cf..8df1892 100644
--- a/src/makefile/functions.rs
+++ b/src/makefile/functions.rs
@@ -62,7 +62,10 @@ pub fn expand_call(name: &str, args: &[TokenString], macros: &MacroSet) -> Resul
         }
 
         // shell
-        "shell" => todo!(),
+        "shell" => {
+            assert_eq!(args.len(), 1);
+            shell::shell(macros, &args[0])
+        }
 
         // fallback
         _ => bail!("function not implemented: {}", name),
@@ -274,6 +277,37 @@ mod origin {
     }
 }
 
+mod shell {
+    use super::*;
+
+    use std::env;
+    use std::process::{Command, Stdio};
+
+    pub fn shell(macros: &MacroSet, command: &TokenString) -> Result<String> {
+        // TODO bring this in from command_line
+        let command = macros.expand(command)?;
+        let (program, args) = if cfg!(windows) {
+            let cmd = env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".into());
+            let args = vec!["/c", &command];
+            (cmd, args)
+        } else {
+            let sh = env::var("SHELL").unwrap_or_else(|_| "/bin/sh".into());
+            let args = vec!["-e", "-c", &command];
+            (sh, args)
+        };
+        let result = Command::new(program)
+            .args(args)
+            .stderr(Stdio::inherit())
+            .output()?;
+        let status = result.status;
+        // TODO set .SHELLSTATUS
+        Ok(String::from_utf8(result.stdout)?
+            .replace("\r\n", "\n")
+            .trim_end_matches("\n")
+            .replace("\n", " "))
+    }
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
-- 
cgit v1.2.3