michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef shell_jsoptparse_h michael@0: #define shell_jsoptparse_h michael@0: michael@0: #include michael@0: michael@0: #include "jsalloc.h" michael@0: #include "jsutil.h" michael@0: michael@0: #include "js/Vector.h" michael@0: michael@0: namespace js { michael@0: namespace cli { michael@0: michael@0: namespace detail { michael@0: michael@0: struct BoolOption; michael@0: struct MultiStringOption; michael@0: struct ValuedOption; michael@0: struct StringOption; michael@0: struct IntOption; michael@0: michael@0: enum OptionKind michael@0: { michael@0: OptionKindBool, michael@0: OptionKindString, michael@0: OptionKindInt, michael@0: OptionKindMultiString, michael@0: OptionKindInvalid michael@0: }; michael@0: michael@0: struct Option michael@0: { michael@0: const char *longflag; michael@0: const char *help; michael@0: OptionKind kind; michael@0: char shortflag; michael@0: bool terminatesOptions; michael@0: michael@0: Option(OptionKind kind, char shortflag, const char *longflag, const char *help) michael@0: : longflag(longflag), help(help), kind(kind), shortflag(shortflag), terminatesOptions(false) michael@0: {} michael@0: michael@0: virtual ~Option() = 0; michael@0: michael@0: void setTerminatesOptions(bool enabled) { terminatesOptions = enabled; } michael@0: bool getTerminatesOptions() const { return terminatesOptions; } michael@0: michael@0: virtual bool isValued() const { return false; } michael@0: michael@0: /* Only some valued options are variadic (like MultiStringOptions). */ michael@0: virtual bool isVariadic() const { return false; } michael@0: michael@0: /* michael@0: * For arguments, the shortflag field is used to indicate whether the michael@0: * argument is optional. michael@0: */ michael@0: bool isOptional() { return shortflag; } michael@0: michael@0: void setFlagInfo(char shortflag, const char *longflag, const char *help) { michael@0: this->shortflag = shortflag; michael@0: this->longflag = longflag; michael@0: this->help = help; michael@0: } michael@0: michael@0: ValuedOption *asValued(); michael@0: const ValuedOption *asValued() const; michael@0: michael@0: #define OPTION_CONVERT_DECL(__cls) \ michael@0: bool is##__cls##Option() const; \ michael@0: __cls##Option *as##__cls##Option(); \ michael@0: const __cls##Option *as##__cls##Option() const; michael@0: michael@0: OPTION_CONVERT_DECL(Bool) michael@0: OPTION_CONVERT_DECL(String) michael@0: OPTION_CONVERT_DECL(Int) michael@0: OPTION_CONVERT_DECL(MultiString) michael@0: }; michael@0: michael@0: inline Option::~Option() {} michael@0: michael@0: struct BoolOption : public Option michael@0: { michael@0: size_t argno; michael@0: bool value; michael@0: michael@0: BoolOption(char shortflag, const char *longflag, const char *help) michael@0: : Option(OptionKindBool, shortflag, longflag, help), value(false) michael@0: {} michael@0: michael@0: virtual ~BoolOption() {} michael@0: }; michael@0: michael@0: struct ValuedOption : public Option michael@0: { michael@0: const char *metavar; michael@0: michael@0: ValuedOption(OptionKind kind, char shortflag, const char *longflag, const char *help, michael@0: const char *metavar) michael@0: : Option(kind, shortflag, longflag, help), metavar(metavar) michael@0: {} michael@0: michael@0: virtual ~ValuedOption() = 0; michael@0: virtual bool isValued() const { return true; } michael@0: }; michael@0: michael@0: inline ValuedOption::~ValuedOption() {} michael@0: michael@0: struct StringOption : public ValuedOption michael@0: { michael@0: const char *value; michael@0: michael@0: StringOption(char shortflag, const char *longflag, const char *help, const char *metavar) michael@0: : ValuedOption(OptionKindString, shortflag, longflag, help, metavar), value(nullptr) michael@0: {} michael@0: michael@0: virtual ~StringOption() {} michael@0: }; michael@0: michael@0: struct IntOption : public ValuedOption michael@0: { michael@0: int value; michael@0: michael@0: IntOption(char shortflag, const char *longflag, const char *help, const char *metavar, michael@0: int defaultValue) michael@0: : ValuedOption(OptionKindInt, shortflag, longflag, help, metavar), value(defaultValue) michael@0: {} michael@0: michael@0: virtual ~IntOption() {} michael@0: }; michael@0: michael@0: struct StringArg michael@0: { michael@0: char *value; michael@0: size_t argno; michael@0: michael@0: StringArg(char *value, size_t argno) : value(value), argno(argno) {} michael@0: }; michael@0: michael@0: struct MultiStringOption : public ValuedOption michael@0: { michael@0: Vector strings; michael@0: michael@0: MultiStringOption(char shortflag, const char *longflag, const char *help, const char *metavar) michael@0: : ValuedOption(OptionKindMultiString, shortflag, longflag, help, metavar) michael@0: {} michael@0: michael@0: virtual ~MultiStringOption() {} michael@0: michael@0: virtual bool isVariadic() const { return true; } michael@0: }; michael@0: michael@0: } /* namespace detail */ michael@0: michael@0: class MultiStringRange michael@0: { michael@0: typedef detail::StringArg StringArg; michael@0: const StringArg *cur; michael@0: const StringArg *end; michael@0: michael@0: public: michael@0: explicit MultiStringRange(const StringArg *cur, const StringArg *end) michael@0: : cur(cur), end(end) { michael@0: JS_ASSERT(end - cur >= 0); michael@0: } michael@0: michael@0: bool empty() const { return cur == end; } michael@0: void popFront() { JS_ASSERT(!empty()); ++cur; } michael@0: char *front() const { JS_ASSERT(!empty()); return cur->value; } michael@0: size_t argno() const { JS_ASSERT(!empty()); return cur->argno; } michael@0: }; michael@0: michael@0: /* michael@0: * Builder for describing a command line interface and parsing the resulting michael@0: * specification. michael@0: * michael@0: * - A multi-option is an option that can appear multiple times and still michael@0: * parse as valid command line arguments. michael@0: * - An "optional argument" is supported for backwards compatibility with prior michael@0: * command line interface usage. Once one optional argument has been added, michael@0: * *only* optional arguments may be added. michael@0: */ michael@0: class OptionParser michael@0: { michael@0: public: michael@0: enum Result michael@0: { michael@0: Okay = 0, michael@0: Fail, /* As in, allocation fail. */ michael@0: ParseError, /* Successfully parsed but with an error. */ michael@0: ParseHelp /* Aborted on help flag. */ michael@0: }; michael@0: michael@0: private: michael@0: typedef Vector Options; michael@0: typedef detail::Option Option; michael@0: typedef detail::BoolOption BoolOption; michael@0: michael@0: Options options; michael@0: Options arguments; michael@0: BoolOption helpOption; michael@0: const char *usage; michael@0: const char *ver; michael@0: const char *descr; michael@0: size_t descrWidth; michael@0: size_t helpWidth; michael@0: size_t nextArgument; michael@0: michael@0: // If '--' is passed, all remaining arguments should be interpreted as the michael@0: // argument at index 'restArgument'. Defaults to the next unassigned michael@0: // argument. michael@0: int restArgument; michael@0: michael@0: static const char prognameMeta[]; michael@0: michael@0: Option *findOption(char shortflag); michael@0: const Option *findOption(char shortflag) const; michael@0: Option *findOption(const char *longflag); michael@0: const Option *findOption(const char *longflag) const; michael@0: int findArgumentIndex(const char *name) const; michael@0: Option *findArgument(const char *name); michael@0: const Option *findArgument(const char *name) const; michael@0: michael@0: Result error(const char *fmt, ...); michael@0: Result extractValue(size_t argc, char **argv, size_t *i, char **value); michael@0: Result handleArg(size_t argc, char **argv, size_t *i, bool *optsAllowed); michael@0: Result handleOption(Option *opt, size_t argc, char **argv, size_t *i, bool *optsAllowed); michael@0: michael@0: public: michael@0: explicit OptionParser(const char *usage) michael@0: : helpOption('h', "help", "Display help information"), michael@0: usage(usage), ver(nullptr), descr(nullptr), descrWidth(80), helpWidth(80), michael@0: nextArgument(0), restArgument(-1) michael@0: {} michael@0: michael@0: ~OptionParser(); michael@0: michael@0: Result parseArgs(int argc, char **argv); michael@0: Result printHelp(const char *progname); michael@0: michael@0: /* Metadata */ michael@0: michael@0: void setVersion(const char *version) { ver = version; } michael@0: void setHelpWidth(size_t width) { helpWidth = width; } michael@0: void setDescriptionWidth(size_t width) { descrWidth = width; } michael@0: void setDescription(const char *description) { descr = description; } michael@0: void setHelpOption(char shortflag, const char *longflag, const char *help); michael@0: void setArgTerminatesOptions(const char *name, bool enabled); michael@0: void setArgCapturesRest(const char *name); michael@0: michael@0: /* Arguments: no further arguments may be added after a variadic argument. */ michael@0: michael@0: bool addOptionalStringArg(const char *name, const char *help); michael@0: bool addOptionalMultiStringArg(const char *name, const char *help); michael@0: michael@0: const char *getStringArg(const char *name) const; michael@0: MultiStringRange getMultiStringArg(const char *name) const; michael@0: michael@0: /* Options */ michael@0: michael@0: bool addBoolOption(char shortflag, const char *longflag, const char *help); michael@0: bool addStringOption(char shortflag, const char *longflag, const char *help, michael@0: const char *metavar); michael@0: bool addIntOption(char shortflag, const char *longflag, const char *help, michael@0: const char *metavar, int defaultValue); michael@0: bool addMultiStringOption(char shortflag, const char *longflag, const char *help, michael@0: const char *metavar); michael@0: bool addOptionalVariadicArg(const char *name); michael@0: michael@0: int getIntOption(char shortflag) const; michael@0: int getIntOption(const char *longflag) const; michael@0: const char *getStringOption(char shortflag) const; michael@0: const char *getStringOption(const char *longflag) const; michael@0: bool getBoolOption(char shortflag) const; michael@0: bool getBoolOption(const char *longflag) const; michael@0: MultiStringRange getMultiStringOption(char shortflag) const; michael@0: MultiStringRange getMultiStringOption(const char *longflag) const; michael@0: michael@0: /* michael@0: * Return whether the help option was present (and thus help was already michael@0: * displayed during parse_args). michael@0: */ michael@0: bool getHelpOption() const; michael@0: }; michael@0: michael@0: } /* namespace cli */ michael@0: } /* namespace js */ michael@0: michael@0: #endif /* shell_jsoptparse_h */