diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/commandline.cpp | 155 | ||||
| -rw-r--r-- | src/commandline.h | 51 | ||||
| -rw-r--r-- | src/main.cpp | 69 |
3 files changed, 190 insertions, 85 deletions
diff --git a/src/commandline.cpp b/src/commandline.cpp index f0bdadf9..588b5085 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -8,15 +8,22 @@ CommandLine::CommandLine() { createOptions(); m_commands.push_back(std::make_unique<CrashDumpCommand>()); + m_commands.push_back(std::make_unique<LaunchCommand>()); } -int CommandLine::run(int argc, char** argv) +std::optional<int> CommandLine::run(const std::wstring& line) { try { po::variables_map vm; - auto parsed = po::command_line_parser(argc, argv) + auto args = po::split_winmain(line); + if (!args.empty()) { + // remove program name + args.erase(args.begin()); + } + + auto parsed = po::wcommand_line_parser(args) .options(m_allOptions) .positional(m_positional) .allow_unregistered() @@ -25,10 +32,10 @@ int CommandLine::run(int argc, char** argv) po::store(parsed, vm); if (vm.count("command")) { - const auto cmd = vm["command"].as<std::string>(); + const auto commandName = vm["command"].as<std::string>(); for (auto&& c : m_commands) { - if (c->name() == cmd) { + if (c->name() == commandName) { auto co = c->options(); co.add_options() ("help", "shows this message"); @@ -39,9 +46,14 @@ int CommandLine::run(int argc, char** argv) // remove the command name itself opts.erase(opts.begin()); - parsed = po::command_line_parser(opts) - .options(co) - .run(); + po::wcommand_line_parser parser(opts); + parser.options(co); + + if (c->allow_unregistered()) { + parser.allow_unregistered(); + } + + parsed = parser.run(); po::store(parsed, vm); @@ -51,7 +63,7 @@ int CommandLine::run(int argc, char** argv) return 0; } - return c->run(vm); + return c->run(line, vm, opts); } } } @@ -62,13 +74,16 @@ int CommandLine::run(int argc, char** argv) return 0; } - return -1; + return {}; } catch(po::error& e) { env::Console console; - std::cerr << e.what() << "\n"; - std::cerr << usage() << "\n"; + + std::cerr + << e.what() << "\n" + << usage() << "\n"; + return 1; } } @@ -80,7 +95,8 @@ void CommandLine::createOptions() po::options_description options; options.add_options() - ("command", po::value<std::string>(), "command to execute"); + ("command", po::value<std::string>(), "command") + ("subargs", po::value<std::vector<std::string> >(), "args"); m_positional .add("command", 1) @@ -135,6 +151,11 @@ std::string Command::description() const return meta().description; } +bool Command::allow_unregistered() const +{ + return false; +} + po::options_description Command::options() const { return doOptions(); @@ -161,11 +182,32 @@ std::string Command::usage() const return oss.str(); } -int Command::run(po::variables_map& vm) +std::optional<int> Command::run( + const std::wstring& originalLine, + po::variables_map vm, + std::vector<std::wstring> untouched) { - return doRun(vm); + m_original = originalLine; + m_vm = vm; + m_untouched = untouched; + + return doRun(); } +const std::wstring& Command::originalCmd() const +{ + return m_original; +} + +const po::variables_map& Command::vm() const +{ + return m_vm; +} + +const std::vector<std::wstring>& Command::untouched() const +{ + return m_untouched; +} po::options_description CrashDumpCommand::doOptions() const @@ -183,11 +225,11 @@ Command::Meta CrashDumpCommand::meta() const return {"crashdump", "writes a crashdump for a running process of MO"}; } -int CrashDumpCommand::doRun(po::variables_map& vm) +std::optional<int> CrashDumpCommand::doRun() { env::Console console; - const auto typeString = vm["type"].as<std::string>(); + const auto typeString = vm()["type"].as<std::string>(); const auto type = env::coreDumpTypeFromString(typeString); // dump @@ -202,4 +244,85 @@ int CrashDumpCommand::doRun(po::variables_map& vm) return (b ? 0 : 1); } + +bool LaunchCommand::allow_unregistered() const +{ + return true; +} + +po::options_description LaunchCommand::doOptions() const +{ + return {}; +} + +Command::Meta LaunchCommand::meta() const +{ + return {"launch", ""}; +} + +std::optional<int> LaunchCommand::doRun() +{ + // needs at least the working directory and process name + if (untouched().size() < 2) { + return 1; + } + + std::vector<std::wstring> arg; + auto args = UntouchedCommandLineArguments(2, arg); + + return SpawnWaitProcess(arg[1].c_str(), args); +} + +int LaunchCommand::SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine) +{ + PROCESS_INFORMATION pi{ 0 }; + STARTUPINFO si{ 0 }; + si.cb = sizeof(si); + std::wstring commandLineCopy = commandLine; + + if (!CreateProcessW(NULL, &commandLineCopy[0], NULL, NULL, FALSE, 0, NULL, workingDirectory, &si, &pi)) { + // A bit of a problem where to log the error message here, at least this way you can get the message + // using a either DebugView or a live debugger: + std::wostringstream ost; + ost << L"CreateProcess failed: " << commandLine << ", " << GetLastError(); + OutputDebugStringW(ost.str().c_str()); + return -1; + } + + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exitCode = (DWORD)-1; + ::GetExitCodeProcess(pi.hProcess, &exitCode); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return static_cast<int>(exitCode); +} + +// Parses the first parseArgCount arguments of the current process command line and returns +// them in parsedArgs, the rest of the command line is returned untouched. +LPCWSTR LaunchCommand::UntouchedCommandLineArguments( + int parseArgCount, std::vector<std::wstring>& parsedArgs) +{ + LPCWSTR cmd = GetCommandLineW(); + LPCWSTR arg = nullptr; // to skip executable name + for (; parseArgCount >= 0 && *cmd; ++cmd) + { + if (*cmd == '"') { + int escaped = 0; + for (++cmd; *cmd && (*cmd != '"' || escaped % 2 != 0); ++cmd) + escaped = *cmd == '\\' ? escaped + 1 : 0; + } + if (*cmd == ' ') { + if (arg) + if (cmd-1 > arg && *arg == '"' && *(cmd-1) == '"') + parsedArgs.push_back(std::wstring(arg+1, cmd-1)); + else + parsedArgs.push_back(std::wstring(arg, cmd)); + arg = cmd + 1; + --parseArgCount; + } + } + return cmd; +} + } // namespace diff --git a/src/commandline.h b/src/commandline.h index d5ad5a32..b45a86c6 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -20,7 +20,12 @@ public: po::options_description options() const; std::string usage() const; - int run(po::variables_map& vm); + virtual bool allow_unregistered() const; + + std::optional<int> run( + const std::wstring& originalLine, + po::variables_map vm, + std::vector<std::wstring> untouched); protected: struct Meta @@ -30,7 +35,16 @@ protected: virtual po::options_description doOptions() const; virtual Meta meta() const = 0; - virtual int doRun(po::variables_map& vm) = 0; + virtual std::optional<int> doRun() = 0; + + const std::wstring& originalCmd() const; + const po::variables_map& vm() const; + const std::vector<std::wstring>& untouched() const; + +private: + std::wstring m_original; + po::variables_map m_vm; + std::vector<std::wstring> m_untouched; }; @@ -39,7 +53,36 @@ class CrashDumpCommand : public Command protected: po::options_description doOptions() const; Meta meta() const override; - int doRun(po::variables_map& vm) override; + std::optional<int> doRun() override; +}; + + +// this is the `launch` command used when starting a process from within the +// virtualized directory, see processrunner.cpp +// +// it has its own parsing of the command line to extract the argument after +// `launch` and use it as the cwd of the process, but pass the remaining +// arguments verbatim +// +// this is very old code that should probably never be changed +// +// note that it's actually buggy; in particular, it doesn't handle multiple +// whitespace between arguments +// +class LaunchCommand : public Command +{ +public: + bool allow_unregistered() const override; + +protected: + po::options_description doOptions() const; + Meta meta() const override; + std::optional<int> doRun() override; + + int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine); + + LPCWSTR UntouchedCommandLineArguments( + int parseArgCount, std::vector<std::wstring>& parsedArgs); }; @@ -48,7 +91,7 @@ class CommandLine public: CommandLine(); - int run(int argc, char** argv); + std::optional<int> run(const std::wstring& line); std::string usage(const Command* c=nullptr) const; private: diff --git a/src/main.cpp b/src/main.cpp index 8a006907..68dcfe11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -174,60 +174,6 @@ void setUnhandledExceptionHandler() prevTerminateHandler = std::set_terminate(terminateHandler); } -// Parses the first parseArgCount arguments of the current process command line and returns -// them in parsedArgs, the rest of the command line is returned untouched. -LPCWSTR UntouchedCommandLineArguments(int parseArgCount, std::vector<std::wstring>& parsedArgs) -{ - LPCWSTR cmd = GetCommandLineW(); - LPCWSTR arg = nullptr; // to skip executable name - for (; parseArgCount >= 0 && *cmd; ++cmd) - { - if (*cmd == '"') { - int escaped = 0; - for (++cmd; *cmd && (*cmd != '"' || escaped % 2 != 0); ++cmd) - escaped = *cmd == '\\' ? escaped + 1 : 0; - } - if (*cmd == ' ') { - if (arg) - if (cmd-1 > arg && *arg == '"' && *(cmd-1) == '"') - parsedArgs.push_back(std::wstring(arg+1, cmd-1)); - else - parsedArgs.push_back(std::wstring(arg, cmd)); - arg = cmd + 1; - --parseArgCount; - } - } - return cmd; -} - - -static int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine) { - PROCESS_INFORMATION pi{ 0 }; - STARTUPINFO si{ 0 }; - si.cb = sizeof(si); - std::wstring commandLineCopy = commandLine; - - if (!CreateProcessW(NULL, &commandLineCopy[0], NULL, NULL, FALSE, 0, NULL, workingDirectory, &si, &pi)) { - // A bit of a problem where to log the error message here, at least this way you can get the message - // using a either DebugView or a live debugger: - std::wostringstream ost; - ost << L"CreateProcess failed: " << commandLine << ", " << GetLastError(); - OutputDebugStringW(ost.str().c_str()); - return -1; - } - - WaitForSingleObject(pi.hProcess, INFINITE); - - DWORD exitCode = (DWORD)-1; - ::GetExitCodeProcess(pi.hProcess, &exitCode); - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - return static_cast<int>(exitCode); -} - -static DWORD WaitForProcess() { - -} static bool HaveWriteAccess(const std::wstring &path) { @@ -879,13 +825,13 @@ void initLogging() int main(int argc, char *argv[]) { - TimeThis tt("main to runApplication()"); cl::CommandLine cl; - const auto r = cl.run(argc, argv); - if (r >= 0) - return r; + const auto r = cl.run(GetCommandLineW()); + if (r) + return *r; + TimeThis tt("main to runApplication()"); initLogging(); //Make sure the configured temp folder exists @@ -896,13 +842,6 @@ int main(int argc, char *argv[]) //Should allow for better scaling of ui with higher resolution displays QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - if (argc >= 4) { - std::vector<std::wstring> arg; - auto args = UntouchedCommandLineArguments(2, arg); - if (arg[0] == L"launch") - return SpawnWaitProcess(arg[1].c_str(), args); - } - MOApplication application(argc, argv); QStringList arguments = application.arguments(); |
