From 5e86b92df7cea563e247ff971eb8ecd9162b4e43 Mon Sep 17 00:00:00 2001 From: Egor Tensin Date: Wed, 14 Oct 2020 20:40:15 +0300 Subject: cmd_line.cpp: move implementation from cmd_line.hpp --- CMakeLists.txt | 2 +- include/winapi/cmd_line.hpp | 138 +++----------------------------------- src/cmd_line.cpp | 157 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+), 130 deletions(-) create mode 100644 src/cmd_line.cpp 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 - -#include #include -// clang-format off -#include -#include -// clang-format on - -#include -#include -#include -#include #include #include #include @@ -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 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& get_args() const { return args; } - std::vector escape_args() const { - std::vector safe; - safe.reserve(args.size()); - for (const auto& arg : args) - safe.emplace_back(escape(arg)); - return safe; - } + std::vector 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 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 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&& 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 +// 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 +#include +#include + +#include +#include + +// clang-format off +#include +#include +// clang-format on + +#include +#include +#include +#include +#include +#include +#include + +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 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 CommandLine::escape_args() const { + std::vector 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 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 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 -- cgit v1.2.3