Enable bssl (md5sum, sha256sum, etc.) on Windows.

We deal with the difference between binary and text modes on Windows by
doing all I/O in binary mode (including, in particular,
stdin/stdout/stderr) and by treating text mode as equivalent to binary
mode (i.e. we use Unix line ending semantics).

Change-Id: I76a46d8d02cd7efe1931c8272d8f2c311aef3acb
Reviewed-on: https://boringssl-review.googlesource.com/3070
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/BUILDING b/BUILDING
index 88c96f4..8438daf 100644
--- a/BUILDING
+++ b/BUILDING
@@ -70,9 +70,6 @@
   * The bssl client, server, s_client, and s_server commands are not built on
     Windows.
 
-  * The bssl md5sum, sha1sum, sha256sum, etc. commands are not built on
-    Windows.
-
   * The tests written in Go do not work.
 
 [1] http://www.cmake.org/download/
diff --git a/tool/digest.cc b/tool/digest.cc
index 224b79c..9c75d2d 100644
--- a/tool/digest.cc
+++ b/tool/digest.cc
@@ -14,8 +14,6 @@
 
 #include <openssl/base.h>
 
-#if !defined(OPENSSL_WINDOWS)
-
 #include <memory>
 #include <string>
 #include <vector>
@@ -26,11 +24,23 @@
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+
+#if !defined(OPENSSL_WINDOWS)
 #include <unistd.h>
+#if !defined(O_BINARY)
+#define O_BINARY 0
+#endif
+#else
+#define NOMINMAX
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <io.h>
+#define PATH_MAX MAX_PATH
+#define read _read
+#endif
 
 #include <openssl/digest.h>
 
-
 struct close_delete {
   void operator()(int *fd) {
     close(*fd);
@@ -70,13 +80,14 @@
 static bool OpenFile(int *out_fd, const std::string &filename) {
   *out_fd = -1;
 
-  int fd = open(filename.c_str(), O_RDONLY);
+  int fd = open(filename.c_str(), O_RDONLY | O_BINARY);
   if (fd < 0) {
     fprintf(stderr, "Failed to open input file '%s': %s\n", filename.c_str(),
             strerror(errno));
     return false;
   }
 
+#if !defined(OPENSSL_WINDOWS)
   struct stat st;
   if (fstat(fd, &st)) {
     fprintf(stderr, "Failed to stat input file '%s': %s\n", filename.c_str(),
@@ -88,6 +99,7 @@
     fprintf(stderr, "%s: not a regular file\n", filename.c_str());
     goto err;
   }
+#endif
 
   *out_fd = fd;
   return true;
@@ -179,6 +191,14 @@
     return false;
   }
 
+  // TODO: When given "--binary" or "-b", we should print " *" instead of "  "
+  // between the digest and the filename.
+  //
+  // MSYS and Cygwin md5sum default to binary mode by default, whereas other
+  // platforms' tools default to text mode by default. We default to text mode
+  // by default and consider text mode equivalent to binary mode (i.e. we
+  // always use Unix semantics, even on Windows), which means that our default
+  // output will differ from the MSYS and Cygwin tools' default output.
   printf("%s  %s\n", hex_digest.c_str(),
          source.is_stdin() ? "-" : source.filename().c_str());
   return true;
@@ -213,7 +233,7 @@
       return false;
     }
 
-    file = fdopen(fd, "r");
+    file = fdopen(fd, "rb");
     if (!file) {
       perror("fdopen");
       close(fd);
@@ -366,7 +386,7 @@
         switch (arg[i]) {
           case 'b':
           case 't':
-            // Binary/text mode – irrelevent.
+            // Binary/text mode – irrelevent, even on Windows.
             break;
           case 'c':
             check_mode = true;
@@ -381,7 +401,7 @@
         }
       }
     } else if (arg == "--binary" || arg == "--text") {
-      // Binary/text mode – irrelevent.
+      // Binary/text mode – irrelevent, even on Windows.
     } else if (arg == "--check") {
       check_mode = true;
     } else if (arg == "--quiet") {
@@ -455,5 +475,3 @@
 bool SHA512Sum(const std::vector<std::string> &args) {
   return DigestSum(EVP_sha512(), args);
 }
-
-#endif  /* !OPENSSL_WINDOWS */
diff --git a/tool/tool.cc b/tool/tool.cc
index 88b6f24..3b24be5 100644
--- a/tool/tool.cc
+++ b/tool/tool.cc
@@ -18,7 +18,10 @@
 #include <openssl/err.h>
 #include <openssl/ssl.h>
 
-#if !defined(OPENSSL_WINDOWS)
+#if defined(OPENSSL_WINDOWS)
+#include <fcntl.h>
+#include <io.h>
+#else
 #include <libgen.h>
 #endif
 
@@ -26,13 +29,13 @@
 #if !defined(OPENSSL_WINDOWS)
 bool Client(const std::vector<std::string> &args);
 bool Server(const std::vector<std::string> &args);
+#endif
 bool MD5Sum(const std::vector<std::string> &args);
 bool SHA1Sum(const std::vector<std::string> &args);
 bool SHA224Sum(const std::vector<std::string> &args);
 bool SHA256Sum(const std::vector<std::string> &args);
 bool SHA384Sum(const std::vector<std::string> &args);
 bool SHA512Sum(const std::vector<std::string> &args);
-#endif
 bool DoPKCS12(const std::vector<std::string> &args);
 bool Speed(const std::vector<std::string> &args);
 
@@ -51,13 +54,13 @@
   { "s_client", Client },
   { "server", Server },
   { "s_server", Server },
+#endif
   { "md5sum", MD5Sum },
   { "sha1sum", SHA1Sum },
   { "sha224sum", SHA224Sum },
   { "sha256sum", SHA256Sum },
   { "sha384sum", SHA384Sum },
   { "sha512sum", SHA512Sum },
-#endif
   { "", nullptr },
 };
 
@@ -87,6 +90,25 @@
 }
 
 int main(int argc, char **argv) {
+#if defined(OPENSSL_WINDOWS)
+  // Read and write in binary mode. This makes bssl on Windows consistent with
+  // bssl on other platforms, and also makes it consistent with MSYS's commands
+  // like diff(1) and md5sum(1). This is especially important for the digest
+  // commands.
+  if (_setmode(_fileno(stdin), _O_BINARY) == -1) {
+    perror("_setmode(_fileno(stdin), O_BINARY)");
+    return 1;
+  }
+  if (_setmode(_fileno(stdout), _O_BINARY) == -1) {
+    perror("_setmode(_fileno(stdout), O_BINARY)");
+    return 1;
+  }
+  if (_setmode(_fileno(stderr), _O_BINARY) == -1) {
+    perror("_setmode(_fileno(stderr), O_BINARY)");
+    return 1;
+  }
+#endif
+
   SSL_library_init();
 
   int starting_arg = 1;