OpenShot Library | OpenShotAudio  0.2.2
juce_ConsoleApplication.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
6 
7  JUCE is an open source library subject to commercial or open-source
8  licensing.
9 
10  The code included in this file is provided under the terms of the ISC license
11  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12  To use, copy, modify, and/or distribute this software for any purpose with or
13  without fee is hereby granted provided that the above copyright notice and
14  this permission notice appear in all copies.
15 
16  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18  DISCLAIMED.
19 
20  ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 static inline File resolveFilename (const String& name)
27 {
28  return File::getCurrentWorkingDirectory().getChildFile (name.unquoted());
29 }
30 
31 static inline File checkFileExists (const File& f)
32 {
33  if (! f.exists())
34  ConsoleApplication::fail ("Could not find file: " + f.getFullPathName());
35 
36  return f;
37 }
38 
39 static inline File checkFolderExists (const File& f)
40 {
41  if (! f.isDirectory())
42  ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
43 
44  return f;
45 }
46 
47 static inline File resolveFilenameForOption (const ArgumentList& args, StringRef option, const String& filename)
48 {
49  if (filename.isEmpty())
50  {
51  args.failIfOptionIsMissing (option);
52  ConsoleApplication::fail ("Expected a filename after the " + option + " option");
53  }
54 
55  return resolveFilename (filename);
56 }
57 
59 {
60  return resolveFilename (text);
61 }
62 
64 {
65  return checkFileExists (resolveAsFile());
66 }
67 
69 {
70  auto f = resolveAsFile();
71 
72  if (! f.isDirectory())
73  ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
74 
75  return f;
76 }
77 
78 static inline bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; }
79 static inline bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; }
80 static inline bool isOptionFormat (StringRef s) { return s[0] == '-'; }
81 
82 bool ArgumentList::Argument::isLongOption() const { return isLongOptionFormat (text); }
83 bool ArgumentList::Argument::isShortOption() const { return isShortOptionFormat (text); }
84 bool ArgumentList::Argument::isOption() const { return isOptionFormat (text); }
85 
86 bool ArgumentList::Argument::isLongOption (const String& option) const
87 {
88  if (! isLongOptionFormat (option))
89  {
90  jassert (! isShortOptionFormat (option)); // this will always fail to match
91  return isLongOption ("--" + option);
92  }
93 
94  return text.upToFirstOccurrenceOf ("=", false, false) == option;
95 }
96 
98 {
99  if (isLongOption())
100  {
101  auto equalsIndex = text.indexOfChar ('=');
102 
103  if (equalsIndex > 0)
104  return text.substring (equalsIndex + 1);
105  }
106 
107  return {};
108 }
109 
111 {
112  jassert (option != '-'); // this is probably not what you intended to pass in
113 
114  return isShortOption() && text.containsChar (String (option)[0]);
115 }
116 
118 {
119  for (auto& o : StringArray::fromTokens (wildcard, "|", {}))
120  {
121  if (text == o)
122  return true;
123 
124  if (isShortOptionFormat (o) && o.length() == 2 && isShortOption ((char) o[1]))
125  return true;
126 
127  if (isLongOptionFormat (o) && isLongOption (o))
128  return true;
129  }
130 
131  return false;
132 }
133 
135 
136 //==============================================================================
138  : executableName (std::move (exeName))
139 {
140  args.trim();
141  args.removeEmptyStrings();
142 
143  for (auto& a : args)
144  arguments.add ({ a });
145 }
146 
147 ArgumentList::ArgumentList (int argc, char* argv[])
148  : ArgumentList (argv[0], StringArray (argv + 1, argc - 1))
149 {
150 }
151 
152 ArgumentList::ArgumentList (const String& exeName, const String& args)
153  : ArgumentList (exeName, StringArray::fromTokens (args, true))
154 {
155 }
156 
157 int ArgumentList::size() const { return arguments.size(); }
158 ArgumentList::Argument ArgumentList::operator[] (int index) const { return arguments[index]; }
159 
160 void ArgumentList::checkMinNumArguments (int expectedMinNumberOfArgs) const
161 {
162  if (size() < expectedMinNumberOfArgs)
163  ConsoleApplication::fail ("Not enough arguments!");
164 }
165 
167 {
168  jassert (option == String (option).trim()); // passing non-trimmed strings will always fail to find a match!
169 
170  for (int i = 0; i < arguments.size(); ++i)
171  if (arguments.getReference(i) == option)
172  return i;
173 
174  return -1;
175 }
176 
178 {
179  return indexOfOption (option) >= 0;
180 }
181 
183 {
184  auto i = indexOfOption (option);
185 
186  if (i >= 0)
187  arguments.remove (i);
188 
189  return i >= 0;
190 }
191 
193 {
194  if (indexOfOption (option) < 0)
195  ConsoleApplication::fail ("Expected the option " + option);
196 }
197 
199 {
200  jassert (isOptionFormat (option)); // the thing you're searching for must be an option
201 
202  for (int i = 0; i < arguments.size(); ++i)
203  {
204  auto& arg = arguments.getReference(i);
205 
206  if (arg == option)
207  {
208  if (arg.isShortOption())
209  {
210  if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
211  return arguments.getReference (i + 1).text;
212 
213  return {};
214  }
215 
216  if (arg.isLongOption())
217  return arg.getLongOptionValue();
218  }
219  }
220 
221  return {};
222 }
223 
225 {
226  jassert (isOptionFormat (option)); // the thing you're searching for must be an option
227 
228  for (int i = 0; i < arguments.size(); ++i)
229  {
230  auto& arg = arguments.getReference(i);
231 
232  if (arg == option)
233  {
234  if (arg.isShortOption())
235  {
236  if (i < arguments.size() - 1 && ! arguments.getReference (i + 1).isOption())
237  {
238  auto result = arguments.getReference (i + 1).text;
239  arguments.removeRange (i, 2);
240  return result;
241  }
242 
243  arguments.remove (i);
244  return {};
245  }
246 
247  if (arg.isLongOption())
248  {
249  auto result = arg.getLongOptionValue();
250  arguments.remove (i);
251  return result;
252  }
253  }
254  }
255 
256  return {};
257 }
258 
260 {
261  return resolveFilenameForOption (*this, option, getValueForOption (option));
262 }
263 
265 {
266  return resolveFilenameForOption (*this, option, removeValueForOption (option));
267 }
268 
270 {
271  return checkFileExists (getFileForOption (option));
272 }
273 
275 {
276  return checkFileExists (getFileForOptionAndRemove (option));
277 }
278 
280 {
281  return checkFolderExists (getFileForOption (option));
282 }
283 
285 {
286  return checkFolderExists (getFileForOptionAndRemove (option));
287 }
288 
289 //==============================================================================
291 {
292  String errorMessage;
293  int returnCode;
294 };
295 
296 void ConsoleApplication::fail (String errorMessage, int returnCode)
297 {
298  throw ConsoleAppFailureCode { std::move (errorMessage), returnCode };
299 }
300 
301 int ConsoleApplication::invokeCatchingFailures (std::function<int()>&& f)
302 {
303  int returnCode = 0;
304 
305  try
306  {
307  returnCode = f();
308  }
309  catch (const ConsoleAppFailureCode& error)
310  {
311  std::cerr << error.errorMessage << std::endl;
312  returnCode = error.returnCode;
313  }
314 
315  return returnCode;
316 }
317 
318 const ConsoleApplication::Command* ConsoleApplication::findCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
319 {
320  for (auto& c : commands)
321  {
322  auto index = args.indexOfOption (c.commandOption);
323 
324  if (optionMustBeFirstArg ? (index == 0) : (index >= 0))
325  return &c;
326  }
327 
328  if (commandIfNoOthersRecognised >= 0)
329  return &commands[(size_t) commandIfNoOthersRecognised];
330 
331  return {};
332 }
333 
334 int ConsoleApplication::findAndRunCommand (const ArgumentList& args, bool optionMustBeFirstArg) const
335 {
336  return invokeCatchingFailures ([&args, optionMustBeFirstArg, this]
337  {
338  if (auto c = findCommand (args, optionMustBeFirstArg))
339  c->command (args);
340  else
341  fail ("Unrecognised arguments");
342 
343  return 0;
344  });
345 }
346 
347 int ConsoleApplication::findAndRunCommand (int argc, char* argv[]) const
348 {
349  return findAndRunCommand (ArgumentList (argc, argv));
350 }
351 
353 {
354  commands.emplace_back (std::move (c));
355 }
356 
358 {
359  commandIfNoOthersRecognised = (int) commands.size();
360  addCommand (std::move (c));
361 }
362 
363 void ConsoleApplication::addHelpCommand (String arg, String helpMessage, bool makeDefaultCommand)
364 {
365  Command c { arg, arg, "Prints the list of commands", {},
366  [this, helpMessage] (const ArgumentList& args)
367  {
368  std::cout << helpMessage << std::endl;
369  printCommandList (args);
370  }};
371 
372  if (makeDefaultCommand)
373  addDefaultCommand (std::move (c));
374  else
375  addCommand (std::move (c));
376 }
377 
379 {
380  addCommand ({ arg, arg, "Prints the current version number", {},
381  [versionText] (const ArgumentList&)
382  {
383  std::cout << versionText << std::endl;
384  }});
385 }
386 
387 const std::vector<ConsoleApplication::Command>& ConsoleApplication::getCommands() const
388 {
389  return commands;
390 }
391 
392 static String getExeNameAndArgs (const ArgumentList& args, const ConsoleApplication::Command& command)
393 {
394  auto exeName = args.executableName.fromLastOccurrenceOf ("/", false, false)
395  .fromLastOccurrenceOf ("\\", false, false);
396 
397  return " " + exeName + " " + command.argumentDescription;
398 }
399 
400 static void printCommandDescription (const ArgumentList& args, const ConsoleApplication::Command& command,
401  int descriptionIndent)
402 {
403  auto nameAndArgs = getExeNameAndArgs (args, command);
404 
405  if (nameAndArgs.length() > descriptionIndent)
406  std::cout << nameAndArgs << std::endl << String().paddedRight (' ', descriptionIndent);
407  else
408  std::cout << nameAndArgs.paddedRight (' ', descriptionIndent);
409 
410  std::cout << command.shortDescription << std::endl;
411 }
412 
414 {
415  int descriptionIndent = 0;
416 
417  for (auto& c : commands)
418  descriptionIndent = std::max (descriptionIndent, getExeNameAndArgs (args, c).length());
419 
420  descriptionIndent = std::min (descriptionIndent + 2, 40);
421 
422  for (auto& c : commands)
423  printCommandDescription (args, c, descriptionIndent);
424 
425  std::cout << std::endl;
426 }
427 
428 void ConsoleApplication::printCommandDetails (const ArgumentList& args, const Command& command) const
429 {
430  auto len = getExeNameAndArgs (args, command).length();
431 
432  printCommandDescription (args, command, std::min (len + 3, 40));
433 
434  if (command.longDescription.isNotEmpty())
435  std::cout << std::endl << command.longDescription << std::endl;
436 }
437 
438 
439 } // namespace juce
Array< Argument > arguments
The list of arguments (not including the name of the executable that was invoked).
String getLongOptionValue() const
If this argument is a long option with a value, this returns the value.
ArgumentList(String executable, StringArray arguments)
Creates an argument list for a given executable.
bool isShortOption() const
Returns true if this argument starts with a single dash.
bool isOption() const
Returns true if this argument starts with one or more dashes.
int findAndRunCommand(const ArgumentList &, bool optionMustBeFirstArg=false) const
Looks for the first command in the list which matches the given arguments, and tries to invoke it...
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Definition: juce_String.h:306
String text
The original text of this argument.
String getValueForOption(StringRef option) const
Looks for a given argument and returns either its assigned value (for long options) or the string tha...
A simple class for holding temporary references to a string literal or String.
bool removeOptionIfFound(StringRef option)
Returns true if the given string matches one of the arguments, and also removes the argument from the...
String removeValueForOption(StringRef option)
Looks for a given argument and returns either its assigned value (for long options) or the string tha...
int indexOfOption(StringRef option) const
Returns the index of the given string if it matches one of the arguments, or -1 if it doesn&#39;t...
void printCommandDetails(const ArgumentList &, const Command &) const
Prints out a longer description of a particular command, based on its longDescription member...
Holds a list of command-line arguments, and provides useful methods for searching and operating on th...
void removeEmptyStrings(bool removeWhitespaceStrings=true)
Removes empty strings from the array.
static void fail(String errorMessage, int returnCode=1)
Throws a failure exception to cause a command-line app to terminate.
File getFileForOption(StringRef option) const
Looks for the value of argument using getValueForOption() and tries to parse that value as a file...
bool containsChar(juce_wchar character) const noexcept
Tests whether the string contains a particular character.
STL namespace.
String shortDescription
A short (one line) description of this command, which can be printed by ConsoleApplication::printComm...
File getExistingFileForOptionAndRemove(StringRef option)
Looks for a file argument using getFileForOption() and fails with a suitable error if the file doesn&#39;...
A special array for holding a list of strings.
File getChildFile(StringRef relativeOrAbsolutePath) const
Returns a file that represents a relative (or absolute) sub-path of the current one.
Definition: juce_File.cpp:414
The JUCE String class!
Definition: juce_String.h:42
const Command * findCommand(const ArgumentList &, bool optionMustBeFirstArg) const
Looks for the first command in the list which matches the given arguments.
File getFileForOptionAndRemove(StringRef option)
Looks for the value of argument using getValueForOption() and tries to parse that value as a file...
void trim()
Deletes any whitespace characters from the starts and ends of all the strings.
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from the last occurrence of a given substring.
String paddedRight(juce_wchar padCharacter, int minimumLength) const
Returns a copy of this string with the specified character repeatedly added to its end until the tota...
String substring(int startIndex, int endIndex) const
Returns a subsection of the string.
String argumentDescription
A description of the command-line arguments needed for this command, which will be printed as part of...
void addCommand(Command)
Adds a command to the list.
Represents a command that can be executed if its command-line arguments are matched.
void addDefaultCommand(Command)
Adds a command to the list, and marks it as one which is invoked if no other command matches...
Argument operator[](int index) const
Returns one of the arguments.
bool operator!=(StringRef stringToCompare) const
Compares this argument against a string.
File resolveAsExistingFolder() const
Resolves a user-supplied folder name into an absolute File, using the current working directory as a ...
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Returns an array containing the tokens in a given string.
const std::vector< Command > & getCommands() const
Gives read-only access to the list of registered commands.
One of the arguments in an ArgumentList.
Represents a local file or directory.
Definition: juce_File.h:44
File resolveAsFile() const
Resolves this argument as an absolute File, using the current working directory as a base for resolvi...
File resolveAsExistingFile() const
Resolves this argument as an absolute File, using the current working directory as a base for resolvi...
File getExistingFileForOption(StringRef option) const
Looks for a file argument using getFileForOption() and fails with a suitable error if the file doesn&#39;...
void checkMinNumArguments(int expectedMinNumberOfArgs) const
Throws an error unless there are at least the given number of arguments.
void addHelpCommand(String helpArgument, String helpMessage, bool makeDefaultCommand)
Adds a help command to the list.
String executableName
The name or path of the executable that was invoked, as it was specified on the command-line.
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the first occurrence of a substring.
void printCommandList(const ArgumentList &) const
Prints out the list of commands and their short descriptions in a format that&#39;s suitable for use as h...
bool operator==(StringRef stringToCompare) const
Compares this argument against a string.
void failIfOptionIsMissing(StringRef option) const
Throws an error unless the given option is found in the argument list.
int size() const
Returns the number of arguments in the list.
File getExistingFolderForOptionAndRemove(StringRef option)
Looks for a filename argument using getFileForOption() and fails with a suitable error if the file is...
bool isLongOption() const
Returns true if this argument starts with a double dash.
int indexOfChar(juce_wchar characterToLookFor) const noexcept
Searches for a character inside this string.
static File getCurrentWorkingDirectory()
Returns the current working directory.
static int invokeCatchingFailures(std::function< int()> &&functionToCall)
Invokes a function, catching any fail() calls that it might trigger, and handling them by printing th...
int length() const noexcept
Returns the number of characters in the string.
File getExistingFolderForOption(StringRef option) const
Looks for a filename argument using getFileForOption() and fails with a suitable error if the file is...
String longDescription
A longer description of this command, for use in extended help.
bool containsOption(StringRef option) const
Returns true if the given string matches one of the arguments.
void addVersionCommand(String versionArgument, String versionText)
Adds a command that will print the given text in response to the "--version" option.