summaryrefslogtreecommitdiffstats
path: root/js/src/shell/jsoptparse.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/shell/jsoptparse.h')
-rw-r--r--js/src/shell/jsoptparse.h336
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 */