summaryrefslogtreecommitdiffstats
path: root/js/src/shell/jsoptparse.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/src/shell/jsoptparse.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/shell/jsoptparse.cpp')
-rw-r--r--js/src/shell/jsoptparse.cpp644
1 files changed, 644 insertions, 0 deletions
diff --git a/js/src/shell/jsoptparse.cpp b/js/src/shell/jsoptparse.cpp
new file mode 100644
index 000000000..eaf4042d3
--- /dev/null
+++ b/js/src/shell/jsoptparse.cpp
@@ -0,0 +1,644 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#include "shell/jsoptparse.h"
+
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "jsutil.h"
+
+using namespace js;
+using namespace js::cli;
+using namespace js::cli::detail;
+
+const char OptionParser::prognameMeta[] = "{progname}";
+
+#define OPTION_CONVERT_IMPL(__cls) \
+ bool \
+ Option::is##__cls##Option() const \
+ { \
+ return kind == OptionKind##__cls; \
+ } \
+ __cls##Option * \
+ Option::as##__cls##Option() \
+ { \
+ MOZ_ASSERT(is##__cls##Option()); \
+ return static_cast<__cls##Option*>(this); \
+ } \
+ const __cls##Option * \
+ Option::as##__cls##Option() const \
+ { \
+ return const_cast<Option*>(this)->as##__cls##Option(); \
+ }
+
+ValuedOption*
+Option::asValued()
+{
+ MOZ_ASSERT(isValued());
+ return static_cast<ValuedOption*>(this);
+}
+
+const ValuedOption*
+Option::asValued() const
+{
+ return const_cast<Option*>(this)->asValued();
+}
+
+OPTION_CONVERT_IMPL(Bool)
+OPTION_CONVERT_IMPL(String)
+OPTION_CONVERT_IMPL(Int)
+OPTION_CONVERT_IMPL(MultiString)
+
+void
+OptionParser::setArgTerminatesOptions(const char* name, bool enabled)
+{
+ findArgument(name)->setTerminatesOptions(enabled);
+}
+
+void
+OptionParser::setArgCapturesRest(const char* name)
+{
+ MOZ_ASSERT(restArgument == -1, "only one argument may be set to capture the rest");
+ restArgument = findArgumentIndex(name);
+ MOZ_ASSERT(restArgument != -1, "unknown argument name passed to setArgCapturesRest");
+}
+
+OptionParser::Result
+OptionParser::error(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "Error: ");
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fputs("\n\n", stderr);
+ return ParseError;
+}
+
+/* Quick and dirty paragraph printer. */
+static void
+PrintParagraph(const char* text, unsigned startColno, const unsigned limitColno, bool padFirstLine)
+{
+ unsigned colno = startColno;
+ unsigned indent = 0;
+ const char* it = text;
+
+ if (padFirstLine)
+ printf("%*s", startColno, "");
+
+ /* Skip any leading spaces. */
+ while (*it != '\0' && isspace(*it))
+ ++it;
+
+ while (*it != '\0') {
+ MOZ_ASSERT(!isspace(*it) || *it == '\n');
+
+ /* Delimit the current token. */
+ const char* limit = it;
+ while (!isspace(*limit) && *limit != '\0')
+ ++limit;
+
+ /*
+ * If the current token is longer than the available number of columns,
+ * then make a line break before printing the token.
+ */
+ size_t tokLen = limit - it;
+ if (tokLen + colno >= limitColno) {
+ printf("\n%*s%.*s", startColno + indent, "", int(tokLen), it);
+ colno = startColno + tokLen;
+ } else {
+ printf("%.*s", int(tokLen), it);
+ colno += tokLen;
+ }
+
+ switch (*limit) {
+ case '\0':
+ return;
+ case ' ':
+ putchar(' ');
+ colno += 1;
+ it = limit;
+ while (*it == ' ')
+ ++it;
+ break;
+ case '\n':
+ /* |text| wants to force a newline here. */
+ printf("\n%*s", startColno, "");
+ colno = startColno;
+ it = limit + 1;
+ /* Could also have line-leading spaces. */
+ indent = 0;
+ while (*it == ' ') {
+ putchar(' ');
+ ++colno;
+ ++indent;
+ ++it;
+ }
+ break;
+ default:
+ MOZ_CRASH("unhandled token splitting character in text");
+ }
+ }
+}
+
+static const char*
+OptionFlagsToFormatInfo(char shortflag, bool isValued, size_t* length)
+{
+ static const char * const fmt[4] = { " -%c --%s ",
+ " --%s ",
+ " -%c --%s=%s ",
+ " --%s=%s " };
+
+ /* How mny chars w/o longflag? */
+ size_t lengths[4] = { strlen(fmt[0]) - 3,
+ strlen(fmt[1]) - 3,
+ strlen(fmt[2]) - 5,
+ strlen(fmt[3]) - 5 };
+ int index = isValued ? 2 : 0;
+ if (!shortflag)
+ index++;
+
+ *length = lengths[index];
+ return fmt[index];
+}
+
+OptionParser::Result
+OptionParser::printHelp(const char* progname)
+{
+ const char* prefixEnd = strstr(usage, prognameMeta);
+ if (prefixEnd) {
+ printf("%.*s%s%s\n", int(prefixEnd - usage), usage, progname,
+ prefixEnd + sizeof(prognameMeta) - 1);
+ } else {
+ puts(usage);
+ }
+
+ if (descr) {
+ putchar('\n');
+ PrintParagraph(descr, 2, descrWidth, true);
+ putchar('\n');
+ }
+
+ if (version)
+ printf("\nVersion: %s\n\n", version);
+
+ if (!arguments.empty()) {
+ printf("Arguments:\n");
+
+ static const char fmt[] = " %s ";
+ size_t fmtChars = sizeof(fmt) - 2;
+ size_t lhsLen = 0;
+ for (Option* arg : arguments)
+ lhsLen = Max(lhsLen, strlen(arg->longflag) + fmtChars);
+
+ for (Option* arg : arguments) {
+ size_t chars = printf(fmt, arg->longflag);
+ for (; chars < lhsLen; ++chars)
+ putchar(' ');
+ PrintParagraph(arg->help, lhsLen, helpWidth, false);
+ putchar('\n');
+ }
+ putchar('\n');
+ }
+
+ if (!options.empty()) {
+ printf("Options:\n");
+
+ /* Calculate sizes for column alignment. */
+ size_t lhsLen = 0;
+ for (Option* opt : options) {
+ size_t longflagLen = strlen(opt->longflag);
+
+ size_t fmtLen;
+ OptionFlagsToFormatInfo(opt->shortflag, opt->isValued(), &fmtLen);
+
+ size_t len = fmtLen + longflagLen;
+ if (opt->isValued())
+ len += strlen(opt->asValued()->metavar);
+ lhsLen = Max(lhsLen, len);
+ }
+
+ /* Print option help text. */
+ for (Option* opt : options) {
+ size_t fmtLen;
+ const char* fmt = OptionFlagsToFormatInfo(opt->shortflag, opt->isValued(), &fmtLen);
+ size_t chars;
+ if (opt->isValued()) {
+ if (opt->shortflag)
+ chars = printf(fmt, opt->shortflag, opt->longflag, opt->asValued()->metavar);
+ else
+ chars = printf(fmt, opt->longflag, opt->asValued()->metavar);
+ } else {
+ if (opt->shortflag)
+ chars = printf(fmt, opt->shortflag, opt->longflag);
+ else
+ chars = printf(fmt, opt->longflag);
+ }
+ for (; chars < lhsLen; ++chars)
+ putchar(' ');
+ PrintParagraph(opt->help, lhsLen, helpWidth, false);
+ putchar('\n');
+ }
+ }
+
+ return EarlyExit;
+}
+
+OptionParser::Result
+OptionParser::printVersion()
+{
+ MOZ_ASSERT(version);
+ printf("%s\n", version);
+ return EarlyExit;
+}
+
+OptionParser::Result
+OptionParser::extractValue(size_t argc, char** argv, size_t* i, char** value)
+{
+ MOZ_ASSERT(*i < argc);
+ char* eq = strchr(argv[*i], '=');
+ if (eq) {
+ *value = eq + 1;
+ if (*value[0] == '\0')
+ return error("A value is required for option %.*s", (int) (eq - argv[*i]), argv[*i]);
+ return Okay;
+ }
+
+ if (argc == *i + 1)
+ return error("Expected a value for option %s", argv[*i]);
+
+ *i += 1;
+ *value = argv[*i];
+ return Okay;
+}
+
+OptionParser::Result
+OptionParser::handleOption(Option* opt, size_t argc, char** argv, size_t* i, bool* optionsAllowed)
+{
+ if (opt->getTerminatesOptions())
+ *optionsAllowed = false;
+
+ switch (opt->kind) {
+ case OptionKindBool:
+ {
+ if (opt == &helpOption)
+ return printHelp(argv[0]);
+ if (opt == &versionOption)
+ return printVersion();
+ opt->asBoolOption()->value = true;
+ return Okay;
+ }
+ /*
+ * Valued options are allowed to specify their values either via
+ * successive arguments or a single --longflag=value argument.
+ */
+ case OptionKindString:
+ {
+ char* value = nullptr;
+ if (Result r = extractValue(argc, argv, i, &value))
+ return r;
+ opt->asStringOption()->value = value;
+ return Okay;
+ }
+ case OptionKindInt:
+ {
+ char* value = nullptr;
+ if (Result r = extractValue(argc, argv, i, &value))
+ return r;
+ opt->asIntOption()->value = atoi(value);
+ return Okay;
+ }
+ case OptionKindMultiString:
+ {
+ char* value = nullptr;
+ if (Result r = extractValue(argc, argv, i, &value))
+ return r;
+ StringArg arg(value, *i);
+ return opt->asMultiStringOption()->strings.append(arg) ? Okay : Fail;
+ }
+ default:
+ MOZ_CRASH("unhandled option kind");
+ }
+}
+
+OptionParser::Result
+OptionParser::handleArg(size_t argc, char** argv, size_t* i, bool* optionsAllowed)
+{
+ if (nextArgument >= arguments.length())
+ return error("Too many arguments provided");
+
+ Option* arg = arguments[nextArgument];
+
+ if (arg->getTerminatesOptions())
+ *optionsAllowed = false;
+
+ switch (arg->kind) {
+ case OptionKindString:
+ arg->asStringOption()->value = argv[*i];
+ nextArgument += 1;
+ return Okay;
+ case OptionKindMultiString:
+ {
+ /* Don't advance the next argument -- there can only be one (final) variadic argument. */
+ StringArg value(argv[*i], *i);
+ return arg->asMultiStringOption()->strings.append(value) ? Okay : Fail;
+ }
+ default:
+ MOZ_CRASH("unhandled argument kind");
+ }
+}
+
+OptionParser::Result
+OptionParser::parseArgs(int inputArgc, char** argv)
+{
+ MOZ_ASSERT(inputArgc >= 0);
+ size_t argc = inputArgc;
+ /* Permit a "no more options" capability, like |--| offers in many shell interfaces. */
+ bool optionsAllowed = true;
+
+ for (size_t i = 1; i < argc; ++i) {
+ char* arg = argv[i];
+ Result r;
+ /* Note: solo dash option is actually a 'stdin' argument. */
+ if (arg[0] == '-' && arg[1] != '\0' && optionsAllowed) {
+ /* Option. */
+ Option* opt;
+ if (arg[1] == '-') {
+ if (arg[2] == '\0') {
+ /* End of options */
+ optionsAllowed = false;
+ nextArgument = restArgument;
+ continue;
+ } else {
+ /* Long option. */
+ opt = findOption(arg + 2);
+ if (!opt)
+ return error("Invalid long option: %s", arg);
+ }
+ } else {
+ /* Short option */
+ if (arg[2] != '\0')
+ return error("Short option followed by junk: %s", arg);
+ opt = findOption(arg[1]);
+ if (!opt)
+ return error("Invalid short option: %s", arg);
+ }
+
+ r = handleOption(opt, argc, argv, &i, &optionsAllowed);
+ } else {
+ /* Argument. */
+ r = handleArg(argc, argv, &i, &optionsAllowed);
+ }
+
+ if (r != Okay)
+ return r;
+ }
+ return Okay;
+}
+
+void
+OptionParser::setHelpOption(char shortflag, const char* longflag, const char* help)
+{
+ helpOption.setFlagInfo(shortflag, longflag, help);
+}
+
+bool
+OptionParser::getHelpOption() const
+{
+ return helpOption.value;
+}
+
+bool
+OptionParser::getBoolOption(char shortflag) const
+{
+ return findOption(shortflag)->asBoolOption()->value;
+}
+
+int
+OptionParser::getIntOption(char shortflag) const
+{
+ return findOption(shortflag)->asIntOption()->value;
+}
+
+const char*
+OptionParser::getStringOption(char shortflag) const
+{
+ return findOption(shortflag)->asStringOption()->value;
+}
+
+MultiStringRange
+OptionParser::getMultiStringOption(char shortflag) const
+{
+ const MultiStringOption* mso = findOption(shortflag)->asMultiStringOption();
+ return MultiStringRange(mso->strings.begin(), mso->strings.end());
+}
+
+bool
+OptionParser::getBoolOption(const char* longflag) const
+{
+ return findOption(longflag)->asBoolOption()->value;
+}
+
+int
+OptionParser::getIntOption(const char* longflag) const
+{
+ return findOption(longflag)->asIntOption()->value;
+}
+
+const char*
+OptionParser::getStringOption(const char* longflag) const
+{
+ return findOption(longflag)->asStringOption()->value;
+}
+
+MultiStringRange
+OptionParser::getMultiStringOption(const char* longflag) const
+{
+ const MultiStringOption* mso = findOption(longflag)->asMultiStringOption();
+ return MultiStringRange(mso->strings.begin(), mso->strings.end());
+}
+
+OptionParser::~OptionParser()
+{
+ for (Option* opt : options)
+ js_delete<Option>(opt);
+ for (Option* arg : arguments)
+ js_delete<Option>(arg);
+}
+
+Option*
+OptionParser::findOption(char shortflag)
+{
+ for (Option* opt : options) {
+ if (opt->shortflag == shortflag)
+ return opt;
+ }
+
+ if (versionOption.shortflag == shortflag)
+ return &versionOption;
+
+ return helpOption.shortflag == shortflag ? &helpOption : nullptr;
+}
+
+const Option*
+OptionParser::findOption(char shortflag) const
+{
+ return const_cast<OptionParser*>(this)->findOption(shortflag);
+}
+
+Option*
+OptionParser::findOption(const char* longflag)
+{
+ for (Option* opt : options) {
+ const char* target = opt->longflag;
+ if (opt->isValued()) {
+ size_t targetLen = strlen(target);
+ /* Permit a trailing equals sign on the longflag argument. */
+ for (size_t i = 0; i < targetLen; ++i) {
+ if (longflag[i] == '\0' || longflag[i] != target[i])
+ goto no_match;
+ }
+ if (longflag[targetLen] == '\0' || longflag[targetLen] == '=')
+ return opt;
+ } else {
+ if (strcmp(target, longflag) == 0)
+ return opt;
+ }
+ no_match:;
+ }
+
+ if (strcmp(versionOption.longflag, longflag) == 0)
+ return &versionOption;
+
+ return strcmp(helpOption.longflag, longflag) ? nullptr : &helpOption;
+}
+
+const Option*
+OptionParser::findOption(const char* longflag) const
+{
+ return const_cast<OptionParser*>(this)->findOption(longflag);
+}
+
+/* Argument accessors */
+
+int
+OptionParser::findArgumentIndex(const char* name) const
+{
+ for (Option * const* it = arguments.begin(); it != arguments.end(); ++it) {
+ const char* target = (*it)->longflag;
+ if (strcmp(target, name) == 0)
+ return it - arguments.begin();
+ }
+ return -1;
+}
+
+Option*
+OptionParser::findArgument(const char* name)
+{
+ int index = findArgumentIndex(name);
+ return (index == -1) ? nullptr : arguments[index];
+}
+
+const Option*
+OptionParser::findArgument(const char* name) const
+{
+ int index = findArgumentIndex(name);
+ return (index == -1) ? nullptr : arguments[index];
+}
+
+const char*
+OptionParser::getStringArg(const char* name) const
+{
+ return findArgument(name)->asStringOption()->value;
+}
+
+MultiStringRange
+OptionParser::getMultiStringArg(const char* name) const
+{
+ const MultiStringOption* mso = findArgument(name)->asMultiStringOption();
+ return MultiStringRange(mso->strings.begin(), mso->strings.end());
+}
+
+/* Option builders */
+
+bool
+OptionParser::addIntOption(char shortflag, const char* longflag, const char* metavar,
+ const char* help, int defaultValue)
+{
+ if (!options.reserve(options.length() + 1))
+ return false;
+ IntOption* io = js_new<IntOption>(shortflag, longflag, help, metavar, defaultValue);
+ if (!io)
+ return false;
+ options.infallibleAppend(io);
+ return true;
+}
+
+bool
+OptionParser::addBoolOption(char shortflag, const char* longflag, const char* help)
+{
+ if (!options.reserve(options.length() + 1))
+ return false;
+ BoolOption* bo = js_new<BoolOption>(shortflag, longflag, help);
+ if (!bo)
+ return false;
+ options.infallibleAppend(bo);
+ return true;
+}
+
+bool
+OptionParser::addStringOption(char shortflag, const char* longflag, const char* metavar,
+ const char* help)
+{
+ if (!options.reserve(options.length() + 1))
+ return false;
+ StringOption* so = js_new<StringOption>(shortflag, longflag, help, metavar);
+ if (!so)
+ return false;
+ options.infallibleAppend(so);
+ return true;
+}
+
+bool
+OptionParser::addMultiStringOption(char shortflag, const char* longflag, const char* metavar,
+ const char* help)
+{
+ if (!options.reserve(options.length() + 1))
+ return false;
+ MultiStringOption* mso = js_new<MultiStringOption>(shortflag, longflag, help, metavar);
+ if (!mso)
+ return false;
+ options.infallibleAppend(mso);
+ return true;
+}
+
+/* Argument builders */
+
+bool
+OptionParser::addOptionalStringArg(const char* name, const char* help)
+{
+ if (!arguments.reserve(arguments.length() + 1))
+ return false;
+ StringOption* so = js_new<StringOption>(1, name, help, (const char*) nullptr);
+ if (!so)
+ return false;
+ arguments.infallibleAppend(so);
+ return true;
+}
+
+bool
+OptionParser::addOptionalMultiStringArg(const char* name, const char* help)
+{
+ MOZ_ASSERT_IF(!arguments.empty(), !arguments.back()->isVariadic());
+ if (!arguments.reserve(arguments.length() + 1))
+ return false;
+ MultiStringOption* mso = js_new<MultiStringOption>(1, name, help, (const char*) nullptr);
+ if (!mso)
+ return false;
+ arguments.infallibleAppend(mso);
+ return true;
+}