diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cmd_line.cpp | 157 |
1 files changed, 157 insertions, 0 deletions
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 |