diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | include/winapi/cmd_line.hpp | 138 | ||||
-rw-r--r-- | src/cmd_line.cpp | 157 |
3 files changed, 167 insertions, 130 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d965c7..ee72ccb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ add_library(winapi_common ${winapi_common_include} ${winapi_common_src}) target_include_directories(winapi_common PUBLIC include/) add_subdirectory(3rdparty/winapi/utf8 EXCLUDE_FROM_ALL) -target_link_libraries(winapi_common PUBLIC winapi_utf8) +target_link_libraries(winapi_common PRIVATE winapi_utf8) find_package(Boost REQUIRED) target_link_libraries(winapi_common PUBLIC Boost::boost) diff --git a/include/winapi/cmd_line.hpp b/include/winapi/cmd_line.hpp index 025b09a..ac88286 100644 --- a/include/winapi/cmd_line.hpp +++ b/include/winapi/cmd_line.hpp @@ -5,22 +5,8 @@ #pragma once -#include "error.hpp" - -#include <winapi/utf8.hpp> - -#include <boost/algorithm/string.hpp> #include <boost/config.hpp> -// clang-format off -#include <windows.h> -#include <shellapi.h> -// clang-format on - -#include <cstddef> -#include <memory> -#include <sstream> -#include <stdexcept> #include <string> #include <utility> #include <vector> @@ -29,24 +15,9 @@ namespace winapi { class CommandLine { public: - static CommandLine query() { return build_from_string(::GetCommandLineW()); } - - static CommandLine build_from_main(int argc, wchar_t* argv[]) { - if (argc < 1) - throw std::range_error{"invalid argc value"}; - - std::string argv0{narrow(argv[0])}; - --argc; - ++argv; - - std::vector<std::string> args; - args.reserve(argc); + static CommandLine query(); - for (int i = 0; i < argc; ++i) - args.emplace_back(narrow(argv[i])); - - return {std::move(argv0), std::move(args)}; - } + static CommandLine build_from_main(int argc, wchar_t* argv[]); CommandLine() = default; @@ -60,113 +31,22 @@ public: const std::vector<std::string>& get_args() const { return args; } - std::vector<std::string> escape_args() const { - std::vector<std::string> safe; - safe.reserve(args.size()); - for (const auto& arg : args) - safe.emplace_back(escape(arg)); - return safe; - } + std::vector<std::string> escape_args() const; static BOOST_CONSTEXPR char sep() { return ' '; } - std::string join_args() const { - return boost::algorithm::join(escape_args(), std::string{sep()}); - } + std::string join_args() const; - std::string join() const { - if (!has_argv0()) - throw std::logic_error{"argv[0] isn't defined"}; - std::ostringstream oss; - oss << escape_argv0(); - if (has_args()) - oss << sep() << join_args(); - return oss.str(); - } + std::string join() const; private: - static CommandLine build_from_string(const std::string& src) { - return build_from_string(widen(src)); - } - - static CommandLine build_from_string(std::wstring src) { - boost::trim(src); - if (src.empty()) - return {}; - - int argc = 0; - std::unique_ptr<wchar_t*, LocalDelete> argv{::CommandLineToArgvW(src.c_str(), &argc)}; - - if (argv.get() == NULL) - throw error::windows(GetLastError(), "CommandLineToArgvW"); - - if (argc == 0) - return {}; - - std::string argv0{narrow(argv.get()[0])}; - - std::vector<std::string> args; - args.reserve(argc - 1); - - for (int i = 1; i < argc; ++i) - args.emplace_back(narrow(argv.get()[i])); - - return {std::move(argv0), std::move(args)}; - } - - struct LocalDelete { - void operator()(wchar_t* argv[]) const { ::LocalFree(argv); } - }; - - static std::string escape_for_cmd(const std::string& arg) { - BOOST_STATIC_CONSTEXPR auto escape_symbol = '^'; - static const std::string dangerous_symbols{"^!\"%&()<>|"}; - - auto safe = escape(arg); - for (const auto danger : dangerous_symbols) { - std::ostringstream replacement; - replacement << escape_symbol << danger; - boost::replace_all(safe, std::string{danger}, replacement.str()); - } - return safe; - } - - static std::string escape(const std::string& arg) { - std::string safe; - // Including the quotes: - safe.reserve(arg.length() + 2); - - safe.push_back('"'); - - for (auto it = arg.cbegin(); it != arg.cend(); ++it) { - std::size_t numof_backslashes = 0; - - for (; it != arg.cend() && *it == '\\'; ++it) - ++numof_backslashes; - - if (it == arg.cend()) { - safe.reserve(safe.capacity() + numof_backslashes); - safe.append(2 * numof_backslashes, '\\'); - break; - } - - switch (*it) { - case L'"': - safe.reserve(safe.capacity() + numof_backslashes + 1); - safe.append(2 * numof_backslashes + 1, '\\'); - break; + static CommandLine build_from_string(const std::string&); - default: - safe.append(numof_backslashes, '\\'); - break; - } + static CommandLine build_from_string(std::wstring); - safe.push_back(*it); - } + static std::string escape_for_cmd(const std::string&); - safe.push_back('"'); - return safe; - } + static std::string escape(const std::string&); CommandLine(std::vector<std::string>&& args) : args(std::move(args)) {} diff --git a/src/cmd_line.cpp b/src/cmd_line.cpp new file mode 100644 index 0000000..b771299 --- /dev/null +++ b/src/cmd_line.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "winapi-common" project. +// For details, see https://github.com/egor-tensin/winapi-common. +// Distributed under the MIT License. + +#include <winapi/cmd_line.hpp> +#include <winapi/error.hpp> +#include <winapi/utf8.hpp> + +#include <boost/algorithm/string.hpp> +#include <boost/config.hpp> + +// clang-format off +#include <windows.h> +#include <shellapi.h> +// clang-format on + +#include <cstddef> +#include <memory> +#include <stdexcept> +#include <sstream> +#include <string> +#include <utility> +#include <vector> + +namespace winapi { +namespace { + +struct LocalDelete { + void operator()(wchar_t* argv[]) const { ::LocalFree(argv); } +}; + +} // namespace + +CommandLine CommandLine::query() { + return build_from_string(::GetCommandLineW()); +} + +CommandLine CommandLine::build_from_main(int argc, wchar_t* argv[]) { + if (argc < 1) + throw std::range_error{"invalid argc value"}; + + std::string argv0{narrow(argv[0])}; + --argc; + ++argv; + + std::vector<std::string> args; + args.reserve(argc); + + for (int i = 0; i < argc; ++i) + args.emplace_back(narrow(argv[i])); + + return {std::move(argv0), std::move(args)}; +} + +std::vector<std::string> CommandLine::escape_args() const { + std::vector<std::string> safe; + safe.reserve(args.size()); + for (const auto& arg : args) + safe.emplace_back(escape(arg)); + return safe; +} + +std::string CommandLine::join_args() const { + return boost::algorithm::join(escape_args(), std::string{sep()}); +} + +std::string CommandLine::join() const { + if (!has_argv0()) + throw std::logic_error{"argv[0] isn't defined"}; + std::ostringstream oss; + oss << escape_argv0(); + if (has_args()) + oss << sep() << join_args(); + return oss.str(); +} + +CommandLine CommandLine::build_from_string(const std::string& src) { + return build_from_string(widen(src)); +} + +CommandLine CommandLine::build_from_string(std::wstring src) { + boost::trim(src); + if (src.empty()) + return {}; + + int argc = 0; + std::unique_ptr<wchar_t*, LocalDelete> argv{::CommandLineToArgvW(src.c_str(), &argc)}; + + if (argv.get() == NULL) + throw error::windows(GetLastError(), "CommandLineToArgvW"); + + if (argc == 0) + return {}; + + std::string argv0{narrow(argv.get()[0])}; + + std::vector<std::string> args; + args.reserve(argc - 1); + + for (int i = 1; i < argc; ++i) + args.emplace_back(narrow(argv.get()[i])); + + return {std::move(argv0), std::move(args)}; +} + +std::string CommandLine::escape_for_cmd(const std::string& arg) { + BOOST_STATIC_CONSTEXPR auto escape_symbol = '^'; + static const std::string dangerous_symbols{"^!\"%&()<>|"}; + + auto safe = escape(arg); + for (const auto danger : dangerous_symbols) { + std::ostringstream replacement; + replacement << escape_symbol << danger; + boost::replace_all(safe, std::string{danger}, replacement.str()); + } + return safe; +} + +std::string CommandLine::escape(const std::string& arg) { + std::string safe; + // Including the quotes: + safe.reserve(arg.length() + 2); + + safe.push_back('"'); + + for (auto it = arg.cbegin(); it != arg.cend(); ++it) { + std::size_t numof_backslashes = 0; + + for (; it != arg.cend() && *it == '\\'; ++it) + ++numof_backslashes; + + if (it == arg.cend()) { + safe.reserve(safe.capacity() + numof_backslashes); + safe.append(2 * numof_backslashes, '\\'); + break; + } + + switch (*it) { + case L'"': + safe.reserve(safe.capacity() + numof_backslashes + 1); + safe.append(2 * numof_backslashes + 1, '\\'); + break; + + default: + safe.append(numof_backslashes, '\\'); + break; + } + + safe.push_back(*it); + } + + safe.push_back('"'); + return safe; +} + +} // namespace winapi |