[C++] Use vfork and exec instead of popen for $(shell)
diff --git a/func.cc b/func.cc
index d85f6f7..9309ed3 100644
--- a/func.cc
+++ b/func.cc
@@ -16,10 +16,14 @@
 
 #include "func.h"
 
+#include <errno.h>
 #include <glob.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
 
 #include <algorithm>
 #include <iterator>
@@ -426,6 +430,48 @@
 }
 #endif
 
+static void RunCommand(const string& cmd, string* s) {
+  int pipefd[2];
+  if (pipe(pipefd) != 0)
+    PERROR("pipe failed");
+  int pid;
+  if ((pid = vfork())) {
+    close(pipefd[1]);
+    while (true) {
+      int status;
+      int result = waitpid(pid, &status, WNOHANG);
+      if (result < 0)
+        PERROR("waitpid failed");
+
+      while (true) {
+        char buf[4096];
+        ssize_t r = read(pipefd[0], buf, 4096);
+        if (r < 0)
+          PERROR("read failed");
+        if (r == 0)
+          break;
+        s->append(buf, buf+r);
+      }
+
+      if (result != 0) {
+        break;
+      }
+    }
+    close(pipefd[0]);
+  } else {
+    close(pipefd[0]);
+    if (dup2(pipefd[1], 1) < 0)
+      PERROR("dup2 failed");
+    close(pipefd[1]);
+
+    const char* argv[] = {
+      // TODO: Handle $(SHELL).
+      "/bin/sh", "-c", cmd.c_str(), NULL
+    };
+    execvp(argv[0], const_cast<char**>(argv));
+  }
+}
+
 void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   shared_ptr<string> cmd = args[0]->Eval(ev);
   if (ev->avoid_io()) {
@@ -448,9 +494,9 @@
     return;
 #endif
 
-
   COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd->c_str());
   string out;
+#if 0
   // TODO: Handle $(SHELL).
   FILE* fp = popen(cmd->c_str(), "r");
   while (true) {
@@ -462,6 +508,9 @@
       break;
     }
   }
+#else
+  RunCommand(*cmd, &out);
+#endif
 
   while (out[out.size()-1] == '\n')
     out.pop_back();