aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-10-16 23:43:11 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2020-10-16 23:43:11 +0300
commitfe03b65c8bd952ac26a516e23f4c04bb014455e9 (patch)
tree6881a7039c95f502409623441b7b5e29bd02a3d4
parentsome more process creation tests (diff)
downloadwinapi-common-fe03b65c8bd952ac26a516e23f4c04bb014455e9.tar.gz
winapi-common-fe03b65c8bd952ac26a516e23f4c04bb014455e9.zip
CommandLine: saner API
-rw-r--r--include/winapi/cmd_line.hpp36
-rw-r--r--src/cmd_line.cpp130
-rw-r--r--src/process.cpp2
-rw-r--r--test/unit_tests/cmd_line.cpp6
4 files changed, 84 insertions, 90 deletions
diff --git a/include/winapi/cmd_line.hpp b/include/winapi/cmd_line.hpp
index 60bc56a..176cf3d 100644
--- a/include/winapi/cmd_line.hpp
+++ b/include/winapi/cmd_line.hpp
@@ -17,7 +17,9 @@ class CommandLine {
public:
static CommandLine query();
- static CommandLine build_from_main(int argc, wchar_t* argv[]);
+ static CommandLine parse(const std::string&);
+
+ static CommandLine from_main(int argc, wchar_t* argv[]);
CommandLine() = default;
@@ -27,37 +29,29 @@ public:
CommandLine(std::string&& argv0, std::vector<std::string>&& args = {})
: argv0(std::move(argv0)), args(std::move(args)) {}
- bool has_argv0() const { return !argv0.empty(); }
-
- std::string get_argv0() const { return argv0; }
-
- std::string escape_argv0() const { return escape(get_argv0()); }
+ static std::string escape(const std::string&);
- bool has_args() const { return !get_args().empty(); }
+ static std::string escape_cmd(const std::string&);
- const std::vector<std::string>& get_args() const { return args; }
+ std::string to_string() const;
- std::vector<std::string> escape_args() const;
+ std::string args_to_string() const;
- static BOOST_CONSTEXPR char sep() { return ' '; }
+ std::string get_argv0() const { return argv0; }
- std::string join_args() const;
+ bool has_args() const { return !get_args().empty(); }
- std::string join() const;
+ const std::vector<std::string>& get_args() const { return args; }
private:
- static CommandLine build_from_string(const std::string&);
-
- static CommandLine build_from_string(std::wstring);
+ static BOOST_CONSTEXPR char token_sep() { return ' '; }
- static std::string escape_for_cmd(const std::string&);
-
- static std::string escape(const std::string&);
+ std::string escape_argv0() const { return escape(get_argv0()); }
- CommandLine(std::vector<std::string>&& args) : args(std::move(args)) {}
+ std::vector<std::string> escape_args() const;
- const std::string argv0;
- const std::vector<std::string> args;
+ std::string argv0;
+ std::vector<std::string> args;
};
} // namespace winapi
diff --git a/src/cmd_line.cpp b/src/cmd_line.cpp
index b771299..468bb96 100644
--- a/src/cmd_line.cpp
+++ b/src/cmd_line.cpp
@@ -18,8 +18,8 @@
#include <cstddef>
#include <memory>
#include <stdexcept>
-#include <sstream>
#include <string>
+#include <sstream>
#include <utility>
#include <vector>
@@ -30,59 +30,11 @@ 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) {
+CommandLine do_parse(std::wstring src) {
boost::trim(src);
- if (src.empty())
- return {};
+ if (src.empty()) {
+ throw std::runtime_error{"Command line cannot be an empty string"};
+ }
int argc = 0;
std::unique_ptr<wchar_t*, LocalDelete> argv{::CommandLineToArgvW(src.c_str(), &argc)};
@@ -90,8 +42,9 @@ CommandLine CommandLine::build_from_string(std::wstring src) {
if (argv.get() == NULL)
throw error::windows(GetLastError(), "CommandLineToArgvW");
- if (argc == 0)
- return {};
+ if (argc == 0) {
+ throw std::runtime_error{"Command line must contain at least one token"};
+ }
std::string argv0{narrow(argv.get()[0])};
@@ -104,17 +57,31 @@ CommandLine CommandLine::build_from_string(std::wstring src) {
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{"^!\"%&()<>|"};
+} // namespace
- 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;
+CommandLine CommandLine::query() {
+ return do_parse(::GetCommandLineW());
+}
+
+CommandLine CommandLine::parse(const std::string& src) {
+ return do_parse(widen(src));
+}
+
+CommandLine CommandLine::from_main(int argc, wchar_t* argv[]) {
+ if (argc < 1)
+ throw std::range_error{"argc must be a positive number"};
+
+ 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::string CommandLine::escape(const std::string& arg) {
@@ -154,4 +121,37 @@ std::string CommandLine::escape(const std::string& arg) {
return safe;
}
+std::string CommandLine::escape_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::to_string() const {
+ std::ostringstream oss;
+ oss << escape(get_argv0());
+ if (has_args())
+ oss << token_sep() << args_to_string();
+ return oss.str();
+}
+
+std::string CommandLine::args_to_string() const {
+ return boost::algorithm::join(escape_args(), std::string{token_sep()});
+}
+
+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;
+}
+
} // namespace winapi
diff --git a/src/process.cpp b/src/process.cpp
index 508f9e1..f95bc56 100644
--- a/src/process.cpp
+++ b/src/process.cpp
@@ -52,7 +52,7 @@ Handle create_process(EscapedCommandLine cmd_line, Process::IO& io) {
}
EscapedCommandLine escape_command_line(const CommandLine& cmd_line) {
- const auto unicode_cmd_line = widen(cmd_line.join());
+ const auto unicode_cmd_line = widen(cmd_line.to_string());
EscapedCommandLine buffer;
buffer.reserve(unicode_cmd_line.size() + 1);
buffer.assign(unicode_cmd_line.cbegin(), unicode_cmd_line.cend());
diff --git a/test/unit_tests/cmd_line.cpp b/test/unit_tests/cmd_line.cpp
index 4506304..99646ff 100644
--- a/test/unit_tests/cmd_line.cpp
+++ b/test/unit_tests/cmd_line.cpp
@@ -13,7 +13,7 @@ BOOST_AUTO_TEST_SUITE(cmd_line_tests)
BOOST_AUTO_TEST_CASE(query) {
const auto cmd_line = winapi::CommandLine::query();
- BOOST_TEST(cmd_line.has_argv0());
+ BOOST_TEST(!cmd_line.get_argv0().empty());
BOOST_TEST_MESSAGE(cmd_line.get_argv0());
}
@@ -25,10 +25,10 @@ BOOST_AUTO_TEST_CASE(escape) {
LR"(path\to\dir\)",
LR"(weird\\argument)",
};
- const auto cmd_line = winapi::CommandLine::build_from_main(5, argv);
+ const auto cmd_line = winapi::CommandLine::from_main(5, argv);
const auto expected =
R"("test.exe" "arg1 arg2" "path\to\file" "path\to\dir\\" "weird\\argument")";
- BOOST_TEST(cmd_line.join() == expected);
+ BOOST_TEST(cmd_line.to_string() == expected);
}
BOOST_AUTO_TEST_SUITE_END()