From 5e528fb4cf16ae208944d15d568c9140e3d741e4 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 02:24:44 -0400 Subject: new CommandLine class implemented crashdump as a command, fixed dump_running_process.bat to use it attach to console if present instead of always create one --- src/commandline.cpp | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 src/commandline.cpp (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp new file mode 100644 index 00000000..f0bdadf9 --- /dev/null +++ b/src/commandline.cpp @@ -0,0 +1,205 @@ +#include "commandline.h" +#include "env.h" + +namespace cl +{ + +CommandLine::CommandLine() +{ + createOptions(); + m_commands.push_back(std::make_unique()); +} + +int CommandLine::run(int argc, char** argv) +{ + try + { + po::variables_map vm; + + auto parsed = po::command_line_parser(argc, argv) + .options(m_allOptions) + .positional(m_positional) + .allow_unregistered() + .run(); + + po::store(parsed, vm); + + if (vm.count("command")) { + const auto cmd = vm["command"].as(); + + for (auto&& c : m_commands) { + if (c->name() == cmd) { + auto co = c->options(); + co.add_options() + ("help", "shows this message"); + + auto opts = po::collect_unrecognized( + parsed.options, po::include_positional); + + // remove the command name itself + opts.erase(opts.begin()); + + parsed = po::command_line_parser(opts) + .options(co) + .run(); + + po::store(parsed, vm); + + if (vm.count("help")) { + env::Console console; + std::cout << usage(c.get()) << "\n"; + return 0; + } + + return c->run(vm); + } + } + } + + if (vm.count("help")) { + env::Console console; + std::cout << usage() << "\n"; + return 0; + } + + return -1; + } + catch(po::error& e) + { + env::Console console; + std::cerr << e.what() << "\n"; + std::cerr << usage() << "\n"; + return 1; + } +} + +void CommandLine::createOptions() +{ + m_visibleOptions.add_options() + ("help", "shows this message"); + + po::options_description options; + options.add_options() + ("command", po::value(), "command to execute"); + + m_positional + .add("command", 1) + .add("subargs", -1); + + m_allOptions.add(m_visibleOptions); + m_allOptions.add(options); +} + +std::string CommandLine::usage(const Command* c) const +{ + std::ostringstream oss; + + oss + << "\n" + << "Usage:\n"; + + if (c) { + oss + << " ModOrganizer.exe [options] " << c->name() << " [command-options]\n" + << "\n" + << "Command options:\n" + << c->options() << "\n"; + } else { + oss + << " ModOrganizer.exe [options] [[command] [command-options]]\n" + << "\n" + << "Commands:\n"; + + for (auto&& c : m_commands) { + oss << " " << c->name() << " " << c->description() << "\n"; + } + + oss << "\n"; + } + + oss + << "Global options:\n" + << m_visibleOptions << "\n"; + + return oss.str(); +} + + +std::string Command::name() const +{ + return meta().name; +} + +std::string Command::description() const +{ + return meta().description; +} + +po::options_description Command::options() const +{ + return doOptions(); +} + +po::options_description Command::doOptions() const +{ + // no-op + return {}; +} + +std::string Command::usage() const +{ + std::ostringstream oss; + + oss + << "\n" + << "Usage:\n" + << " ModOrganizer.exe [options] [[command] [command-options]]\n" + << "\n" + << "Options:\n" + << options() << "\n"; + + return oss.str(); +} + +int Command::run(po::variables_map& vm) +{ + return doRun(vm); +} + + + +po::options_description CrashDumpCommand::doOptions() const +{ + po::options_description d; + + d.add_options() + ("type", po::value()->default_value("mini"), "mini|data|full"); + + return d; +} + +Command::Meta CrashDumpCommand::meta() const +{ + return {"crashdump", "writes a crashdump for a running process of MO"}; +} + +int CrashDumpCommand::doRun(po::variables_map& vm) +{ + env::Console console; + + const auto typeString = vm["type"].as(); + const auto type = env::coreDumpTypeFromString(typeString); + + // dump + const auto b = env::coredumpOther(type); + if (!b) { + std::wcerr << L"\n>>>> a minidump file was not written\n\n"; + } + + std::wcerr << L"Press enter to continue..."; + std::wcin.get(); + + return (b ? 0 : 1); +} + +} // namespace -- cgit v1.3.1 From f121d92602772110b80ce8ee89fef82c475190d3 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 03:38:11 -0400 Subject: implemented `launch` as a command --- src/commandline.cpp | 155 ++++++++++++++++++++++++++++++++++++++++++++++------ src/commandline.h | 51 +++++++++++++++-- src/main.cpp | 69 ++--------------------- 3 files changed, 190 insertions(+), 85 deletions(-) (limited to 'src/commandline.cpp') 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()); + m_commands.push_back(std::make_unique()); } -int CommandLine::run(int argc, char** argv) +std::optional 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(); + const auto commandName = vm["command"].as(); 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(), "command to execute"); + ("command", po::value(), "command") + ("subargs", po::value >(), "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 Command::run( + const std::wstring& originalLine, + po::variables_map vm, + std::vector untouched) +{ + m_original = originalLine; + m_vm = vm; + m_untouched = untouched; + + return doRun(); +} + +const std::wstring& Command::originalCmd() const { - return doRun(vm); + return m_original; } +const po::variables_map& Command::vm() const +{ + return m_vm; +} + +const std::vector& 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 CrashDumpCommand::doRun() { env::Console console; - const auto typeString = vm["type"].as(); + const auto typeString = vm()["type"].as(); 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 LaunchCommand::doRun() +{ + // needs at least the working directory and process name + if (untouched().size() < 2) { + return 1; + } + + std::vector 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(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& 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 run( + const std::wstring& originalLine, + po::variables_map vm, + std::vector 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 doRun() = 0; + + const std::wstring& originalCmd() const; + const po::variables_map& vm() const; + const std::vector& untouched() const; + +private: + std::wstring m_original; + po::variables_map m_vm; + std::vector 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 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 doRun() override; + + int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine); + + LPCWSTR UntouchedCommandLineArguments( + int parseArgCount, std::vector& parsedArgs); }; @@ -48,7 +91,7 @@ class CommandLine public: CommandLine(); - int run(int argc, char** argv); + std::optional 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& 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(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 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(); -- cgit v1.3.1 From 75cc2ffead148ab2409cd1ef469613d2e9b80e17 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 04:06:18 -0400 Subject: removed unused `update` parameter moved --multiple to CommandLine --- src/commandline.cpp | 44 ++++++++++++++++++++++++++++++++++---------- src/commandline.h | 4 ++++ src/main.cpp | 7 +------ src/singleinstance.cpp | 10 ---------- src/singleinstance.h | 12 +----------- 5 files changed, 40 insertions(+), 37 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 588b5085..f84906e9 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -15,8 +15,6 @@ std::optional CommandLine::run(const std::wstring& line) { try { - po::variables_map vm; - auto args = po::split_winmain(line); if (!args.empty()) { // remove program name @@ -29,10 +27,10 @@ std::optional CommandLine::run(const std::wstring& line) .allow_unregistered() .run(); - po::store(parsed, vm); + po::store(parsed, m_vm); - if (vm.count("command")) { - const auto commandName = vm["command"].as(); + if (m_vm.count("command")) { + const auto commandName = m_vm["command"].as(); for (auto&& c : m_commands) { if (c->name() == commandName) { @@ -55,20 +53,20 @@ std::optional CommandLine::run(const std::wstring& line) parsed = parser.run(); - po::store(parsed, vm); + po::store(parsed, m_vm); - if (vm.count("help")) { + if (m_vm.count("help")) { env::Console console; std::cout << usage(c.get()) << "\n"; return 0; } - return c->run(line, vm, opts); + return c->run(line, m_vm, opts); } } } - if (vm.count("help")) { + if (m_vm.count("help")) { env::Console console; std::cout << usage() << "\n"; return 0; @@ -91,7 +89,8 @@ std::optional CommandLine::run(const std::wstring& line) void CommandLine::createOptions() { m_visibleOptions.add_options() - ("help", "shows this message"); + ("help", "shows this message") + ("multiple", "allows multiple instances of MO to run; see below"); po::options_description options; options.add_options() @@ -137,9 +136,34 @@ std::string CommandLine::usage(const Command* c) const << "Global options:\n" << m_visibleOptions << "\n"; + if (!c) { + oss << "\n" << more() << "\n"; + } + return oss.str(); } +bool CommandLine::multiple() const +{ + return (m_vm.count("multiple") > 0); +} + +std::string CommandLine::more() const +{ + return + "--multiple can be used to allow multiple instances of MO to run\n" + "simultaneously. This is unsupported and can create all sorts of weird\n" + "problems. To minimize the problems:\n" + "\n" + " 1) Never have multiple MO instances opened that manage the same game\n" + " instance.\n" + " 2) If an executable is launched from an instance, only this instance\n" + " may launch executables until all instances are closed.\n" + "\n" + "It is recommended to close _all_ instances of MO as soon as multiple\n" + "instances become unnecessary."; +} + std::string Command::name() const { diff --git a/src/commandline.h b/src/commandline.h index b45a86c6..deeb9923 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -94,12 +94,16 @@ public: std::optional run(const std::wstring& line); std::string usage(const Command* c=nullptr) const; + bool multiple() const; + private: po::options_description m_visibleOptions, m_allOptions; po::positional_options_description m_positional; std::vector> m_commands; + po::variables_map m_vm; void createOptions(); + std::string more() const; }; } // namespace diff --git a/src/main.cpp b/src/main.cpp index 68dcfe11..a1a4be01 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -852,12 +852,7 @@ int main(int argc, char *argv[]) SingleInstance::Flags siFlags = SingleInstance::NoFlags; - if (arguments.contains("update")) { - arguments.removeAll("update"); - siFlags |= SingleInstance::ForcePrimary; - } - - if (arguments.contains("--multiple")) { + if (cl.multiple()) { arguments.removeAll("--multiple"); siFlags |= SingleInstance::AllowMultiple; } diff --git a/src/singleinstance.cpp b/src/singleinstance.cpp index d38095df..e32c8de1 100644 --- a/src/singleinstance.cpp +++ b/src/singleinstance.cpp @@ -34,16 +34,6 @@ SingleInstance::SingleInstance(Flags flags, QObject *parent) : m_SharedMem.setKey(s_Key); if (!m_SharedMem.create(1)) { - if (flags.testFlag(ForcePrimary)) { - while (m_SharedMem.error() == QSharedMemory::AlreadyExists) { - Sleep(500); - if (m_SharedMem.create(1)) { - m_OwnsSM = true; - break; - } - } - } - if (m_SharedMem.error() == QSharedMemory::AlreadyExists) { if (!flags.testFlag(AllowMultiple)) { m_SharedMem.attach(); diff --git a/src/singleinstance.h b/src/singleinstance.h index 5c7cdf85..d500a056 100644 --- a/src/singleinstance.h +++ b/src/singleinstance.h @@ -40,19 +40,9 @@ public: { NoFlags = 0x00, - - // when set, this will be treated as the primary instance even if - // another instance is running. This is used after an update since the - // other instance is assumed to be in the process of quitting - // - // todo: this makes no sense. The second instance after an update needs - // to delete the files from before the update so the first instance - // needs to quit first anyway - ForcePrimary = 0x01, - // if another instance is running, run this one disconnected from the // shared memory - AllowMultiple = 0x02 + AllowMultiple = 0x01 }; using Flags = QFlags; -- cgit v1.3.1 From ccab9eae8df3cf5367ce5cf164c98d1534ac13cb Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 05:07:32 -0400 Subject: add warning when profile given with -p doesn't exist implemented moshortcut, nxm links and executable names as command line options --- src/commandline.cpp | 98 +++++++++++++++++++++++++++++++++-------- src/commandline.h | 13 ++++++ src/main.cpp | 119 ++++++++++++++++++++++---------------------------- src/moshortcut.cpp | 9 ++++ src/moshortcut.h | 8 ++-- src/organizercore.cpp | 9 +++- src/organizercore.h | 3 -- src/shared/util.cpp | 6 +++ src/shared/util.h | 2 + 9 files changed, 175 insertions(+), 92 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index f84906e9..f5c4df60 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -1,5 +1,6 @@ #include "commandline.h" #include "env.h" +#include "shared/util.h" namespace cl { @@ -29,21 +30,22 @@ std::optional CommandLine::run(const std::wstring& line) po::store(parsed, m_vm); + auto opts = po::collect_unrecognized( + parsed.options, po::include_positional); + + if (m_vm.count("command")) { const auto commandName = m_vm["command"].as(); for (auto&& c : m_commands) { if (c->name() == commandName) { + // remove the command name itself + opts.erase(opts.begin()); + auto co = c->options(); co.add_options() ("help", "shows this message"); - auto opts = po::collect_unrecognized( - parsed.options, po::include_positional); - - // remove the command name itself - opts.erase(opts.begin()); - po::wcommand_line_parser parser(opts); parser.options(co); @@ -72,6 +74,26 @@ std::optional CommandLine::run(const std::wstring& line) return 0; } + if (!opts.empty()) { + const auto qs = QString::fromStdWString(opts[0]); + m_shortcut = qs; + + if (!m_shortcut.isValid()) { + if (isNxmLink(qs)) { + m_nxmLink = qs; + } else { + m_executable = qs; + } + } + + // remove the shortcut/nxm/executable + opts.erase(opts.begin()); + + for (auto&& o : opts) { + m_untouched.push_back(QString::fromStdWString(o)); + } + } + return {}; } catch(po::error& e) @@ -86,11 +108,19 @@ std::optional CommandLine::run(const std::wstring& line) } } +void CommandLine::clear() +{ + m_vm.clear(); + m_shortcut = {}; + m_nxmLink = {}; +} + void CommandLine::createOptions() { m_visibleOptions.add_options() - ("help", "shows this message") - ("multiple", "allows multiple instances of MO to run; see below"); + ("help", "show this message") + ("multiple", "allow multiple instances of MO to run; see below") + ("profile,p", po::value(), "use the given profile (defaults to last used)"); po::options_description options; options.add_options() @@ -148,20 +178,50 @@ bool CommandLine::multiple() const return (m_vm.count("multiple") > 0); } +std::optional CommandLine::profile() const +{ + if (m_vm.count("profile")) { + return QString::fromStdString(m_vm["profile"].as()); + } + + return {}; +} + +const MOShortcut& CommandLine::shortcut() const +{ + return m_shortcut; +} + +std::optional CommandLine::nxmLink() const +{ + return m_nxmLink; +} + +std::optional CommandLine::executable() const +{ + return m_executable; +} + +const QStringList& CommandLine::untouched() const +{ + return m_untouched; +} + std::string CommandLine::more() const { return - "--multiple can be used to allow multiple instances of MO to run\n" - "simultaneously. This is unsupported and can create all sorts of weird\n" - "problems. To minimize the problems:\n" - "\n" - " 1) Never have multiple MO instances opened that manage the same game\n" - " instance.\n" - " 2) If an executable is launched from an instance, only this instance\n" - " may launch executables until all instances are closed.\n" - "\n" - "It is recommended to close _all_ instances of MO as soon as multiple\n" - "instances become unnecessary."; + "Multiple instances\n" + " --multiple can be used to allow multiple instances of MO to run\n" + " simultaneously. This is unsupported and can create all sorts of weird\n" + " problems. To minimize the problems:\n" + " \n" + " 1) Never have multiple MO instances opened that manage the same\n" + " game instance.\n" + " 2) If an executable is launched from an instance, only this\n" + " instance may launch executables until all instances are closed.\n" + " \n" + " It is recommended to close _all_ instances of MO as soon as multiple\n" + " instances become unnecessary."; } diff --git a/src/commandline.h b/src/commandline.h index deeb9923..cdd1c917 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -1,5 +1,6 @@ #pragma once +#include "moshortcut.h" #include #include @@ -92,15 +93,27 @@ public: CommandLine(); std::optional run(const std::wstring& line); + void clear(); + std::string usage(const Command* c=nullptr) const; bool multiple() const; + std::optional profile() const; + const MOShortcut& shortcut() const; + std::optional nxmLink() const; + std::optional executable() const; + + const QStringList& untouched() const; private: po::options_description m_visibleOptions, m_allOptions; po::positional_options_description m_positional; std::vector> m_commands; po::variables_map m_vm; + MOShortcut m_shortcut; + std::optional m_nxmLink; + std::optional m_executable; + QStringList m_untouched; void createOptions(); std::string more() const; diff --git a/src/main.cpp b/src/main.cpp index a1a4be01..3c5bc92d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -225,18 +225,13 @@ static bool HaveWriteAccess(const std::wstring &path) } -QString determineProfile(QStringList &arguments, const Settings &settings) +QString determineProfile(const cl::CommandLine& cl, const Settings &settings) { auto selectedProfileName = settings.game().selectedProfileName(); - { // see if there is a profile on the command line - int profileIndex = arguments.indexOf("-p", 1); - if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) { - log::debug("profile overwritten on command line"); - selectedProfileName = arguments.at(profileIndex + 1); - } - arguments.removeAt(profileIndex); - arguments.removeAt(profileIndex); + if (cl.profile()) { + log::debug("profile overwritten on command line"); + selectedProfileName = *cl.profile(); } if (!selectedProfileName) { @@ -481,7 +476,7 @@ static QString getVersionDisplayString() int runApplication( - MOApplication &application, QStringList& arguments, + MOApplication &application, const cl::CommandLine& cl, SingleInstance &instance, const QString &splashPath) { TimeThis tt("runApplication() to exec()"); @@ -635,57 +630,51 @@ int runApplication( organizer.updateExecutablesList(); organizer.updateModInfoFromDisc(); - QString selectedProfileName = determineProfile(arguments, settings); + QString selectedProfileName = determineProfile(cl, settings); organizer.setCurrentProfile(selectedProfileName); // if we have a command line parameter, it is either a nxm link or // a binary to start - if (arguments.size() > 1) { - if (MOShortcut shortcut{ arguments.at(1) }) { - if (shortcut.hasExecutable()) { - try { - organizer.processRunner() - .setFromShortcut(shortcut) - .setWaitForCompletion() - .run(); - - return 0; - } - catch (const std::exception &e) { - reportError( - QObject::tr("failed to start shortcut: %1").arg(e.what())); - return 1; - } - } - } - else if (OrganizerCore::isNxmLink(arguments.at(1))) { - log::debug("starting download from command line: {}", arguments.at(1)); - organizer.externalMessage(arguments.at(1)); - } - else { - QString exeName = arguments.at(1); - log::debug("starting {} from command line", exeName); - - arguments.removeFirst(); // remove application name (ModOrganizer.exe) - arguments.removeFirst(); // remove binary name - - try - { - // pass the remaining parameters to the binary - organizer.processRunner() - .setFromFileOrExecutable(exeName, arguments) + if (cl.shortcut().isValid()) { + if (cl.shortcut().hasExecutable()) { + try { + organizer.processRunner() + .setFromShortcut(cl.shortcut()) .setWaitForCompletion() .run(); - return 0; - } - catch (const std::exception &e) - { - reportError( - QObject::tr("failed to start application: %1").arg(e.what())); - return 1; - } - } - } + + return 0; + } + catch (const std::exception &e) { + reportError( + QObject::tr("failed to start shortcut: %1").arg(e.what())); + return 1; + } + } + } else if (cl.nxmLink()) { + log::debug("starting download from command line: {}", *cl.nxmLink()); + organizer.externalMessage(*cl.nxmLink()); + } else if (cl.executable()) { + const QString exeName = *cl.executable(); + log::debug("starting {} from command line", exeName); + + try + { + // pass the remaining parameters to the binary + organizer.processRunner() + .setFromFileOrExecutable(exeName, cl.untouched()) + .setWaitForCompletion() + .run(); + + return 0; + } + catch (const std::exception &e) + { + reportError( + QObject::tr("failed to start application: %1").arg(e.what())); + return 1; + } + } QPixmap pixmap; @@ -857,15 +846,13 @@ int main(int argc, char *argv[]) siFlags |= SingleInstance::AllowMultiple; } - MOShortcut moshortcut{ arguments.size() > 1 ? arguments.at(1) : "" }; - SingleInstance instance(siFlags); if (instance.ephemeral()) { - if (moshortcut || - arguments.size() > 1 && OrganizerCore::isNxmLink(arguments.at(1))) - { - log::debug("not primary instance, sending shortcut/download message"); - instance.sendMessage(arguments.at(1)); + if (cl.shortcut().isValid()) { + instance.sendMessage(cl.shortcut().toString()); + return 0; + } else if (cl.nxmLink()) { + instance.sendMessage(*cl.nxmLink()); return 0; } else if (arguments.size() == 1) { QMessageBox::information( @@ -887,8 +874,8 @@ int main(int argc, char *argv[]) try { InstanceManager& instanceManager = InstanceManager::instance(); - if (moshortcut && moshortcut.hasInstance()) - instanceManager.overrideInstance(moshortcut.instance()); + if (cl.shortcut().isValid() && cl.shortcut().hasInstance()) + instanceManager.overrideInstance(cl.shortcut().instance()); dataPath = instanceManager.determineDataPath(); } catch (const std::exception &e) { if (strcmp(e.what(),"Canceled")) @@ -920,12 +907,12 @@ int main(int argc, char *argv[]) tt.stop(); - const int result = runApplication(application, arguments, instance, splash); + const int result = runApplication(application, cl, instance, splash); if (result != RestartExitCode) { return result; } argc = 1; - moshortcut = MOShortcut(""); + cl.clear(); } while (true); } diff --git a/src/moshortcut.cpp b/src/moshortcut.cpp index aad7380e..4efedbdb 100644 --- a/src/moshortcut.cpp +++ b/src/moshortcut.cpp @@ -40,3 +40,12 @@ MOShortcut::MOShortcut(const QString& link) m_hasExecutable=true; } } + +QString MOShortcut::toString() const +{ + if (m_hasInstance) { + return "moshortcut://" + m_instance + ":" + m_executable; + } else { + return "moshortcut://" + m_executable; + } +} diff --git a/src/moshortcut.h b/src/moshortcut.h index 2ce54910..0067b3bc 100644 --- a/src/moshortcut.h +++ b/src/moshortcut.h @@ -27,19 +27,21 @@ along with Mod Organizer. If not, see . class MOShortcut { public: - MOShortcut(const QString& link); + MOShortcut(const QString& link={}); /// true iff intialized using a valid moshortcut link - operator bool() const { return m_valid; } + bool isValid() const { return m_valid; } bool hasInstance() const { return m_hasInstance; } - + bool hasExecutable() const { return m_hasExecutable; } const QString& instance() const { return m_instance; } const QString& executable() const { return m_executable; } + QString toString() const; + private: QString m_instance; QString m_executable; diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 1c35720b..f9cafc95 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -38,6 +38,7 @@ #include "shared/directoryentry.h" #include "shared/filesorigin.h" #include "shared/fileentry.h" +#include "shared/util.h" #include #include @@ -395,7 +396,9 @@ void OrganizerCore::profileRemoved(QString const& profileName) void OrganizerCore::externalMessage(const QString &message) { - if (MOShortcut moshortcut{ message } ) { + MOShortcut moshortcut(message); + + if (moshortcut.isValid()) { if(moshortcut.hasExecutable()) { processRunner() .setFromShortcut(moshortcut) @@ -555,12 +558,16 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) QString profileDir = profileBaseDir.absoluteFilePath(profileName); if (!QDir(profileDir).exists()) { + log::error("profile '{}' does not exist", profileName); + // selected profile doesn't exist. Ensure there is at least one profile, // then pick any one createDefaultProfile(); profileDir = profileBaseDir.absoluteFilePath( profileBaseDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot).at(0)); + + log::error("picked profile '{}' instead", QDir(profileDir).dirName()); } // Keep the old profile to emit signal-changed: diff --git a/src/organizercore.h b/src/organizercore.h index 70ce94f5..1452bf08 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -191,9 +191,6 @@ public: }; public: - - static bool isNxmLink(const QString &link) { return link.startsWith("nxm://", Qt::CaseInsensitive); } - OrganizerCore(Settings &settings); ~OrganizerCore(); diff --git a/src/shared/util.cpp b/src/shared/util.cpp index ba500da9..f316549e 100644 --- a/src/shared/util.cpp +++ b/src/shared/util.cpp @@ -435,3 +435,9 @@ void ResetExitFlag() { g_exiting = false; } + + +bool isNxmLink(const QString& link) +{ + return link.startsWith("nxm://", Qt::CaseInsensitive); +} diff --git a/src/shared/util.h b/src/shared/util.h index 2761b64f..1688a931 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -83,4 +83,6 @@ bool ModOrganizerExiting(); bool ModOrganizerCanCloseNow(); void ResetExitFlag(); +bool isNxmLink(const QString& link); + #endif // UTIL_H -- cgit v1.3.1 From f4ca82f798fa7e456bb904ed301ddc17db5410c8 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 05:30:57 -0400 Subject: fixed handling of profile names with different casing than on the filesystem added --instance --- src/commandline.cpp | 12 ++++++++++++ src/commandline.h | 2 ++ src/main.cpp | 6 ++++-- src/organizercore.cpp | 19 +++++++++++++++++-- 4 files changed, 35 insertions(+), 4 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index f5c4df60..11f43dea 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -120,6 +120,7 @@ void CommandLine::createOptions() m_visibleOptions.add_options() ("help", "show this message") ("multiple", "allow multiple instances of MO to run; see below") + ("instance,i", po::value(), "use the given instance (defaults to last used)") ("profile,p", po::value(), "use the given profile (defaults to last used)"); po::options_description options; @@ -187,6 +188,17 @@ std::optional CommandLine::profile() const return {}; } +std::optional CommandLine::instance() const +{ + if (m_shortcut.isValid() && m_shortcut.hasInstance()) { + return m_shortcut.instance(); + } else if (m_vm.count("instance")) { + return QString::fromStdString(m_vm["instance"].as()); + } + + return {}; +} + const MOShortcut& CommandLine::shortcut() const { return m_shortcut; diff --git a/src/commandline.h b/src/commandline.h index cdd1c917..ecc19c89 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -99,6 +99,8 @@ public: bool multiple() const; std::optional profile() const; + std::optional instance() const; + const MOShortcut& shortcut() const; std::optional nxmLink() const; std::optional executable() const; diff --git a/src/main.cpp b/src/main.cpp index 3c5bc92d..5ae8999c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -874,8 +874,10 @@ int main(int argc, char *argv[]) try { InstanceManager& instanceManager = InstanceManager::instance(); - if (cl.shortcut().isValid() && cl.shortcut().hasInstance()) - instanceManager.overrideInstance(cl.shortcut().instance()); + + if (cl.instance()) + instanceManager.overrideInstance(*cl.instance()); + dataPath = instanceManager.determineDataPath(); } catch (const std::exception &e) { if (strcmp(e.what(),"Canceled")) diff --git a/src/organizercore.cpp b/src/organizercore.cpp index f9cafc95..a9469e6e 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -555,9 +555,24 @@ void OrganizerCore::setCurrentProfile(const QString &profileName) log::debug("selecting profile '{}'", profileName); QDir profileBaseDir(settings().paths().profiles()); - QString profileDir = profileBaseDir.absoluteFilePath(profileName); - if (!QDir(profileDir).exists()) { + const auto subdirs = profileBaseDir.entryList( + QDir::AllDirs | QDir::NoDotAndDotDot); + + QString profileDir; + + // the profile name may not have the correct case, which breaks other parts + // of the ui like the profile combobox, which walks directories on its own + // + // find the real name with the correct case by walking the directories + for (auto&& dirName : subdirs) { + if (QString::compare(dirName, profileName, Qt::CaseInsensitive) == 0) { + profileDir = profileBaseDir.absoluteFilePath(dirName); + break; + } + } + + if (profileDir.isEmpty()) { log::error("profile '{}' does not exist", profileName); // selected profile doesn't exist. Ensure there is at least one profile, -- cgit v1.3.1 From a8b6f227302f2264eea38ce95d82ddc5aebf1100 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 18 Jul 2020 13:17:31 -0400 Subject: formatting for command list added empty exe and run commands --- src/commandline.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/commandline.h | 24 +++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 11f43dea..819ac1c6 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -5,9 +5,47 @@ namespace cl { +std::string pad_right(std::string s, std::size_t n, char c=' ') +{ + if (s.size() < n) + s.append(n - s.size() , c); + + return s; +} + +std::string table( + const std::vector>& v, + std::size_t indent, std::size_t spacing) +{ + std::size_t longest = 0; + + for (auto&& p : v) + longest = std::max(longest, p.first.size()); + + std::string s; + + for (auto&& p : v) + { + if (!s.empty()) + s += "\n"; + + s += + std::string(indent, ' ') + + pad_right(p.first, longest) + " " + + std::string(spacing, ' ') + + p.second; + } + + return s; + +} + + CommandLine::CommandLine() { createOptions(); + m_commands.push_back(std::make_unique()); + m_commands.push_back(std::make_unique()); m_commands.push_back(std::make_unique()); m_commands.push_back(std::make_unique()); } @@ -156,11 +194,14 @@ std::string CommandLine::usage(const Command* c) const << "\n" << "Commands:\n"; + std::vector> v; for (auto&& c : m_commands) { - oss << " " << c->name() << " " << c->description() << "\n"; + v.push_back({c->name(), c->description()}); } - oss << "\n"; + oss + << table(v, 2, 4) << "\n" + << "\n"; } oss @@ -353,7 +394,7 @@ po::options_description LaunchCommand::doOptions() const Command::Meta LaunchCommand::meta() const { - return {"launch", ""}; + return {"launch", "(internal, do not use)"}; } std::optional LaunchCommand::doRun() @@ -421,4 +462,46 @@ LPCWSTR LaunchCommand::UntouchedCommandLineArguments( return cmd; } + +bool ExeCommand::allow_unregistered() const +{ + return true; +} + +po::options_description ExeCommand::doOptions() const +{ + return {}; +} + +Command::Meta ExeCommand::meta() const +{ + return {"exe", "launches a configured executable"}; +} + +std::optional ExeCommand::doRun() +{ + return {}; +} + + +bool RunCommand::allow_unregistered() const +{ + return true; +} + +po::options_description RunCommand::doOptions() const +{ + return {}; +} + +Command::Meta RunCommand::meta() const +{ + return {"run", "launches an arbitrary program"}; +} + +std::optional RunCommand::doRun() +{ + return {}; +} + } // namespace diff --git a/src/commandline.h b/src/commandline.h index ecc19c89..f633518b 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -87,6 +87,30 @@ protected: }; +class ExeCommand : public Command +{ +public: + bool allow_unregistered() const override; + +protected: + po::options_description doOptions() const; + Meta meta() const override; + std::optional doRun() override; +}; + + +class RunCommand : public Command +{ +public: + bool allow_unregistered() const override; + +protected: + po::options_description doOptions() const; + Meta meta() const override; + std::optional doRun() override; +}; + + class CommandLine { public: -- cgit v1.3.1 From cf289d7f95f6b5730cceaa8e6e22b4b33cf050c8 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 22 Jul 2020 22:17:58 -0400 Subject: command usage lines, exe options --- src/commandline.cpp | 178 ++++++++++++++++++++++++++++++++++++++-------------- src/commandline.h | 27 ++++---- 2 files changed, 147 insertions(+), 58 deletions(-) (limited to 'src/commandline.cpp') diff --git a/src/commandline.cpp b/src/commandline.cpp index 819ac1c6..3f8a6b1a 100644 --- a/src/commandline.cpp +++ b/src/commandline.cpp @@ -67,6 +67,7 @@ std::optional CommandLine::run(const std::wstring& line) .run(); po::store(parsed, m_vm); + po::notify(m_vm); auto opts = po::collect_unrecognized( parsed.options, po::include_positional); @@ -80,28 +81,43 @@ std::optional CommandLine::run(const std::wstring& line) // remove the command name itself opts.erase(opts.begin()); - auto co = c->options(); - co.add_options() - ("help", "shows this message"); + try + { + po::wcommand_line_parser parser(opts); - po::wcommand_line_parser parser(opts); - parser.options(co); + auto co = c->allOptions(); + parser.options(co); - if (c->allow_unregistered()) { - parser.allow_unregistered(); - } + if (c->allow_unregistered()) { + parser.allow_unregistered(); + } - parsed = parser.run(); + auto pos = c->positional(); + parser.positional(pos); - po::store(parsed, m_vm); + parsed = parser.run(); - if (m_vm.count("help")) { - env::Console console; - std::cout << usage(c.get()) << "\n"; - return 0; + po::store(parsed, m_vm); + po::notify(m_vm); + + if (m_vm.count("help")) { + env::Console console; + std::cout << usage(c.get()) << "\n"; + return 0; + } + + return c->run(line, m_vm, opts); } + catch(po::error& e) + { + env::Console console; - return c->run(line, m_vm, opts); + std::cerr + << e.what() << "\n" + << usage(c.get()) << "\n"; + + return 1; + } } } } @@ -157,7 +173,7 @@ void CommandLine::createOptions() { m_visibleOptions.add_options() ("help", "show this message") - ("multiple", "allow multiple instances of MO to run; see below") + ("multiple", "allow multiple MO processes to run; see below") ("instance,i", po::value(), "use the given instance (defaults to last used)") ("profile,p", po::value(), "use the given profile (defaults to last used)"); @@ -184,10 +200,10 @@ std::string CommandLine::usage(const Command* c) const if (c) { oss - << " ModOrganizer.exe [options] " << c->name() << " [command-options]\n" + << " ModOrganizer.exe [global-options] " << c->usageLine() << "\n" << "\n" << "Command options:\n" - << c->options() << "\n"; + << c->visibleOptions() << "\n"; } else { oss << " ModOrganizer.exe [options] [[command] [command-options]]\n" @@ -263,18 +279,24 @@ const QStringList& CommandLine::untouched() const std::string CommandLine::more() const { return - "Multiple instances\n" - " --multiple can be used to allow multiple instances of MO to run\n" + "Multiple processes\n" + " A note on terminology: 'instance' can either mean an MO process\n" + " that's running on the system, or a set of mods and profiles managed\n" + " by MO. To avoid confusion, the term 'process' is used below for the\n" + " former.\n" + " \n" + " --multiple can be used to allow multiple MO processes to run\n" " simultaneously. This is unsupported and can create all sorts of weird\n" - " problems. To minimize the problems:\n" + " problems. To minimize these:\n" " \n" - " 1) Never have multiple MO instances opened that manage the same\n" + " 1) Never have multiple MO processes running that manage the same\n" " game instance.\n" - " 2) If an executable is launched from an instance, only this\n" - " instance may launch executables until all instances are closed.\n" + " 2) If an executable is launched from an MO process, only this\n" + " process may launch executables until all processes are \n" + " terminated.\n" " \n" - " It is recommended to close _all_ instances of MO as soon as multiple\n" - " instances become unnecessary."; + " It is recommended to close _all_ MO processes as soon as multiple\n" + " processes become unnecessary."; } @@ -288,22 +310,65 @@ std::string Command::description() const return meta().description; } +std::string Command::usageLine() const +{ + return name() + " " + getUsageLine(); +} + bool Command::allow_unregistered() const { return false; } -po::options_description Command::options() const +po::options_description Command::allOptions() const +{ + po::options_description d; + + d.add(visibleOptions()); + d.add(getInternalOptions()); + + return d; +} + +po::options_description Command::visibleOptions() const +{ + po::options_description d(getVisibleOptions()); + + d.add_options() + ("help", "shows this message"); + + return d; +} + +po::positional_options_description Command::positional() const +{ + return getPositional(); +} + +std::string Command::getUsageLine() const +{ + return "[options]"; +} + +po::options_description Command::getVisibleOptions() const +{ + // no-op + return {}; +} + +po::options_description Command::getInternalOptions() const { - return doOptions(); + // no-op + return {}; } -po::options_description Command::doOptions() const +po::positional_options_description Command::getPositional() const { // no-op return {}; } + std::string Command::usage() const { std::ostringstream oss; @@ -314,7 +379,7 @@ std::string Command::usage() const << " ModOrganizer.exe [options] [[command] [command-options]]\n" << "\n" << "Options:\n" - << options() << "\n"; + << visibleOptions() << "\n"; return oss.str(); } @@ -347,7 +412,7 @@ const std::vector& Command::untouched() const } -po::options_description CrashDumpCommand::doOptions() const +po::options_description CrashDumpCommand::getVisibleOptions() const { po::options_description d; @@ -387,11 +452,6 @@ bool LaunchCommand::allow_unregistered() const return true; } -po::options_description LaunchCommand::doOptions() const -{ - return {}; -} - Command::Meta LaunchCommand::meta() const { return {"launch", "(internal, do not use)"}; @@ -463,14 +523,39 @@ LPCWSTR LaunchCommand::UntouchedCommandLineArguments( } -bool ExeCommand::allow_unregistered() const +std::string ExeCommand::getUsageLine() const { - return true; + return "[options] exe-name"; } -po::options_description ExeCommand::doOptions() const +po::options_description ExeCommand::getVisibleOptions() const { - return {}; + po::options_description d; + + d.add_options() + ("arguments,a", po::value()->default_value(""), "override arguments") + ("cwd,c", po::value()->default_value(""), "override working directory"); + + return d; +} + +po::options_description ExeCommand::getInternalOptions() const +{ + po::options_description d; + + d.add_options() + ("exe-name", po::value()->required(), "executable name"); + + return d; +} + +po::positional_options_description ExeCommand::getPositional() const +{ + po::positional_options_description d; + + d.add("exe-name", 1); + + return d; } Command::Meta ExeCommand::meta() const @@ -480,16 +565,17 @@ Command::Meta ExeCommand::meta() const std::optional ExeCommand::doRun() { - return {}; -} + const auto exe = vm()["exe-name"].as(); + const auto args = vm()["arguments"].as(); + const auto cwd = vm()["cwd"].as(); -bool RunCommand::allow_unregistered() const -{ - return true; + + return 0; } -po::options_description RunCommand::doOptions() const + +po::options_description RunCommand::getOptions() const { return {}; } diff --git a/src/commandline.h b/src/commandline.h index f633518b..0e300327 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -17,8 +17,11 @@ public: std::string name() const; std::string description() const; + std::string usageLine() const; - po::options_description options() const; + po::options_description allOptions() const; + po::options_description visibleOptions() const; + po::positional_options_description positional() const; std::string usage() const; virtual bool allow_unregistered() const; @@ -34,7 +37,11 @@ protected: std::string name, description; }; - virtual po::options_description doOptions() const; + virtual std::string getUsageLine() const; + virtual po::options_description getVisibleOptions() const; + virtual po::options_description getInternalOptions() const; + virtual po::positional_options_description getPositional() const; + virtual Meta meta() const = 0; virtual std::optional doRun() = 0; @@ -52,7 +59,7 @@ private: class CrashDumpCommand : public Command { protected: - po::options_description doOptions() const; + po::options_description getVisibleOptions() const override; Meta meta() const override; std::optional doRun() override; }; @@ -76,7 +83,6 @@ public: bool allow_unregistered() const override; protected: - po::options_description doOptions() const; Meta meta() const override; std::optional doRun() override; @@ -89,11 +95,11 @@ protected: class ExeCommand : public Command { -public: - bool allow_unregistered() const override; - protected: - po::options_description doOptions() const; + std::string getUsageLine() const override; + po::options_description getVisibleOptions() const override; + po::options_description getInternalOptions() const override; + po::positional_options_description getPositional() const override; Meta meta() const override; std::optional doRun() override; }; @@ -101,11 +107,8 @@ protected: class RunCommand : public Command { -public: - bool allow_unregistered() const override; - protected: - po::options_description doOptions() const; + po::options_description getOptions() const; Meta meta() const override; std::optional doRun() override; }; -- cgit v1.3.1