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
23namespace juce
24{
25
26static inline File resolveFilename (const String& name)
27{
28 return File::getCurrentWorkingDirectory().getChildFile (name.unquoted());
29}
30
31static 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
39static 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
47static 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
78static inline bool isShortOptionFormat (StringRef s) { return s[0] == '-' && s[1] != '-'; }
79static inline bool isLongOptionFormat (StringRef s) { return s[0] == '-' && s[1] == '-' && s[2] != '-'; }
80static inline bool isOptionFormat (StringRef s) { return s[0] == '-'; }
81
82bool ArgumentList::Argument::isLongOption() const { return isLongOptionFormat (text); }
83bool ArgumentList::Argument::isShortOption() const { return isShortOptionFormat (text); }
84bool ArgumentList::Argument::isOption() const { return isOptionFormat (text); }
85
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
134bool ArgumentList::Argument::operator!= (StringRef s) const { return ! operator== (s); }
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
147ArgumentList::ArgumentList (int argc, char* argv[])
148 : ArgumentList (argv[0], StringArray (argv + 1, argc - 1))
149{
150}
151
152ArgumentList::ArgumentList (const String& exeName, const String& args)
153 : ArgumentList (exeName, StringArray::fromTokens (args, true))
154{
155}
156
157int ArgumentList::size() const { return arguments.size(); }
159
160void 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
296void ConsoleApplication::fail (String errorMessage, int returnCode)
297{
298 throw ConsoleAppFailureCode { std::move (errorMessage), returnCode };
299}
300
301int 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
318const 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
334int 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
347int 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
363void 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
387const std::vector<ConsoleApplication::Command>& ConsoleApplication::getCommands() const
388{
389 return commands;
390}
391
392static 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
400static 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
428void 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
Represents a local file or directory.
Definition: juce_File.h:45
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
static File getCurrentWorkingDirectory()
Returns the current working directory.
A special array for holding a list of strings.
void removeEmptyStrings(bool removeWhitespaceStrings=true)
Removes empty strings from the array.
static StringArray fromTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Returns an array containing the tokens in a given string.
void trim()
Deletes any whitespace characters from the starts and ends of all the strings.
A simple class for holding temporary references to a string literal or String.
The JUCE String class!
Definition: juce_String.h:43
int indexOfChar(juce_wchar characterToLookFor) const noexcept
Searches for a character inside this string.
int length() const noexcept
Returns the number of characters in the string.
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from the last occurrence of a given substring.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Definition: juce_String.h:306
String argumentDescription
A description of the command-line arguments needed for this command, which will be printed as part of...
String longDescription
A longer description of this command, for use in extended help.
Represents a command that can be executed if its command-line arguments are matched.
One of the arguments in an ArgumentList.
bool operator!=(StringRef stringToCompare) const
Compares this argument against a string.
File resolveAsExistingFile() const
Resolves this argument as an absolute File, using the current working directory as a base for resolvi...
String text
The original text of this argument.
bool isLongOption() const
Returns true if this argument starts with a double dash.
File resolveAsExistingFolder() const
Resolves a user-supplied folder name into an absolute File, using the current working directory as a ...
bool isShortOption() const
Returns true if this argument starts with a single dash.
File resolveAsFile() const
Resolves this argument as an absolute File, using the current working directory as a base for resolvi...
bool operator==(StringRef stringToCompare) const
Compares this argument against a string.
String getLongOptionValue() const
If this argument is a long option with a value, this returns the value.
bool isOption() const
Returns true if this argument starts with one or more dashes.
Holds a list of command-line arguments, and provides useful methods for searching and operating on th...
File getExistingFileForOptionAndRemove(StringRef option)
Looks for a file argument using getFileForOption() and fails with a suitable error if the file doesn'...
String removeValueForOption(StringRef option)
Looks for a given argument and returns either its assigned value (for long options) or the string tha...
File getExistingFileForOption(StringRef option) const
Looks for a file argument using getFileForOption() and fails with a suitable error if the file doesn'...
bool removeOptionIfFound(StringRef option)
Returns true if the given string matches one of the arguments, and also removes the argument from the...
File getExistingFolderForOption(StringRef option) const
Looks for a filename argument using getFileForOption() and fails with a suitable error if the file is...
int indexOfOption(StringRef option) const
Returns the index of the given string if it matches one of the arguments, or -1 if it doesn't.
String executableName
The name or path of the executable that was invoked, as it was specified on the command-line.
File getFileForOption(StringRef option) const
Looks for the value of argument using getValueForOption() and tries to parse that value as a file.
void failIfOptionIsMissing(StringRef option) const
Throws an error unless the given option is found in the argument list.
Array< Argument > arguments
The list of arguments (not including the name of the executable that was invoked).
bool containsOption(StringRef option) const
Returns true if the given string matches one of the arguments.
void checkMinNumArguments(int expectedMinNumberOfArgs) const
Throws an error unless there are at least the given number of arguments.
String getValueForOption(StringRef option) const
Looks for a given argument and returns either its assigned value (for long options) or the string tha...
Argument operator[](int index) const
Returns one of the arguments.
int size() const
Returns the number of arguments in the list.
ArgumentList(String executable, StringArray arguments)
Creates an argument list for a given executable.
File getFileForOptionAndRemove(StringRef option)
Looks for the value of argument using getValueForOption() and tries to parse that value as a file.
File getExistingFolderForOptionAndRemove(StringRef option)
Looks for a filename argument using getFileForOption() and fails with a suitable error if the file is...
void printCommandDetails(const ArgumentList &, const Command &) const
Prints out a longer description of a particular command, based on its longDescription member.
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.
void addDefaultCommand(Command)
Adds a command to the list, and marks it as one which is invoked if no other command matches.
void printCommandList(const ArgumentList &) const
Prints out the list of commands and their short descriptions in a format that's suitable for use as h...
const Command * findCommand(const ArgumentList &, bool optionMustBeFirstArg) const
Looks for the first command in the list which matches the given arguments.
void addCommand(Command)
Adds a command to the list.
static int invokeCatchingFailures(std::function< int()> &&functionToCall)
Invokes a function, catching any fail() calls that it might trigger, and handling them by printing th...
void addHelpCommand(String helpArgument, String helpMessage, bool makeDefaultCommand)
Adds a help command to the list.
void addVersionCommand(String versionArgument, String versionText)
Adds a command that will print the given text in response to the "--version" option.
static void fail(String errorMessage, int returnCode=1)
Throws a failure exception to cause a command-line app to terminate.
const std::vector< Command > & getCommands() const
Gives read-only access to the list of registered commands.