]> git.ipfire.org Git - thirdparty/ccache.git/commitdiff
C++-ify win32argvtos
authorJoel Rosdahl <joel@rosdahl.net>
Wed, 29 Jul 2020 12:29:14 +0000 (14:29 +0200)
committerJoel Rosdahl <joel@rosdahl.net>
Wed, 29 Jul 2020 14:43:59 +0000 (16:43 +0200)
src/Win32Util.cpp
src/Win32Util.hpp
src/execute.cpp
src/execute.hpp
src/hashutil.cpp
unittest/CMakeLists.txt
unittest/test_Win32Util.cpp [new file with mode: 0644]

index f046153fa7773415de4b99cec2ba05b8066c951c..4af53abaa8ddbac103fef18e2f4ec4bbdaa5c5d3 100644 (file)
@@ -38,4 +38,43 @@ error_message(DWORD error_code)
   return message;
 }
 
+std::string
+argv_to_string(const char* const* argv, const std::string& prefix)
+{
+  std::string result;
+  size_t i = 0;
+  const char* arg = prefix.empty() ? argv[i++] : prefix.c_str();
+
+  do {
+    int bs = 0;
+    result += '"';
+    for (size_t j = 0; arg[j]; ++j) {
+      switch (arg[j]) {
+      case '\\':
+        ++bs;
+        break;
+      // Fallthrough.
+      case '"':
+        bs = (bs << 1) + 1;
+      // Fallthrough.
+      default:
+        while (bs > 0) {
+          result += '\\';
+          --bs;
+        }
+        result += arg[j];
+      }
+    }
+    bs <<= 1;
+    while (bs > 0) {
+      result += '\\';
+      --bs;
+    }
+    result += "\" ";
+  } while ((arg = argv[i++]));
+
+  result.resize(result.length() - 1);
+  return result;
+}
+
 } // namespace Win32Util
index 3a7ed1c9f589cd95b79c0fa6c52ee707ae71346f..7a5cfa75d87a1ee210a9e711a1b15a22d73301a1 100644 (file)
 
 namespace Win32Util {
 
+// Recreate a Windows command line string based on `argv`. If `prefix` is
+// non-empty, add it as the first argument.
+std::string argv_to_string(const char* const* argv, const std::string& prefix);
+
+// Return the error message corresponding to `error_code`.
 std::string error_message(DWORD error_code);
 
 } // namespace Win32Util
index 89096aaaf0ec21bfcdc81ef4080addad764b30d7..f97f60e5e4da2c5d1d7a7632d298686028c4244b 100644 (file)
@@ -42,75 +42,8 @@ execute(const char* const* argv, Fd&& fd_out, Fd&& fd_err, pid_t* /*pid*/)
   return win32execute(argv[0], argv, 1, fd_out.release(), fd_err.release());
 }
 
-// Re-create a win32 command line string based on **argv.
-// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
-char*
-win32argvtos(const char* prefix, const char* const* argv, int* length)
-{
-  int i = 0;
-  int k = 0;
-  const char* arg = prefix ? prefix : argv[i++];
-  do {
-    int bs = 0;
-    for (int j = 0; arg[j]; j++) {
-      switch (arg[j]) {
-      case '\\':
-        bs++;
-        break;
-      case '"':
-        bs = (bs << 1) + 1;
-      // Fallthrough.
-      default:
-        k += bs + 1;
-        bs = 0;
-      }
-    }
-    k += (bs << 1) + 3;
-  } while ((arg = argv[i++]));
-
-  char* ptr = static_cast<char*>(malloc(k + 1));
-  char* str = ptr;
-  if (!str) {
-    *length = 0;
-    return NULL;
-  }
-
-  i = 0;
-  arg = prefix ? prefix : argv[i++];
-  do {
-    int bs = 0;
-    *ptr++ = '"';
-    for (int j = 0; arg[j]; j++) {
-      switch (arg[j]) {
-      case '\\':
-        bs++;
-        break;
-      // Fallthrough.
-      case '"':
-        bs = (bs << 1) + 1;
-      // Fallthrough.
-      default:
-        while (bs && bs--) {
-          *ptr++ = '\\';
-        }
-        *ptr++ = arg[j];
-      }
-    }
-    bs <<= 1;
-    while (bs && bs--) {
-      *ptr++ = '\\';
-    }
-    *ptr++ = '"';
-    *ptr++ = ' ';
-  } while ((arg = argv[i++]));
-  ptr[-1] = '\0';
-
-  *length = ptr - str - 1;
-  return str;
-}
-
 std::string
-win32getshell(const char* path)
+win32getshell(const std::string& path)
 {
   const char* path_env = getenv("PATH");
   std::string sh;
@@ -187,16 +120,14 @@ win32execute(const char* path,
     }
   }
 
-  int length;
-  const char* prefix = sh.empty() ? nullptr : sh.c_str();
-  char* args = win32argvtos(prefix, argv, &length);
+  std::string args = Win32Util::argv_to_string(argv, sh);
   const char* ext = strrchr(path, '.');
   char full_path_win_ext[MAX_PATH] = {0};
   add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path);
   BOOL ret = FALSE;
-  if (length > 8192) {
+  if (args.length() > 8192) {
     TemporaryFile tmp_file(path);
-    Util::write_fd(*tmp_file.fd, args, length);
+    Util::write_fd(*tmp_file.fd, args.data(), args.length());
     std::string atfile = fmt::format("\"@{}\"", tmp_file.path);
     ret = CreateProcess(nullptr,
                         const_cast<char*>(atfile.c_str()),
@@ -212,7 +143,7 @@ win32execute(const char* path,
   }
   if (!ret) {
     ret = CreateProcess(full_path_win_ext,
-                        args,
+                        const_cast<char*>(args.c_str()),
                         nullptr,
                         nullptr,
                         1,
@@ -226,7 +157,6 @@ win32execute(const char* path,
     close(fd_stdout);
     close(fd_stderr);
   }
-  free(args);
   if (ret == 0) {
     DWORD error = GetLastError();
     cc_log("failed to execute %s: %s (%lu)",
index 5bcff8ccdc80ec467916ed99c48511d5d70bff62..bbcef5123c629aedc08f168dacc20b09ad53e0eb 100644 (file)
@@ -39,8 +39,7 @@ std::string find_executable_in_path(const std::string& name,
                                     const std::string& path);
 
 #ifdef _WIN32
-char* win32argvtos(const char* prefix, const char* const* argv, int* length);
-std::string win32getshell(const char* path);
+std::string win32getshell(const std::string& path);
 int win32execute(const char* path,
                  const char* const* argv,
                  int doreturn,
index a43e14cd4eef67fd45ac3690052da5707732913d..56dede7f086a216568012c108fbc9202ffe0bc23 100644 (file)
 #  include "InodeCache.hpp"
 #endif
 
+#ifdef _WIN32
+#  include "Win32Util.hpp"
+#endif
+
 #include "third_party/xxhash.h"
 
 // With older GCC (libgcc), __builtin_cpu_supports("avx2) returns true if AVX2
@@ -423,7 +427,7 @@ hash_command_output(Hash& hash, const char* command, const char* compiler)
   if (path.empty()) {
     path = args[0];
   }
-  std::string sh = win32getshell(path.c_str());
+  std::string sh = win32getshell(path);
   if (!sh.empty()) {
     path = sh;
   }
@@ -443,11 +447,7 @@ hash_command_output(Hash& hash, const char* command, const char* compiler)
   if (using_cmd_exe) {
     win32args = command; // quoted
   } else {
-    int length;
-    const char* prefix = sh.empty() ? nullptr : sh.c_str();
-    char* args = win32argvtos(prefix, argv.data(), &length);
-    win32args = args;
-    free(args);
+    win32args = Win32Util::argv_to_string(argv.data(), sh);
   }
   BOOL ret = CreateProcess(path.c_str(),
                            const_cast<char*>(win32args.c_str()),
index 16aae0edd48c81325bec5c810578f372ccc28ec9..cee9924a1b04a4139f11fdf73163f174063886b4 100644 (file)
@@ -23,6 +23,10 @@ if(INODE_CACHE_SUPPORTED)
   list(APPEND source_files test_InodeCache.cpp)
 endif()
 
+if(WIN32)
+  list(APPEND source_files test_Win32Util.cpp)
+endif()
+
 add_executable(unittest ${source_files})
 
 target_link_libraries(
diff --git a/unittest/test_Win32Util.cpp b/unittest/test_Win32Util.cpp
new file mode 100644 (file)
index 0000000..eb7dc8f
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 Joel Rosdahl and other contributors
+//
+// See doc/AUTHORS.adoc for a complete list of contributors.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+// more details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 51
+// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#include "../src/Win32Util.hpp"
+#include "TestUtil.hpp"
+
+#include "third_party/doctest.h"
+
+TEST_SUITE_BEGIN("Win32Util");
+
+TEST_CASE("Win32Util::argv_to_string")
+{
+  {
+    const char* const argv[] = {"a", nullptr};
+    CHECK(Win32Util::argv_to_string(argv, "") == R"("a")");
+  }
+  {
+    const char* const argv[] = {"a", nullptr};
+    CHECK(Win32Util::argv_to_string(argv, "p") == R"("p" "a")");
+  }
+  {
+    const char* const argv[] = {"a", "b c", "\"d\"", "'e'", "\\\"h", nullptr};
+    CHECK(Win32Util::argv_to_string(argv, "")
+          == R"("a" "b c" "\"d\"" "'e'" "\\\"h")");
+  }
+}
+
+TEST_SUITE_END();