summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorisanae <14251494+isanae@users.noreply.github.com>2020-07-18 03:38:11 -0400
committerisanae <14251494+isanae@users.noreply.github.com>2020-11-03 11:39:01 -0500
commitf121d92602772110b80ce8ee89fef82c475190d3 (patch)
tree44c7d3ff4d02cf3c298197c5c2aa02cf07664ebb /src
parent5e528fb4cf16ae208944d15d568c9140e3d741e4 (diff)
implemented `launch` as a command
Diffstat (limited to 'src')
-rw-r--r--src/commandline.cpp155
-rw-r--r--src/commandline.h51
-rw-r--r--src/main.cpp69
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();