#ifndef MODORGANIZER_COMMANDLINE_INCLUDED #define MODORGANIZER_COMMANDLINE_INCLUDED #include "moshortcut.h" #include #include class OrganizerCore; class MOApplication; class MOMultiProcess; 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 meta().usage is // std::string usageLine() const; // shown after the usage line; this is meta().more // std::string moreInfo() 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; // when this returns true, the command line is not parsed at all and doRun() // is expected to use untouched() // virtual bool legacy() const; // remembers the given values // void set(const std::wstring& originalLine, po::variables_map vm, std::vector untouched); // called as soon as the command line has been parsed; return something to // exit immediately // virtual std::optional runEarly(); // called as soon as the MOApplication has been created, which is also the // first time where Qt stuff is available // virtual std::optional runPostApplication(MOApplication& a); // called as soon as the multi process checks have confirmed that this is // a primary instance; return something to exit immediately // virtual std::optional runPostMultiProcess(MOMultiProcess& mp); // called after the OrganizerCore has been initialized; return something to // exit immediatley // virtual std::optional runPostOrganizer(OrganizerCore& core); // whether this command can be forwarded to the primary instance if one is // already running // // if an instance is already running and this returns true, the whole command // line is sent as a message to the primary instance, which will construct a // new CommandLine and call runPostOrganizer() with it // // if an instance is already running and this returns false, the "an instance // is already running" error dialog will be shown // // if this is the primary instance, this function is not called // virtual bool canForwardToPrimary() const; protected: // meta information about this command, returned by derived classes // struct Meta { std::string name, description, usage, more; Meta(std::string name, std::string description, std::string usage, std::string more); }; // meta // virtual Meta meta() const = 0; // 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; // 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& untouched() const; private: std::wstring m_original; po::variables_map m_vm; std::vector m_untouched; }; // generates a crash dump for another MO process // class CrashDumpCommand : public Command { protected: po::options_description getVisibleOptions() const override; Meta meta() const override; std::optional runEarly() 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 legacy() const override; protected: Meta meta() const override; std::optional runEarly() override; int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine); LPCWSTR UntouchedCommandLineArguments(int parseArgCount, std::vector& parsedArgs); }; // runs a program or an executable // class RunCommand : public Command { protected: Meta meta() const override; po::options_description getVisibleOptions() const override; po::options_description getInternalOptions() const override; po::positional_options_description getPositional() const override; bool canForwardToPrimary() const override; std::optional runPostOrganizer(OrganizerCore& core) override; }; // reloads the given plugin // class ReloadPluginCommand : public Command { protected: Meta meta() const override; po::options_description getInternalOptions() const override; po::positional_options_description getPositional() const override; bool canForwardToPrimary() const override; std::optional runPostOrganizer(OrganizerCore& core) override; }; // downloads a file // class DownloadFileCommand : public Command { protected: Meta meta() const override; po::options_description getVisibleOptions() const override; po::options_description getInternalOptions() const override; po::positional_options_description getPositional() const override; bool canForwardToPrimary() const override; std::optional runPostOrganizer(OrganizerCore& core) override; }; // refreshes mo // class RefreshCommand : public Command { protected: Meta meta() const override; bool canForwardToPrimary() const override; std::optional runPostOrganizer(OrganizerCore& core) override; }; // 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 // // // the command is used in two phases: // // 1) run() is called in main() shortly after startup; it parses the command // line and runs the given command, if any // // 2) if the command did not request to exit, the instance is loaded // in main() et al. and setupCore() is called; it handles moshortcut, // nxm links and starting executables/binaries // // either can return an exit code, which will make MO exit immediately // class CommandLine { public: CommandLine(); // parses the given command line and calls runEarly() on the appropriate // command, if any // // returns an empty optional if execution should continue, or a return code // if MO must quit // std::optional process(const std::wstring& line); // called as soon as the MOApplication has been created; this handles a few // global actions and forwards to the command, if any // std::optional runPostApplication(MOApplication& a); // calls Command::runPostMultiProcess() on the command, if any // std::optional runPostMultiProcess(MOMultiProcess& mp); // calls Command::runPostOrganizer() on the command, if any // // if MO wasn't invoked with a valid command, this also handles moshortcut, // nxm links and starting processes // std::optional runPostOrganizer(OrganizerCore& core); // called when this instance is not the primary one // // if a command was executed and the command returns true for // canForwardToPrimary(), this forwards the command line as an external // message and returns true // // if the command returns false for canForwardToPrimary(), returns false // // if there was no valid command on the command line, this also forwards // moshortcut and nxm links // // if there was no command, moshortcut or nxm links, this returns false, which // will end up displaying an error message about an instance already running // bool forwardToPrimary(MOMultiProcess& multiProcess); // 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 --pick was given // bool pick() const; // whether --multiple was given // bool multiple() const; // profile override (-p) // std::optional profile() const; // instance override (-i) // std::optional instance() const; // returns the data parsed from an moshortcut:// option, if any // const MOShortcut& shortcut() const; // returns the nxm:// link, if any // std::optional nxmLink() const; // returns the executable/binary, if any // std::optional executable() const; // returns the list of arguments, excluding moshortcut or executable name; // deprecated, only use with executable() // 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; Command* m_command; void createOptions(); std::string more() const; template void add() { (m_commands.push_back(std::make_unique()), ...); } std::optional runEarly(); }; } // namespace cl #endif // MODORGANIZER_COMMANDLINE_INCLUDED