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 --- src/cmd_line.cpp | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/cmd_line.cpp (limited to 'src/cmd_line.cpp') 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