From: Joel Rosdahl Date: Mon, 2 Oct 2023 16:38:05 +0000 (+0200) Subject: refactor: Move Win32Util::argv_to_string to util X-Git-Tag: v4.9~40 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f067b3f42ba36ccead0c3595ce4befad44f59ea4;p=thirdparty%2Fccache.git refactor: Move Win32Util::argv_to_string to util --- diff --git a/src/Win32Util.cpp b/src/Win32Util.cpp index 340430b50..d50a4bd63 100644 --- a/src/Win32Util.cpp +++ b/src/Win32Util.cpp @@ -49,49 +49,4 @@ error_message(DWORD error_code) return message; } -std::string -argv_to_string(const char* const* argv, - const std::string& prefix, - bool escape_backslashes) -{ - 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 '\\': - if (!escape_backslashes) { - ++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 0130e90b5..b73224090 100644 --- a/src/Win32Util.hpp +++ b/src/Win32Util.hpp @@ -26,14 +26,6 @@ namespace Win32Util { -// Recreate a Windows command line string based on `argv`. If `prefix` is -// non-empty, add it as the first argument. If `escape_backslashes` is true, -// emit an additional backslash for each backslash that is not preceding '"' and -// is not at the end of `argv[i]` either. -std::string argv_to_string(const char* const* argv, - const std::string& prefix, - bool escape_backslashes = false); - // Return the error message corresponding to `error_code`. std::string error_message(DWORD error_code); diff --git a/src/execute.cpp b/src/execute.cpp index 2b23b54b2..6c52fa874 100644 --- a/src/execute.cpp +++ b/src/execute.cpp @@ -216,7 +216,7 @@ win32execute(const char* path, } } - std::string args = Win32Util::argv_to_string(argv, sh); + std::string args = util::format_argv_as_win32_command_string(argv, sh); std::string full_path = util::add_exe_suffix(path); fs::path tmp_file_path; @@ -229,7 +229,7 @@ win32execute(const char* path, if (args.length() > 8192) { auto tmp_file = util::value_or_throw( util::TemporaryFile::create(FMT("{}/cmd_args", temp_dir))); - args = Win32Util::argv_to_string(argv + 1, sh, true); + args = util::format_argv_as_win32_command_string(argv + 1, sh, true); util::write_fd(*tmp_file.fd, args.data(), args.length()); args = FMT(R"("{}" "@{}")", full_path, tmp_file.path); tmp_file_path = tmp_file.path; diff --git a/src/hashutil.cpp b/src/hashutil.cpp index ede773209..7665c4e00 100644 --- a/src/hashutil.cpp +++ b/src/hashutil.cpp @@ -406,7 +406,7 @@ hash_command_output(Hash& hash, if (using_cmd_exe) { win32args = adjusted_command; // quoted } else { - win32args = Win32Util::argv_to_string(argv.data(), sh); + win32args = util::format_argv_as_win32_command_string(argv.data(), sh); } BOOL ret = CreateProcess(path.c_str(), const_cast(win32args.c_str()), diff --git a/src/util/string.cpp b/src/util/string.cpp index 8db2f0464..45f80dc02 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -47,6 +47,51 @@ split_into(std::string_view string, namespace util { +std::string +format_argv_as_win32_command_string(const char* const* argv, + const std::string& prefix, + bool escape_backslashes) +{ + 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 '\\': + if (!escape_backslashes) { + ++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; +} + std::string format_argv_for_logging(const char* const* argv) { diff --git a/src/util/string.hpp b/src/util/string.hpp index 3b263651a..4c981eb34 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -45,6 +45,15 @@ enum class SizeUnitPrefixType { binary, decimal }; // Return true if `suffix` is a suffix of `string`. bool ends_with(std::string_view string, std::string_view suffix); +// Recreate a Windows command line string based on `argv`. If `prefix` is +// non-empty, add it as the first argument. If `escape_backslashes` is true, +// emit an additional backslash for each backslash that is not preceding '"' and +// is not at the end of `argv[i]` either. +std::string +format_argv_as_win32_command_string(const char* const* argv, + const std::string& prefix, + bool escape_backslashes = false); + // Format `argv` as a simple string for logging purposes. That is, the result is // not intended to be easily machine parsable. `argv` must be terminated by a // nullptr. diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index a34c57df3..b2bacc3a6 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -44,7 +44,7 @@ if(INODE_CACHE_SUPPORTED) endif() if(WIN32) - list(APPEND source_files test_bsdmkstemp.cpp test_Win32Util.cpp) + list(APPEND source_files test_bsdmkstemp.cpp) endif() file(GLOB headers *.hpp) diff --git a/unittest/test_Win32Util.cpp b/unittest/test_Win32Util.cpp deleted file mode 100644 index cff6a7f81..000000000 --- a/unittest/test_Win32Util.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// 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" - -#include - -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")"); - } - { - const char* const argv[] = {"a\\b\\c", nullptr}; - CHECK(Win32Util::argv_to_string(argv, "") == R"("a\b\c")"); - } - { - const char* const argv[] = {"a\\b\\c", nullptr}; - CHECK(Win32Util::argv_to_string(argv, "", true) == R"("a\\b\\c")"); - } - { - const char* const argv[] = {R"(a\b \"c\" \)", nullptr}; - CHECK(Win32Util::argv_to_string(argv, "") == R"("a\b \\\"c\\\" \\")"); - } - { - const char* const argv[] = {R"(a\b \"c\" \)", nullptr}; - CHECK(Win32Util::argv_to_string(argv, "", true) - == R"("a\\b \\\"c\\\" \\")"); - } -} - -TEST_SUITE_END(); diff --git a/unittest/test_util_string.cpp b/unittest/test_util_string.cpp index 8410c8228..1c49d70d5 100644 --- a/unittest/test_util_string.cpp +++ b/unittest/test_util_string.cpp @@ -39,6 +39,42 @@ operator==(std::pair> left, TEST_SUITE_BEGIN("util"); +TEST_CASE("util::format_argv_as_win32_command_string") +{ + { + const char* const argv[] = {"a", nullptr}; + CHECK(util::format_argv_as_win32_command_string(argv, "") == R"("a")"); + } + { + const char* const argv[] = {"a", nullptr}; + CHECK(util::format_argv_as_win32_command_string(argv, "p") == R"("p" "a")"); + } + { + const char* const argv[] = {"a", "b c", "\"d\"", "'e'", "\\\"h", nullptr}; + CHECK(util::format_argv_as_win32_command_string(argv, "") + == R"("a" "b c" "\"d\"" "'e'" "\\\"h")"); + } + { + const char* const argv[] = {"a\\b\\c", nullptr}; + CHECK(util::format_argv_as_win32_command_string(argv, "") == R"("a\b\c")"); + } + { + const char* const argv[] = {"a\\b\\c", nullptr}; + CHECK(util::format_argv_as_win32_command_string(argv, "", true) + == R"("a\\b\\c")"); + } + { + const char* const argv[] = {R"(a\b \"c\" \)", nullptr}; + CHECK(util::format_argv_as_win32_command_string(argv, "") + == R"("a\b \\\"c\\\" \\")"); + } + { + const char* const argv[] = {R"(a\b \"c\" \)", nullptr}; + CHECK(util::format_argv_as_win32_command_string(argv, "", true) + == R"("a\\b \\\"c\\\" \\")"); + } +} + TEST_CASE("util::format_argv_for_logging") { SUBCASE("nullptr")