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 */