summaryrefslogtreecommitdiff
path: root/src/commandline.h
blob: c67a0716f906a9e24945a582db51617e64c90e12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
#ifndef MODORGANIZER_COMMANDLINE_INCLUDED
#define MODORGANIZER_COMMANDLINE_INCLUDED
#include "moshortcut.h"
#include <memory>
#include <vector>

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<std::wstring> untouched);

  // called as soon as the command line has been parsed; return something to
  // exit immediately
  //
  virtual std::optional<int> runEarly();

  // called as soon as the MOApplication has been created, which is also the
  // first time where Qt stuff is available
  //
  virtual std::optional<int> 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<int> runPostMultiProcess(MOMultiProcess& mp);

  // called after the OrganizerCore has been initialized; return something to
  // exit immediatley
  //
  virtual std::optional<int> 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<std::wstring>& untouched() const;

private:
  std::wstring m_original;
  po::variables_map m_vm;
  std::vector<std::wstring> 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<int> 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<int> runEarly() override;

  int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine);

  LPCWSTR UntouchedCommandLineArguments(int parseArgCount,
                                        std::vector<std::wstring>& 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<int> 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<int> 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<int> runPostOrganizer(OrganizerCore& core) override;
};

// refreshes mo
//
class RefreshCommand : public Command
{
protected:
  Meta meta() const override;
  bool canForwardToPrimary() const override;
  std::optional<int> 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<int> 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<int> runPostApplication(MOApplication& a);

  // calls Command::runPostMultiProcess() on the command, if any
  //
  std::optional<int> 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<int> 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<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:
  po::options_description m_visibleOptions, m_allOptions;
  po::positional_options_description m_positional;
  std::vector<std::unique_ptr<Command>> m_commands;
  po::variables_map m_vm;
  MOShortcut m_shortcut;
  std::optional<QString> m_nxmLink;
  std::optional<QString> m_executable;
  QStringList m_untouched;
  Command* m_command;

  void createOptions();
  std::string more() const;

  template <class... Ts>
  void add()
  {
    (m_commands.push_back(std::make_unique<Ts>()), ...);
  }

  std::optional<int> runEarly();
};

}  // namespace cl

#endif  // MODORGANIZER_COMMANDLINE_INCLUDED