From: Joel Rosdahl Date: Wed, 29 Jul 2020 12:29:14 +0000 (+0200) Subject: C++-ify win32argvtos X-Git-Tag: v4.0~264 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=88ecdc34365974186cd8248e88029d89ff4cfd85;p=thirdparty%2Fccache.git C++-ify win32argvtos --- diff --git a/src/Win32Util.cpp b/src/Win32Util.cpp index f046153fa..4af53abaa 100644 --- a/src/Win32Util.cpp +++ b/src/Win32Util.cpp @@ -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 diff --git a/src/Win32Util.hpp b/src/Win32Util.hpp index 3a7ed1c9f..7a5cfa75d 100644 --- a/src/Win32Util.hpp +++ b/src/Win32Util.hpp @@ -24,6 +24,11 @@ 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 diff --git a/src/execute.cpp b/src/execute.cpp index 89096aaaf..f97f60e5e 100644 --- a/src/execute.cpp +++ b/src/execute.cpp @@ -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(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(atfile.c_str()), @@ -212,7 +143,7 @@ win32execute(const char* path, } if (!ret) { ret = CreateProcess(full_path_win_ext, - args, + const_cast(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)", diff --git a/src/execute.hpp b/src/execute.hpp index 5bcff8ccd..bbcef5123 100644 --- a/src/execute.hpp +++ b/src/execute.hpp @@ -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, diff --git a/src/hashutil.cpp b/src/hashutil.cpp index a43e14cd4..56dede7f0 100644 --- a/src/hashutil.cpp +++ b/src/hashutil.cpp @@ -33,6 +33,10 @@ # 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(win32args.c_str()), diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 16aae0edd..cee9924a1 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -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 index 000000000..eb7dc8f59 --- /dev/null +++ b/unittest/test_Win32Util.cpp @@ -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();