diff options
Diffstat (limited to 'js/src/shell/jsoptparse.h')
-rw-r--r-- | js/src/shell/jsoptparse.h | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/js/src/shell/jsoptparse.h b/js/src/shell/jsoptparse.h new file mode 100644 index 0000000000..17e4878969 --- /dev/null +++ b/js/src/shell/jsoptparse.h @@ -0,0 +1,336 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef shell_jsoptparse_h +#define shell_jsoptparse_h + +#include <stdio.h> + +#include "js/AllocPolicy.h" +#include "js/Utility.h" +#include "js/Vector.h" + +namespace js { +namespace cli { + +namespace detail { + +// We want to use the shell's option parser before initializing the JS engine. +// The JS malloc arena isn't available yet at this point, so we use a custom +// allocation policy that uses the system malloc instead. +class OptionAllocPolicy { + public: + template <typename T> + T* pod_malloc(size_t numElems) { + size_t bytes; + if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) { + return nullptr; + } + return static_cast<T*>(malloc(bytes)); + } + + template <typename T> + T* pod_realloc(T* p, size_t oldSize, size_t newSize) { + size_t bytes; + if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) { + return nullptr; + } + return static_cast<T*>(realloc(p, bytes)); + } + + void reportAllocOverflow() const {} + bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); } + + template <typename T> + void free_(T* p, size_t numElems = 0) { + free(p); + } +}; + +struct BoolOption; +struct MultiStringOption; +struct ValuedOption; +struct StringOption; +struct IntOption; + +enum OptionKind { + OptionKindBool, + OptionKindString, + OptionKindInt, + OptionKindMultiString, + OptionKindInvalid +}; + +struct Option { + const char* longflag; + const char* help; + OptionKind kind; + char shortflag; + bool terminatesOptions; + + Option(OptionKind kind, char shortflag, const char* longflag, + const char* help) + : longflag(longflag), + help(help), + kind(kind), + shortflag(shortflag), + terminatesOptions(false) {} + + virtual ~Option() = 0; + + void setTerminatesOptions(bool enabled) { terminatesOptions = enabled; } + bool getTerminatesOptions() const { return terminatesOptions; } + + virtual bool isValued() const { return false; } + + /* Only some valued options are variadic (like MultiStringOptions). */ + virtual bool isVariadic() const { return false; } + + /* + * For arguments, the shortflag field is used to indicate whether the + * argument is optional. + */ + bool isOptional() { return shortflag; } + + void setFlagInfo(char shortflag, const char* longflag, const char* help) { + this->shortflag = shortflag; + this->longflag = longflag; + this->help = help; + } + + ValuedOption* asValued(); + const ValuedOption* asValued() const; + +#define OPTION_CONVERT_DECL(__cls) \ + bool is##__cls##Option() const; \ + __cls##Option* as##__cls##Option(); \ + const __cls##Option* as##__cls##Option() const; + + OPTION_CONVERT_DECL(Bool) + OPTION_CONVERT_DECL(String) + OPTION_CONVERT_DECL(Int) + OPTION_CONVERT_DECL(MultiString) +}; + +inline Option::~Option() {} + +struct BoolOption : public Option { + size_t argno; + bool value; + + BoolOption(char shortflag, const char* longflag, const char* help) + : Option(OptionKindBool, shortflag, longflag, help), value(false) {} + + virtual ~BoolOption() {} +}; + +struct ValuedOption : public Option { + const char* metavar; + + ValuedOption(OptionKind kind, char shortflag, const char* longflag, + const char* help, const char* metavar) + : Option(kind, shortflag, longflag, help), metavar(metavar) {} + + virtual ~ValuedOption() = 0; + virtual bool isValued() const override { return true; } +}; + +inline ValuedOption::~ValuedOption() {} + +struct StringOption : public ValuedOption { + const char* value; + + StringOption(char shortflag, const char* longflag, const char* help, + const char* metavar) + : ValuedOption(OptionKindString, shortflag, longflag, help, metavar), + value(nullptr) {} + + virtual ~StringOption() {} +}; + +struct IntOption : public ValuedOption { + int value; + + IntOption(char shortflag, const char* longflag, const char* help, + const char* metavar, int defaultValue) + : ValuedOption(OptionKindInt, shortflag, longflag, help, metavar), + value(defaultValue) {} + + virtual ~IntOption() {} +}; + +struct StringArg { + char* value; + size_t argno; + + StringArg(char* value, size_t argno) : value(value), argno(argno) {} +}; + +struct MultiStringOption : public ValuedOption { + Vector<StringArg, 0, detail::OptionAllocPolicy> strings; + + MultiStringOption(char shortflag, const char* longflag, const char* help, + const char* metavar) + : ValuedOption(OptionKindMultiString, shortflag, longflag, help, + metavar) {} + + virtual ~MultiStringOption() {} + + virtual bool isVariadic() const override { return true; } +}; + +} /* namespace detail */ + +class MultiStringRange { + typedef detail::StringArg StringArg; + const StringArg* cur; + const StringArg* end; + + public: + explicit MultiStringRange(const StringArg* cur, const StringArg* end) + : cur(cur), end(end) { + MOZ_ASSERT(end - cur >= 0); + } + + bool empty() const { return cur == end; } + void popFront() { + MOZ_ASSERT(!empty()); + ++cur; + } + char* front() const { + MOZ_ASSERT(!empty()); + return cur->value; + } + size_t argno() const { + MOZ_ASSERT(!empty()); + return cur->argno; + } +}; + +/* + * Builder for describing a command line interface and parsing the resulting + * specification. + * + * - A multi-option is an option that can appear multiple times and still + * parse as valid command line arguments. + * - An "optional argument" is supported for backwards compatibility with prior + * command line interface usage. Once one optional argument has been added, + * *only* optional arguments may be added. + */ +class OptionParser { + public: + enum Result { + Okay = 0, + Fail, /* As in, allocation fail. */ + ParseError, /* Successfully parsed but with an error. */ + EarlyExit /* Successfully parsed but exits the program, + * for example with --help and --version. */ + }; + + private: + typedef Vector<detail::Option*, 0, detail::OptionAllocPolicy> Options; + typedef detail::Option Option; + typedef detail::BoolOption BoolOption; + + Options options; + Options arguments; + BoolOption helpOption; + BoolOption versionOption; + const char* usage; + const char* version; + const char* descr; + size_t descrWidth; + size_t helpWidth; + size_t nextArgument; + + // If '--' is passed, all remaining arguments should be interpreted as the + // argument at index 'restArgument'. Defaults to the next unassigned + // argument. + int restArgument; + + Option* findOption(char shortflag); + const Option* findOption(char shortflag) const; + const Option* tryFindOption(char shortflag) const; + Option* findOption(const char* longflag); + const Option* findOption(const char* longflag) const; + const Option* tryFindOption(const char* longflag) const; + int findArgumentIndex(const char* name) const; + Option* findArgument(const char* name); + const Option* findArgument(const char* name) const; + + Result error(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3); + Result extractValue(size_t argc, char** argv, size_t* i, char** value); + Result handleArg(size_t argc, char** argv, size_t* i, bool* optsAllowed); + Result handleOption(Option* opt, size_t argc, char** argv, size_t* i, + bool* optsAllowed); + + public: + explicit OptionParser(const char* usage) + : helpOption('h', "help", "Display help information"), + versionOption('v', "version", "Display version information and exit"), + usage(usage), + version(nullptr), + descr(nullptr), + descrWidth(80), + helpWidth(80), + nextArgument(0), + restArgument(-1) {} + + ~OptionParser(); + + Result parseArgs(int argc, char** argv); + Result printHelp(const char* progname); + Result printVersion(); + + /* Metadata */ + + void setVersion(const char* v) { version = v; } + void setHelpWidth(size_t width) { helpWidth = width; } + void setDescriptionWidth(size_t width) { descrWidth = width; } + void setDescription(const char* description) { descr = description; } + void setHelpOption(char shortflag, const char* longflag, const char* help); + void setArgTerminatesOptions(const char* name, bool enabled); + void setArgCapturesRest(const char* name); + + /* Arguments: no further arguments may be added after a variadic argument. */ + + bool addOptionalStringArg(const char* name, const char* help); + bool addOptionalMultiStringArg(const char* name, const char* help); + + const char* getStringArg(const char* name) const; + MultiStringRange getMultiStringArg(const char* name) const; + + /* Options */ + + bool addBoolOption(char shortflag, const char* longflag, const char* help); + bool addStringOption(char shortflag, const char* longflag, const char* help, + const char* metavar); + bool addIntOption(char shortflag, const char* longflag, const char* help, + const char* metavar, int defaultValue); + bool addMultiStringOption(char shortflag, const char* longflag, + const char* help, const char* metavar); + bool addOptionalVariadicArg(const char* name); + + int getIntOption(char shortflag) const; + int getIntOption(const char* longflag) const; + const char* getStringOption(char shortflag) const; + const char* getStringOption(const char* longflag) const; + bool getBoolOption(char shortflag) const; + bool getBoolOption(const char* longflag) const; + MultiStringRange getMultiStringOption(char shortflag) const; + MultiStringRange getMultiStringOption(const char* longflag) const; + + /* + * Return whether the help option was present (and thus help was already + * displayed during parse_args). + */ + bool getHelpOption() const; +}; + +} /* namespace cli */ +} /* namespace js */ + +#endif /* shell_jsoptparse_h */ |