Bazel client: depend less on <unistd.h>

We moved most of the functionality (e.g. _exit,
SetupStreams) into blaze_util_<platform> or
changed to alternative functions (fwrite + stderr
instead of write + STDERR_HANDLE).

This change brings us closer to compiling blaze.cc
with MSVC. We still have to move signal handlers
out of blaze.cc as well as code dealing with the
server PID.

See https://github.com/bazelbuild/bazel/issues/2107

--
MOS_MIGRATED_REVID=140123945
diff --git a/src/main/cpp/blaze.cc b/src/main/cpp/blaze.cc
index f8422f15..f74fa0e 100644
--- a/src/main/cpp/blaze.cc
+++ b/src/main/cpp/blaze.cc
@@ -1098,7 +1098,7 @@
         if (globals->server_pid != -1) {
           KillServerProcess(globals->server_pid);
         }
-        _exit(1);
+        blaze::ExitImmediately(1);
       }
       sigprintf("\n%s caught interrupt signal; shutting down.\n\n",
                 globals->options->product_name.c_str());
@@ -1321,19 +1321,6 @@
   blaze::SetEnv("LC_CTYPE", "en_US.ISO-8859-1");
 }
 
-static void SetupStreams() {
-  // Line-buffer stderr, since we always flush at the end of a server
-  // message.  This saves lots of single-char calls to write(2).
-  // This doesn't work if any writes to stderr have already occurred!
-  setlinebuf(stderr);
-
-  // Ensure we have three open fds.  Otherwise we can end up with
-  // bizarre things like stdout going to the lock file, etc.
-  if (fcntl(STDIN_FILENO, F_GETFL) == -1) open("/dev/null", O_RDONLY);
-  if (fcntl(STDOUT_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
-  if (fcntl(STDERR_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
-}
-
 static void CheckBinaryPath(const string& argv0) {
   if (argv0[0] == '/') {
     globals->binary_path = argv0;
@@ -1360,7 +1347,7 @@
   // Logging must be set first to assure no log statements are missed.
   blaze_util::SetLogHandler(std::move(log_handler));
   globals = new GlobalVariables(option_processor);
-  SetupStreams();
+  blaze::SetupStdStreams();
 
   // Must be done before command line parsing.
   ComputeWorkspace();
@@ -1640,18 +1627,19 @@
     }
 
     bool pipe_broken_now = false;
-    if (response.standard_output().size() > 0) {
-      int result = write(STDOUT_FILENO, response.standard_output().c_str(),
-                         response.standard_output().size());
-      if (result < 0 && errno == EPIPE) {
+
+    if (!response.standard_output().empty()) {
+      size_t size = response.standard_output().size();
+      size_t r = fwrite(response.standard_output().c_str(), 1, size, stdout);
+      if (r < size && errno == EPIPE) {
         pipe_broken_now = true;
       }
     }
 
-    if (response.standard_error().size() > 0) {
-      int result = write(STDERR_FILENO, response.standard_error().c_str(),
-                         response.standard_error().size());
-      if (result < 0 && errno == EPIPE) {
+    if (!response.standard_error().empty()) {
+      size_t size = response.standard_error().size();
+      size_t r = fwrite(response.standard_error().c_str(), 1, size, stderr);
+      if (r < size && errno == EPIPE) {
         pipe_broken_now = true;
       }
     }
diff --git a/src/main/cpp/blaze_util.h b/src/main/cpp/blaze_util.h
index e294a42..06c75ed 100644
--- a/src/main/cpp/blaze_util.h
+++ b/src/main/cpp/blaze_util.h
@@ -118,7 +118,7 @@
 // Returns true iff arg is a valid command line argument for bazel.
 bool IsArg(const std::string& arg);
 
-// Converts a project identifier to string.
+// Returns the string representation of `value`.
 // Workaround for mingw where std::to_string is not implemented.
 // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52015.
 template <typename T>
diff --git a/src/main/cpp/blaze_util_platform.h b/src/main/cpp/blaze_util_platform.h
index a4c9c20..67b685f 100644
--- a/src/main/cpp/blaze_util_platform.h
+++ b/src/main/cpp/blaze_util_platform.h
@@ -19,6 +19,8 @@
 
 #include <string>
 
+#include "src/main/cpp/util/port.h"
+
 namespace blaze {
 
 std::string GetProcessIdAsString();
@@ -159,6 +161,13 @@
 
 void UnsetEnv(const std::string& name);
 
+// Terminate the process immediately.
+// This is a wrapper around POSIX's _exit(2).
+ATTRIBUTE_NORETURN void ExitImmediately(int exit_code);
+
+// Ensure we have open file descriptors for stdin/stdout/stderr.
+void SetupStdStreams();
+
 }  // namespace blaze
 
 #endif  // BAZEL_SRC_MAIN_CPP_BLAZE_UTIL_PLATFORM_H_
diff --git a/src/main/cpp/blaze_util_posix.cc b/src/main/cpp/blaze_util_posix.cc
index b5d8caf..68f6aeb 100644
--- a/src/main/cpp/blaze_util_posix.cc
+++ b/src/main/cpp/blaze_util_posix.cc
@@ -16,6 +16,7 @@
 #include <fcntl.h>
 #include <limits.h>  // PATH_MAX
 #include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -375,4 +376,25 @@
   unsetenv(name.c_str());
 }
 
+ATTRIBUTE_NORETURN void ExitImmediately(int exit_code) {
+  _exit(exit_code);
+}
+
+void SetupStdStreams() {
+  // Set non-buffered output mode for stderr/stdout. The server already
+  // line-buffers messages where it makes sense, so there's no need to do set
+  // line-buffering here. On the other hand the server sometimes sends binary
+  // output (when for example a query returns results as proto), in which case
+  // we must not perform line buffering on the client side. So turn off
+  // buffering here completely.
+  setvbuf(stdout, NULL, _IONBF, 0);
+  setvbuf(stderr, NULL, _IONBF, 0);
+
+  // Ensure we have three open fds.  Otherwise we can end up with
+  // bizarre things like stdout going to the lock file, etc.
+  if (fcntl(STDIN_FILENO, F_GETFL) == -1) open("/dev/null", O_RDONLY);
+  if (fcntl(STDOUT_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
+  if (fcntl(STDERR_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
+}
+
 }   // namespace blaze.
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index 87406f9..962bd28 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -1070,6 +1070,37 @@
 #endif  // COMPILER_MSVC
 }
 
+ATTRIBUTE_NORETURN void ExitImmediately(int exit_code) {
+#ifdef COMPILER_MSVC
+  // TODO(bazel-team): implement this.
+  pdie(255, "blaze::ExitImmediately is not implemented on Windows");
+#else  // not COMPILER_MSVC
+  _exit(exit_code);
+#endif  // COMPILER_MSVC
+}
+
+void SetupStdStreams() {
+#ifdef COMPILER_MSVC
+  // TODO(bazel-team): implement this.
+  pdie(255, "blaze::SetupStdStreams is not implemented on Windows");
+#else  // not COMPILER_MSVC
+  // Set non-buffered output mode for stderr/stdout. The server already
+  // line-buffers messages where it makes sense, so there's no need to do set
+  // line-buffering here. On the other hand the server sometimes sends binary
+  // output (when for example a query returns results as proto), in which case
+  // we must not perform line buffering on the client side. So turn off
+  // buffering here completely.
+  setvbuf(stdout, NULL, _IONBF, 0);
+  setvbuf(stderr, NULL, _IONBF, 0);
+
+  // Ensure we have three open fds.  Otherwise we can end up with
+  // bizarre things like stdout going to the lock file, etc.
+  if (fcntl(STDIN_FILENO, F_GETFL) == -1) open("/dev/null", O_RDONLY);
+  if (fcntl(STDOUT_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
+  if (fcntl(STDERR_FILENO, F_GETFL) == -1) open("/dev/null", O_WRONLY);
+#endif  // COMPILER_MSVC
+}
+
 LARGE_INTEGER WindowsClock::GetFrequency() {
   LARGE_INTEGER result;
   if (!QueryPerformanceFrequency(&result)) {
diff --git a/src/main/cpp/workspace_layout.h b/src/main/cpp/workspace_layout.h
index 62de291..f02371f 100644
--- a/src/main/cpp/workspace_layout.h
+++ b/src/main/cpp/workspace_layout.h
@@ -69,7 +69,7 @@
   static bool WorkspaceRelativizeRcFilePath(const std::string& workspace,
                                             std::string* path_fragment);
 
-  static constexpr char WorkspacePrefix[] = "%workspace%/";
+  static constexpr const char WorkspacePrefix[] = "%workspace%/";
   static const int WorkspacePrefixLength = sizeof WorkspacePrefix - 1;
 };