summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commandline.cpp55
-rw-r--r--src/commandline.h114
2 files changed, 150 insertions, 19 deletions
diff --git a/src/commandline.cpp b/src/commandline.cpp
index 3f8a6b1a..ff1be764 100644
--- a/src/commandline.cpp
+++ b/src/commandline.cpp
@@ -13,6 +13,8 @@ std::string pad_right(std::string s, std::size_t n, char c=' ')
return s;
}
+// formats the list of pairs in two columns
+//
std::string table(
const std::vector<std::pair<std::string, std::string>>& v,
std::size_t indent, std::size_t spacing)
@@ -60,6 +62,9 @@ std::optional<int> CommandLine::run(const std::wstring& line)
args.erase(args.begin());
}
+ // parsing the first part of the command line, including global options and
+ // command name, but not the rest, which will be collected below
+
auto parsed = po::wcommand_line_parser(args)
.options(m_allOptions)
.positional(m_positional)
@@ -69,20 +74,29 @@ std::optional<int> CommandLine::run(const std::wstring& line)
po::store(parsed, m_vm);
po::notify(m_vm);
+ // collect options past the command name
auto opts = po::collect_unrecognized(
parsed.options, po::include_positional);
if (m_vm.count("command")) {
+ // there's a word as the first argument; this may be a command name or
+ // an old style exe name/binary
+
const auto commandName = m_vm["command"].as<std::string>();
+ // look for the command by name first
for (auto&& c : m_commands) {
if (c->name() == commandName) {
+ // this is a command
+
// remove the command name itself
opts.erase(opts.begin());
try
{
+ // parse the the remainder of the command line according to the
+ // command's options
po::wcommand_line_parser parser(opts);
auto co = c->allOptions();
@@ -106,6 +120,7 @@ std::optional<int> CommandLine::run(const std::wstring& line)
return 0;
}
+ // run the command
return c->run(line, m_vm, opts);
}
catch(po::error& e)
@@ -122,6 +137,11 @@ std::optional<int> CommandLine::run(const std::wstring& line)
}
}
+
+ // the first word on the command line is not a valid command, try the other
+ // stuff; this is handled in main.cpp
+
+ // look for help
if (m_vm.count("help")) {
env::Console console;
std::cout << usage() << "\n";
@@ -130,12 +150,25 @@ std::optional<int> CommandLine::run(const std::wstring& line)
if (!opts.empty()) {
const auto qs = QString::fromStdWString(opts[0]);
+
+ if (qs.startsWith("--")) {
+ // assume that for something like `ModOrganizer.exe --bleh`, it's just
+ // a bad option instead of an executable that starts with "--"
+ env::Console console;
+ std::cerr << "\nUnrecognized option " << qs.toStdString() << "\n";
+
+ return 1;
+ }
+
+ // try as an moshorcut://
m_shortcut = qs;
if (!m_shortcut.isValid()) {
+ // not a shortcut, try a link
if (isNxmLink(qs)) {
m_nxmLink = qs;
} else {
+ // assume an executable name/binary
m_executable = qs;
}
}
@@ -182,6 +215,7 @@ void CommandLine::createOptions()
("command", po::value<std::string>(), "command")
("subargs", po::value<std::vector<std::string> >(), "args");
+ // one command name, followed by any arguments for that command
m_positional
.add("command", 1)
.add("subargs", -1);
@@ -210,6 +244,7 @@ std::string CommandLine::usage(const Command* c) const
<< "\n"
<< "Commands:\n";
+ // name and description for all commands
std::vector<std::pair<std::string, std::string>> v;
for (auto&& c : m_commands) {
v.push_back({c->name(), c->description()});
@@ -224,6 +259,7 @@ std::string CommandLine::usage(const Command* c) const
<< "Global options:\n"
<< m_visibleOptions << "\n";
+ // show the more text unless this is usage for a specific command
if (!c) {
oss << "\n" << more() << "\n";
}
@@ -247,6 +283,8 @@ std::optional<QString> CommandLine::profile() const
std::optional<QString> CommandLine::instance() const
{
+ // note that moshortcut:// overrides -i
+
if (m_shortcut.isValid() && m_shortcut.hasInstance()) {
return m_shortcut.instance();
} else if (m_vm.count("instance")) {
@@ -369,21 +407,6 @@ po::positional_options_description Command::getPositional() const
}
-std::string Command::usage() const
-{
- std::ostringstream oss;
-
- oss
- << "\n"
- << "Usage:\n"
- << " ModOrganizer.exe [options] [[command] [command-options]]\n"
- << "\n"
- << "Options:\n"
- << visibleOptions() << "\n";
-
- return oss.str();
-}
-
std::optional<int> Command::run(
const std::wstring& originalLine,
po::variables_map vm,
@@ -569,8 +592,6 @@ std::optional<int> ExeCommand::doRun()
const auto args = vm()["arguments"].as<std::string>();
const auto cwd = vm()["cwd"].as<std::string>();
-
-
return 0;
}
diff --git a/src/commandline.h b/src/commandline.h
index 72018ba3..478ee8ea 100644
--- a/src/commandline.h
+++ b/src/commandline.h
@@ -9,44 +9,96 @@ namespace cl
namespace po = boost::program_options;
-
+// base class for all commands
+//
class Command
{
public:
virtual ~Command() = default;
+ // command name, used on the command line to invoke it; this is meta().name
+ //
std::string name() const;
+
+ // command short description, shown in general help; this is
+ // meta().description
+ //
std::string description() const;
+
+ // usage line, puts together the name and whatever getUsageLine() returns
+ //
std::string usageLine() const;
+
+ // returns all options for this command, including hidden ones; used to parse
+ // the command line
+ //
po::options_description allOptions() const;
+
+ // returns visible options, used to display usage
+ //
po::options_description visibleOptions() const;
+
+ // returns positional arguments
+ //
po::positional_options_description positional() const;
- std::string usage() const;
+ // whether the command allows for unregistered options; this is only used for
+ // the old `launch` command and shouldn't be used anywhere else
+ //
virtual bool allow_unregistered() const;
+ // runs this command, eventually calls doRun()
+ //
std::optional<int> run(
const std::wstring& originalLine,
po::variables_map vm,
std::vector<std::wstring> untouched);
protected:
+ // meta information about this command, returned by derived classes
+ //
struct Meta
{
std::string name, description;
};
+ // returns the usage line for this command, not including the name
+ //
virtual std::string getUsageLine() const;
+
+ // returns visible options specific to this command
+ //
virtual po::options_description getVisibleOptions() const;
+
+ // returns hidden options specific to this command
+ //
virtual po::options_description getInternalOptions() const;
+
+ // returns positional arguments specific to this command
+ //
virtual po::positional_options_description getPositional() const;
+
+ // meta
+ //
virtual Meta meta() const = 0;
+
+ // runs the command
+ //
virtual std::optional<int> doRun() = 0;
+
+ // returns the original command line
+ //
const std::wstring& originalCmd() const;
+
+ // variables
+ //
const po::variables_map& vm() const;
+
+ // returns unparsed options, only used by launch
+ //
const std::vector<std::wstring>& untouched() const;
private:
@@ -56,6 +108,8 @@ private:
};
+// generates a crash dump for another MO process
+//
class CrashDumpCommand : public Command
{
protected:
@@ -93,6 +147,8 @@ protected:
};
+// runs a configured executable
+//
class ExeCommand : public Command
{
protected:
@@ -105,6 +161,8 @@ protected:
};
+// runs an arbitrary executable
+//
class RunCommand : public Command
{
protected:
@@ -114,24 +172,76 @@ protected:
};
+// parses the command line and runs any given command
+//
+// the command line used to support a few commands but with no real conventions;
+// those are mostly preserved for backwards compatibility, but deprecated:
+//
+// - moshortcut:// for desktop/taskbar shortcuts, may contain an instance name
+// and a configured executable or arbitrary binary (still used in MO for
+// shortcuts)
+//
+// - nxm:// links
+//
+// - the name of a configured executable or path to binary, followed by
+// arbitrary parameters, forwarded to the program
+//
+// any command added CommandLine will unfortunately break any executable with
+// the same name: `ModOrganizer.exe run` used to launch a program named "run"
+// but will now execute the command "run"
+//
+// if moshortcut:// is detected and has an instance, it will override -i if both
+// are given
+//
class CommandLine
{
public:
CommandLine();
+ // parses the given command line and executes the appropriate command, if
+ // any
+ //
+ // returns an empty optional if execution should continue, or a return code
+ // if MO must quit
+ //
std::optional<int> run(const std::wstring& line);
+
+ // clears parsed options, used when MO is "restarted" so the options aren't
+ // processed again
+ //
void clear();
+ // global usage string plus usage for the given command, if any
+ //
std::string usage(const Command* c=nullptr) const;
+
+ // whether --multiple was given
+ //
bool multiple() const;
+
+ // profile override (-p)
+ //
std::optional<QString> profile() const;
+
+ // instance override (-i)
+ //
std::optional<QString> instance() const;
+ // returns the data parsed from an moshortcut:// option, if any
+ //
const MOShortcut& shortcut() const;
+
+ // returns the nxm:// link, if any
+ //
std::optional<QString> nxmLink() const;
+
+ // returns the executable/binary, if any
std::optional<QString> executable() const;
+ // returns the list of arguments, excluding moshortcut or executable name;
+ // deprecated, only use with executable()
+ //
const QStringList& untouched() const;
private: