tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

jsoptparse.h (10174B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef shell_jsoptparse_h
      8 #define shell_jsoptparse_h
      9 
     10 #include "js/AllocPolicy.h"
     11 #include "js/Utility.h"
     12 #include "js/Vector.h"
     13 
     14 namespace js {
     15 namespace cli {
     16 
     17 namespace detail {
     18 
     19 // We want to use the shell's option parser before initializing the JS engine.
     20 // The JS malloc arena isn't available yet at this point, so we use a custom
     21 // allocation policy that uses the system malloc instead.
     22 class OptionAllocPolicy {
     23 public:
     24  template <typename T>
     25  T* pod_malloc(size_t numElems) {
     26    size_t bytes;
     27    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
     28      return nullptr;
     29    }
     30    return static_cast<T*>(malloc(bytes));
     31  }
     32 
     33  template <typename T>
     34  T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
     35    size_t bytes;
     36    if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) {
     37      return nullptr;
     38    }
     39    return static_cast<T*>(realloc(p, bytes));
     40  }
     41 
     42  void reportAllocOverflow() const {}
     43  bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); }
     44 
     45  template <typename T>
     46  void free_(T* p, size_t numElems = 0) {
     47    free(p);
     48  }
     49 };
     50 
     51 struct BoolOption;
     52 struct MultiStringOption;
     53 struct ValuedOption;
     54 struct StringOption;
     55 struct IntOption;
     56 
     57 enum OptionKind {
     58  OptionKindBool,
     59  OptionKindString,
     60  OptionKindInt,
     61  OptionKindMultiString,
     62  OptionKindInvalid
     63 };
     64 
     65 struct Option {
     66  const char* longflag;
     67  const char* help;
     68  OptionKind kind;
     69  char shortflag;
     70  bool terminatesOptions = false;
     71  bool ignoresUnknownOptions = false;
     72 
     73  Option(OptionKind kind, char shortflag, const char* longflag,
     74         const char* help)
     75      : longflag(longflag), help(help), kind(kind), shortflag(shortflag) {}
     76 
     77  virtual ~Option() = 0;
     78 
     79  void setTerminatesOptions(bool enabled) { terminatesOptions = enabled; }
     80  bool getTerminatesOptions() const { return terminatesOptions; }
     81 
     82  void setIgnoresUnknownOptions(bool enabled) {
     83    ignoresUnknownOptions = enabled;
     84  }
     85  bool getIgnoresUnknownOptions() const { return ignoresUnknownOptions; }
     86 
     87  virtual bool isValued() const { return false; }
     88 
     89  /* Only some valued options are variadic (like MultiStringOptions). */
     90  virtual bool isVariadic() const { return false; }
     91 
     92  /*
     93   * For arguments, the shortflag field is used to indicate whether the
     94   * argument is optional.
     95   */
     96  bool isOptional() { return shortflag; }
     97 
     98  void setFlagInfo(char shortflag, const char* longflag, const char* help) {
     99    this->shortflag = shortflag;
    100    this->longflag = longflag;
    101    this->help = help;
    102  }
    103 
    104  ValuedOption* asValued();
    105  const ValuedOption* asValued() const;
    106 
    107 #define OPTION_CONVERT_DECL(__cls)    \
    108  bool is##__cls##Option() const;     \
    109  __cls##Option* as##__cls##Option(); \
    110  const __cls##Option* as##__cls##Option() const;
    111 
    112  OPTION_CONVERT_DECL(Bool)
    113  OPTION_CONVERT_DECL(String)
    114  OPTION_CONVERT_DECL(Int)
    115  OPTION_CONVERT_DECL(MultiString)
    116 };
    117 
    118 inline Option::~Option() {}
    119 
    120 struct BoolOption : public Option {
    121  size_t argno;
    122  bool value;
    123 
    124  BoolOption(char shortflag, const char* longflag, const char* help)
    125      : Option(OptionKindBool, shortflag, longflag, help), value(false) {}
    126 
    127  virtual ~BoolOption() {}
    128 };
    129 
    130 struct ValuedOption : public Option {
    131  const char* metavar;
    132 
    133  ValuedOption(OptionKind kind, char shortflag, const char* longflag,
    134               const char* help, const char* metavar)
    135      : Option(kind, shortflag, longflag, help), metavar(metavar) {}
    136 
    137  virtual ~ValuedOption() = 0;
    138  virtual bool isValued() const override { return true; }
    139 };
    140 
    141 inline ValuedOption::~ValuedOption() {}
    142 
    143 struct StringOption : public ValuedOption {
    144  const char* value;
    145 
    146  StringOption(char shortflag, const char* longflag, const char* help,
    147               const char* metavar)
    148      : ValuedOption(OptionKindString, shortflag, longflag, help, metavar),
    149        value(nullptr) {}
    150 
    151  virtual ~StringOption() {}
    152 };
    153 
    154 struct IntOption : public ValuedOption {
    155  int value;
    156 
    157  IntOption(char shortflag, const char* longflag, const char* help,
    158            const char* metavar, int defaultValue)
    159      : ValuedOption(OptionKindInt, shortflag, longflag, help, metavar),
    160        value(defaultValue) {}
    161 
    162  virtual ~IntOption() {}
    163 };
    164 
    165 struct StringArg {
    166  char* value;
    167  size_t argno;
    168 
    169  StringArg(char* value, size_t argno) : value(value), argno(argno) {}
    170 };
    171 
    172 struct MultiStringOption : public ValuedOption {
    173  Vector<StringArg, 0, detail::OptionAllocPolicy> strings;
    174 
    175  MultiStringOption(char shortflag, const char* longflag, const char* help,
    176                    const char* metavar)
    177      : ValuedOption(OptionKindMultiString, shortflag, longflag, help,
    178                     metavar) {}
    179 
    180  virtual ~MultiStringOption() {}
    181 
    182  virtual bool isVariadic() const override { return true; }
    183 };
    184 
    185 } /* namespace detail */
    186 
    187 class MultiStringRange {
    188  using StringArg = detail::StringArg;
    189  const StringArg* cur;
    190  const StringArg* end;
    191 
    192 public:
    193  explicit MultiStringRange(const StringArg* cur, const StringArg* end)
    194      : cur(cur), end(end) {
    195    MOZ_ASSERT(end - cur >= 0);
    196  }
    197 
    198  bool empty() const { return cur == end; }
    199  void popFront() {
    200    MOZ_ASSERT(!empty());
    201    ++cur;
    202  }
    203  char* front() const {
    204    MOZ_ASSERT(!empty());
    205    return cur->value;
    206  }
    207  size_t argno() const {
    208    MOZ_ASSERT(!empty());
    209    return cur->argno;
    210  }
    211 };
    212 
    213 /*
    214 * Builder for describing a command line interface and parsing the resulting
    215 * specification.
    216 *
    217 * - A multi-option is an option that can appear multiple times and still
    218 *   parse as valid command line arguments.
    219 * - An "optional argument" is supported for backwards compatibility with prior
    220 *   command line interface usage. Once one optional argument has been added,
    221 *   *only* optional arguments may be added.
    222 */
    223 class OptionParser {
    224 public:
    225  enum Result {
    226    Okay = 0,
    227    Fail,       /* As in, allocation fail. */
    228    ParseError, /* Successfully parsed but with an error. */
    229    EarlyExit   /* Successfully parsed but exits the program,
    230                 * for example with --help and --version. */
    231  };
    232 
    233 private:
    234  using Options = Vector<detail::Option*, 0, detail::OptionAllocPolicy>;
    235  using Option = detail::Option;
    236  using BoolOption = detail::BoolOption;
    237 
    238  Options options;
    239  Options arguments;
    240  BoolOption helpOption;
    241  BoolOption versionOption;
    242  const char* usage;
    243  const char* version;
    244  const char* descr;
    245  size_t descrWidth;
    246  size_t helpWidth;
    247  size_t nextArgument;
    248 
    249  // If '--' is passed, all remaining arguments should be interpreted as the
    250  // argument at index 'restArgument'. Defaults to the next unassigned
    251  // argument.
    252  int restArgument;
    253 
    254  Option* findOption(char shortflag);
    255  const Option* findOption(char shortflag) const;
    256  const Option* tryFindOption(char shortflag) const;
    257  Option* findOption(const char* longflag);
    258  const Option* findOption(const char* longflag) const;
    259  const Option* tryFindOption(const char* longflag) const;
    260  int findArgumentIndex(const char* name) const;
    261  Option* findArgument(const char* name);
    262  const Option* findArgument(const char* name) const;
    263 
    264  Result error(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
    265  Result extractValue(size_t argc, char** argv, size_t* i, char** value);
    266  Result handleArg(size_t argc, char** argv, size_t* i, bool* optsAllowed);
    267  Result handleOption(Option* opt, size_t argc, char** argv, size_t* i,
    268                      bool* optsAllowed);
    269 
    270 public:
    271  explicit OptionParser(const char* usage)
    272      : helpOption('h', "help", "Display help information"),
    273        versionOption('v', "version", "Display version information and exit"),
    274        usage(usage),
    275        version(nullptr),
    276        descr(nullptr),
    277        descrWidth(80),
    278        helpWidth(80),
    279        nextArgument(0),
    280        restArgument(-1) {}
    281 
    282  ~OptionParser();
    283 
    284  Result parseArgs(int argc, char** argv);
    285  Result printHelp(const char* progname);
    286  Result printVersion();
    287 
    288  /* Metadata */
    289 
    290  void setVersion(const char* v) { version = v; }
    291  void setHelpWidth(size_t width) { helpWidth = width; }
    292  void setDescriptionWidth(size_t width) { descrWidth = width; }
    293  void setDescription(const char* description) { descr = description; }
    294  void setHelpOption(char shortflag, const char* longflag, const char* help);
    295  void setArgTerminatesOptions(const char* name, bool enabled);
    296  void setIgnoresUnknownOptions(const char* name, bool enabled);
    297  void setArgCapturesRest(const char* name);
    298 
    299  /* Arguments: no further arguments may be added after a variadic argument. */
    300 
    301  bool addOptionalStringArg(const char* name, const char* help);
    302  bool addOptionalMultiStringArg(const char* name, const char* help);
    303 
    304  const char* getStringArg(const char* name) const;
    305  MultiStringRange getMultiStringArg(const char* name) const;
    306 
    307  /* Options */
    308 
    309  bool addBoolOption(char shortflag, const char* longflag, const char* help);
    310  bool addStringOption(char shortflag, const char* longflag, const char* help,
    311                       const char* metavar);
    312  bool addIntOption(char shortflag, const char* longflag, const char* help,
    313                    const char* metavar, int defaultValue);
    314  bool addMultiStringOption(char shortflag, const char* longflag,
    315                            const char* help, const char* metavar);
    316  bool addOptionalVariadicArg(const char* name);
    317 
    318  int getIntOption(char shortflag) const;
    319  int getIntOption(const char* longflag) const;
    320  const char* getStringOption(char shortflag) const;
    321  const char* getStringOption(const char* longflag) const;
    322  bool getBoolOption(char shortflag) const;
    323  bool getBoolOption(const char* longflag) const;
    324  MultiStringRange getMultiStringOption(char shortflag) const;
    325  MultiStringRange getMultiStringOption(const char* longflag) const;
    326 
    327  /*
    328   * Return whether the help option was present (and thus help was already
    329   * displayed during parse_args).
    330   */
    331  bool getHelpOption() const;
    332 };
    333 
    334 } /* namespace cli */
    335 } /* namespace js */
    336 
    337 #endif /* shell_jsoptparse_h */