|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2017 - ROLI Ltd.
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- File ArgumentList::Argument::resolveAsFile() const
- {
- return File::getCurrentWorkingDirectory().getChildFile (text.unquoted());
- }
-
- File ArgumentList::Argument::resolveAsExistingFile() const
- {
- auto f = resolveAsFile();
-
- if (! f.exists())
- ConsoleApplication::fail ("Could not find file: " + f.getFullPathName());
-
- return f;
- }
-
- File ArgumentList::Argument::resolveAsExistingFolder() const
- {
- auto f = resolveAsFile();
-
- if (! f.isDirectory())
- ConsoleApplication::fail ("Could not find folder: " + f.getFullPathName());
-
- return f;
- }
-
- bool ArgumentList::Argument::isLongOption() const { return text[0] == '-' && text[1] == '-' && text[2] != '-'; }
- bool ArgumentList::Argument::isShortOption() const { return text[0] == '-' && text[1] != '-'; }
-
- bool ArgumentList::Argument::isLongOption (const String& option) const
- {
- if (option.startsWith ("--"))
- return text == option;
-
- jassert (! option.startsWithChar ('-')); // this will always fail to match
-
- return text == "--" + option;
- }
-
- bool ArgumentList::Argument::isShortOption (char option) const
- {
- jassert (option != '-'); // this is probably not what you intended to pass in
-
- return isShortOption() && text.containsChar (option);
- }
-
- static bool compareOptionStrings (StringRef s1, StringRef s2)
- {
- if (s1 == s2)
- return true;
-
- auto toks1 = StringArray::fromTokens (s1, "|", {});
- auto toks2 = StringArray::fromTokens (s2, "|", {});
-
- for (auto& part1 : toks1)
- for (auto& part2 : toks2)
- if (part1.trim() == part2.trim())
- return true;
-
- return false;
- }
-
- bool ArgumentList::Argument::operator== (StringRef s) const { return compareOptionStrings (text, s); }
- bool ArgumentList::Argument::operator!= (StringRef s) const { return ! operator== (s); }
-
- //==============================================================================
- ArgumentList::ArgumentList (String exeName, StringArray args)
- : executableName (std::move (exeName))
- {
- args.trim();
-
- for (auto& a : args)
- arguments.add ({ a });
- }
-
- ArgumentList::ArgumentList (int argc, char* argv[])
- : ArgumentList (argv[0], StringArray (argv + 1, argc - 1))
- {
- }
-
- ArgumentList::ArgumentList (const String& exeName, const String& args)
- : ArgumentList (exeName, StringArray::fromTokens (args, true))
- {
- }
-
- int ArgumentList::size() const { return arguments.size(); }
- ArgumentList::Argument ArgumentList::operator[] (int index) const { return arguments[index]; }
-
- void ArgumentList::checkMinNumArguments (int expectedMinNumberOfArgs) const
- {
- if (size() < expectedMinNumberOfArgs)
- ConsoleApplication::fail ("Not enough arguments!");
- }
-
- int ArgumentList::indexOfOption (StringRef option) const
- {
- jassert (option == String (option).trim()); // passing non-trimmed strings will always fail to find a match!
-
- for (int i = 0; i < arguments.size(); ++i)
- if (arguments.getReference(i) == option)
- return i;
-
- return -1;
- }
-
- bool ArgumentList::containsOption (StringRef option) const
- {
- return indexOfOption (option) >= 0;
- }
-
- void ArgumentList::failIfOptionIsMissing (StringRef option) const
- {
- if (! containsOption (option))
- ConsoleApplication::fail ("Expected the option " + option);
- }
-
- ArgumentList::Argument ArgumentList::getArgumentAfterOption (StringRef option) const
- {
- for (int i = 0; i < arguments.size() - 1; ++i)
- if (arguments.getReference(i) == option)
- return arguments.getReference (i + 1);
-
- return {};
- }
-
- File ArgumentList::getFileAfterOption (StringRef option) const
- {
- failIfOptionIsMissing (option);
- auto arg = getArgumentAfterOption (option);
-
- if (arg.text.isEmpty() || arg.text.startsWithChar ('-'))
- ConsoleApplication::fail ("Expected a filename after the " + option + " option");
-
- return arg.resolveAsFile();
- }
-
- File ArgumentList::getExistingFileAfterOption (StringRef option) const
- {
- failIfOptionIsMissing (option);
- auto arg = getArgumentAfterOption (option);
-
- if (arg.text.isEmpty())
- ConsoleApplication::fail ("Expected a filename after the " + option + " option");
-
- return arg.resolveAsExistingFile();
- }
-
- File ArgumentList::getExistingFolderAfterOption (StringRef option) const
- {
- failIfOptionIsMissing (option);
- auto arg = getArgumentAfterOption (option);
-
- if (arg.text.isEmpty())
- ConsoleApplication::fail ("Expected a folder name after the " + option + " option");
-
- return arg.resolveAsExistingFolder();
- }
-
- //==============================================================================
- struct ConsoleAppFailureCode
- {
- String errorMessage;
- int returnCode;
- };
-
- void ConsoleApplication::fail (String errorMessage, int returnCode)
- {
- throw ConsoleAppFailureCode { std::move (errorMessage), returnCode };
- }
-
- int ConsoleApplication::invokeCatchingFailures (std::function<int()>&& f)
- {
- int returnCode = 0;
-
- try
- {
- returnCode = f();
- }
- catch (const ConsoleAppFailureCode& error)
- {
- std::cout << error.errorMessage << std::endl;
- returnCode = error.returnCode;
- }
-
- return returnCode;
- }
-
- int ConsoleApplication::findAndRunCommand (const ArgumentList& args) const
- {
- for (auto& c : commands)
- if (args.containsOption (c.commandOption))
- return invokeCatchingFailures ([&] { c.command (args); return 0; });
-
- if (commandIfNoOthersRecognised.isNotEmpty())
- for (auto& c : commands)
- if (compareOptionStrings (c.commandOption, commandIfNoOthersRecognised))
- return invokeCatchingFailures ([&] { c.command (args); return 0; });
-
- fail ("Unrecognised arguments");
- return 0;
- }
-
- int ConsoleApplication::findAndRunCommand (int argc, char* argv[]) const
- {
- return findAndRunCommand (ArgumentList (argc, argv));
- }
-
- void ConsoleApplication::addCommand (Command c)
- {
- commands.emplace_back (std::move (c));
- }
-
- void ConsoleApplication::addHelpCommand (String arg, String helpMessage, bool invokeIfNoOtherCommandRecognised)
- {
- addCommand ({ arg, arg, "Prints this message",
- [this, helpMessage] (const ArgumentList& args) { printHelp (helpMessage, args); }});
-
- if (invokeIfNoOtherCommandRecognised)
- commandIfNoOthersRecognised = arg;
- }
-
- void ConsoleApplication::addVersionCommand (String arg, String versionText)
- {
- addCommand ({ arg, arg, "Prints the current version number",
- [versionText] (const ArgumentList&)
- {
- std::cout << versionText << std::endl;
- }});
- }
-
- void ConsoleApplication::printHelp (const String& preamble, const ArgumentList& args) const
- {
- std::cout << preamble << std::endl;
-
- auto exeName = args.executableName.fromLastOccurrenceOf ("/", false, false)
- .fromLastOccurrenceOf ("\\", false, false);
-
- StringArray namesAndArgs;
- int maxLength = 0;
-
- for (auto& c : commands)
- {
- auto nameAndArgs = exeName + " " + c.argumentDescription;
- namesAndArgs.add (nameAndArgs);
- maxLength = std::max (maxLength, nameAndArgs.length());
- }
-
- for (size_t i = 0; i < commands.size(); ++i)
- std::cout << " " << namesAndArgs[(int) i].paddedRight (' ', maxLength + 2)
- << commands[i].commandDescription << std::endl;
-
- std::cout << std::endl;
- }
-
- } // namespace juce
|