The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

354 lines
15KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. //==============================================================================
  20. /**
  21. Holds a list of command-line arguments, and provides useful methods for searching
  22. and operating on them.
  23. You can create an ArgumentList manually, or give it some argv/argc values from a
  24. main() function to parse.
  25. @see ConsoleApplication
  26. @tags{Core}
  27. */
  28. struct ArgumentList
  29. {
  30. /** Creates an argument list for a given executable. */
  31. ArgumentList (String executable, StringArray arguments);
  32. /** Parses a standard argv/argc pair to create an argument list. */
  33. ArgumentList (int argc, char* argv[]);
  34. /** Tokenises a string containing all the arguments to create an argument list. */
  35. ArgumentList (const String& executable, const String& arguments);
  36. ArgumentList (const ArgumentList&) = default;
  37. ArgumentList& operator= (const ArgumentList&) = default;
  38. //==============================================================================
  39. /**
  40. One of the arguments in an ArgumentList.
  41. @tags{Core}
  42. */
  43. struct Argument
  44. {
  45. /** The original text of this argument. */
  46. String text;
  47. /** Resolves this argument as an absolute File, using the current working
  48. directory as a base for resolving relative paths, and stripping quotes, etc.
  49. */
  50. File resolveAsFile() const;
  51. /** Resolves this argument as an absolute File, using the current working
  52. directory as a base for resolving relative paths, and also doing a check to
  53. make sure the file exists.
  54. If the file doesn't exist, this will call fail() with a suitable error.
  55. @see resolveAsFile, resolveAsExistingFolder
  56. */
  57. File resolveAsExistingFile() const;
  58. /** Resolves a user-supplied folder name into an absolute File, using the current working
  59. directory as a base for resolving relative paths, and also doing a check to make
  60. sure the folder exists.
  61. If the folder doesn't exist, this will call fail() with a suitable error.
  62. @see resolveAsFile, resolveAsExistingFile
  63. */
  64. File resolveAsExistingFolder() const;
  65. /** Returns true if this argument starts with a double dash. */
  66. bool isLongOption() const;
  67. /** Returns true if this argument starts with a single dash. */
  68. bool isShortOption() const;
  69. /** Returns true if this argument starts with a double dash, followed by the given string. */
  70. bool isLongOption (const String& optionRoot) const;
  71. /** If this argument is a long option with a value, this returns the value.
  72. e.g. for "--foo=bar", this would return 'bar'.
  73. */
  74. String getLongOptionValue() const;
  75. /** Returns true if this argument starts with a single dash and then contains the given character somewhere inside it. */
  76. bool isShortOption (char shortOptionCharacter) const;
  77. /** Returns true if this argument starts with one or more dashes. */
  78. bool isOption() const;
  79. /** Compares this argument against a string.
  80. The string may be a pipe-separated list of options, e.g. "--help|-h"
  81. */
  82. bool operator== (StringRef stringToCompare) const;
  83. /** Compares this argument against a string.
  84. The string may be a pipe-separated list of options, e.g. "--help|-h"
  85. */
  86. bool operator!= (StringRef stringToCompare) const;
  87. };
  88. //==============================================================================
  89. /** Returns the number of arguments in the list. */
  90. int size() const;
  91. /** Returns one of the arguments */
  92. Argument operator[] (int index) const;
  93. /** Throws an error unless there are at least the given number of arguments. */
  94. void checkMinNumArguments (int expectedMinNumberOfArgs) const;
  95. /** Returns true if the given string matches one of the arguments.
  96. The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
  97. @see removeOptionIfFound
  98. */
  99. bool containsOption (StringRef option) const;
  100. /** Returns true if the given string matches one of the arguments, and also removes the
  101. argument from the list if found.
  102. The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
  103. @see containsOption
  104. */
  105. bool removeOptionIfFound (StringRef option);
  106. /** Returns the index of the given string if it matches one of the arguments, or -1 if it doesn't.
  107. The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
  108. */
  109. int indexOfOption (StringRef option) const;
  110. /** Throws an error unless the given option is found in the argument list. */
  111. void failIfOptionIsMissing (StringRef option) const;
  112. /** Looks for a given argument and returns either its assigned value (for long options) or the
  113. string that follows it (for short options).
  114. The option can also be a list of different versions separated by pipes, e.g. "--help|-h"
  115. If it finds a long option, it will look for an assignment with a '=' sign, e.g. "--file=foo.txt",
  116. and will return the string following the '='. If there's no '=', it will return an empty string.
  117. If it finds a short option, it will attempt to return the argument that follows it, unless
  118. it's another option.
  119. If the argument isn't found, this returns an empty string.
  120. */
  121. String getValueForOption (StringRef option) const;
  122. /** Looks for a given argument and returns either its assigned value (for long options) or the
  123. string that follows it (for short options).
  124. This works like getValueForOption() but also removes the option argument (and any value arguments)
  125. from the list if they are found.
  126. */
  127. String removeValueForOption (StringRef option);
  128. /** Looks for the value of argument using getValueForOption() and tries to parse that value as a file.
  129. If the option isn't found, or if the value can't be parsed as a filename, it will throw an error.
  130. */
  131. File getFileForOption (StringRef option) const;
  132. /** Looks for the value of argument using getValueForOption() and tries to parse that value as a file.
  133. This works like getFileForOption() but also removes the option argument (and any value arguments)
  134. from the list if they are found.
  135. */
  136. File getFileForOptionAndRemove (StringRef option);
  137. /** Looks for a file argument using getFileForOption() and fails with a suitable error if
  138. the file doesn't exist.
  139. */
  140. File getExistingFileForOption (StringRef option) const;
  141. /** Looks for a file argument using getFileForOption() and fails with a suitable error if
  142. the file doesn't exist.
  143. This works like getExistingFileForOption() but also removes the option argument (and any
  144. value arguments) from the list if they are found.
  145. */
  146. File getExistingFileForOptionAndRemove (StringRef option);
  147. /** Looks for a filename argument using getFileForOption() and fails with a suitable error if
  148. the file isn't a folder that exists.
  149. */
  150. File getExistingFolderForOption (StringRef option) const;
  151. /** Looks for a filename argument using getFileForOption() and fails with a suitable error if
  152. the file isn't a folder that exists.
  153. This works like getExistingFolderForOption() but also removes the option argument (and any
  154. value arguments) from the list if they are found.
  155. */
  156. File getExistingFolderForOptionAndRemove (StringRef option);
  157. /** The name or path of the executable that was invoked, as it was specified on the command-line. */
  158. String executableName;
  159. /** The list of arguments (not including the name of the executable that was invoked). */
  160. Array<Argument> arguments;
  161. };
  162. //==============================================================================
  163. /**
  164. Represents a the set of commands that a console app can perform, and provides
  165. helper functions for performing them.
  166. When using these helper classes to implement a console app, you probably want to
  167. do something along these lines:
  168. @code
  169. int main (int argc, char* argv[])
  170. {
  171. ConsoleApplication app;
  172. app.addHelpCommand ("--help|-h", "Usage:", true);
  173. app.addVersionCommand ("--version|-v", "MyApp version 1.2.3");
  174. app.addCommand ({ "--foo",
  175. "--foo filename",
  176. "Performs a foo operation on the given file",
  177. [] (const auto& args) { doFoo (args); }});
  178. return app.findAndRunCommand (argc, argv);
  179. }
  180. @endcode
  181. @see ArgumentList
  182. @tags{Core}
  183. */
  184. struct ConsoleApplication
  185. {
  186. //==============================================================================
  187. /**
  188. Represents a command that can be executed if its command-line arguments are matched.
  189. @see ConsoleApplication::addCommand(), ConsoleApplication::findAndRunCommand()
  190. @tags{Core}
  191. */
  192. struct Command
  193. {
  194. /** The option string that must appear in the argument list for this command to be invoked.
  195. This can also be a list of different versions separated by pipes, e.g. "--help|-h"
  196. */
  197. String commandOption;
  198. /** A description of the command-line arguments needed for this command, which will be
  199. printed as part of the help text.
  200. */
  201. String argumentDescription;
  202. /** A short (one line) description of this command, which can be printed by
  203. ConsoleApplication::printCommandList().
  204. */
  205. String shortDescription;
  206. /** A longer description of this command, for use in extended help. */
  207. String longDescription;
  208. /** The actual command that should be invoked to perform this action. */
  209. std::function<void (const ArgumentList&)> command;
  210. };
  211. //==============================================================================
  212. /** Adds a command to the list. */
  213. void addCommand (Command);
  214. /** Adds a command to the list, and marks it as one which is invoked if no other
  215. command matches.
  216. */
  217. void addDefaultCommand (Command);
  218. /** Adds a command that will print the given text in response to the "--version" option. */
  219. void addVersionCommand (String versionArgument, String versionText);
  220. /** Adds a help command to the list.
  221. This command will print the user-supplied message that's passed in here as an
  222. argument, followed by a list of all the registered commands.
  223. */
  224. void addHelpCommand (String helpArgument, String helpMessage, bool makeDefaultCommand);
  225. /** Prints out the list of commands and their short descriptions in a format that's
  226. suitable for use as help.
  227. */
  228. void printCommandList (const ArgumentList&) const;
  229. /** Prints out a longer description of a particular command, based on its
  230. longDescription member.
  231. */
  232. void printCommandDetails (const ArgumentList&, const Command&) const;
  233. //==============================================================================
  234. /** Throws a failure exception to cause a command-line app to terminate.
  235. This is intended to be called from code in a Command, so that the
  236. exception will be automatically caught and turned into a printed error message
  237. and a return code which will be returned from main().
  238. @see ConsoleApplication::invokeCatchingFailures()
  239. */
  240. static void fail (String errorMessage, int returnCode = 1);
  241. /** Invokes a function, catching any fail() calls that it might trigger, and handling
  242. them by printing their error message and returning their error code.
  243. @see ConsoleApplication::fail()
  244. */
  245. static int invokeCatchingFailures (std::function<int()>&& functionToCall);
  246. //==============================================================================
  247. /** Looks for the first command in the list which matches the given arguments, and
  248. tries to invoke it.
  249. If no command is found, and if there is no default command to run, it fails with
  250. a suitable error message.
  251. If the command calls the fail() function, this will throw an exception that gets
  252. automatically caught and handled, and this method will return the error code that
  253. was passed into the fail() call.
  254. If optionMustBeFirstArg is true, then only the first argument will be looked at
  255. when searching the available commands - this lets you do 'git' style commands where
  256. the executable name is followed by a verb.
  257. */
  258. int findAndRunCommand (const ArgumentList&,
  259. bool optionMustBeFirstArg = false) const;
  260. /** Creates an ArgumentList object from the argc and argv variablrs, and invokes
  261. findAndRunCommand() using it.
  262. */
  263. int findAndRunCommand (int argc, char* argv[]) const;
  264. /** Looks for the first command in the list which matches the given arguments.
  265. If none is found, this returns either the default command (if one is set)
  266. or nullptr.
  267. If optionMustBeFirstArg is true, then only the first argument will be looked at
  268. when searching the available commands - this lets you do 'git' style commands where
  269. the executable name is followed by a verb.
  270. */
  271. const Command* findCommand (const ArgumentList&, bool optionMustBeFirstArg) const;
  272. /** Gives read-only access to the list of registered commands. */
  273. const std::vector<Command>& getCommands() const;
  274. private:
  275. //==============================================================================
  276. std::vector<Command> commands;
  277. int commandIfNoOthersRecognised = -1;
  278. };
  279. } // namespace juce