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 <stdio.h>
11
12 #include "js/AllocPolicy.h"
13 #include "js/Vector.h"
14
15 namespace js {
16 namespace cli {
17
18 namespace detail {
19
20 struct BoolOption;
21 struct MultiStringOption;
22 struct ValuedOption;
23 struct StringOption;
24 struct IntOption;
25
26 enum OptionKind {
27 OptionKindBool,
28 OptionKindString,
29 OptionKindInt,
30 OptionKindMultiString,
31 OptionKindInvalid
32 };
33
34 struct Option {
35 const char* longflag;
36 const char* help;
37 OptionKind kind;
38 char shortflag;
39 bool terminatesOptions;
40
OptionOption41 Option(OptionKind kind, char shortflag, const char* longflag,
42 const char* help)
43 : longflag(longflag),
44 help(help),
45 kind(kind),
46 shortflag(shortflag),
47 terminatesOptions(false) {}
48
49 virtual ~Option() = 0;
50
setTerminatesOptionsOption51 void setTerminatesOptions(bool enabled) { terminatesOptions = enabled; }
getTerminatesOptionsOption52 bool getTerminatesOptions() const { return terminatesOptions; }
53
isValuedOption54 virtual bool isValued() const { return false; }
55
56 /* Only some valued options are variadic (like MultiStringOptions). */
isVariadicOption57 virtual bool isVariadic() const { return false; }
58
59 /*
60 * For arguments, the shortflag field is used to indicate whether the
61 * argument is optional.
62 */
isOptionalOption63 bool isOptional() { return shortflag; }
64
setFlagInfoOption65 void setFlagInfo(char shortflag, const char* longflag, const char* help) {
66 this->shortflag = shortflag;
67 this->longflag = longflag;
68 this->help = help;
69 }
70
71 ValuedOption* asValued();
72 const ValuedOption* asValued() const;
73
74 #define OPTION_CONVERT_DECL(__cls) \
75 bool is##__cls##Option() const; \
76 __cls##Option* as##__cls##Option(); \
77 const __cls##Option* as##__cls##Option() const;
78
79 OPTION_CONVERT_DECL(Bool)
80 OPTION_CONVERT_DECL(String)
81 OPTION_CONVERT_DECL(Int)
82 OPTION_CONVERT_DECL(MultiString)
83 };
84
~Option()85 inline Option::~Option() {}
86
87 struct BoolOption : public Option {
88 size_t argno;
89 bool value;
90
BoolOptionBoolOption91 BoolOption(char shortflag, const char* longflag, const char* help)
92 : Option(OptionKindBool, shortflag, longflag, help), value(false) {}
93
~BoolOptionBoolOption94 virtual ~BoolOption() {}
95 };
96
97 struct ValuedOption : public Option {
98 const char* metavar;
99
ValuedOptionValuedOption100 ValuedOption(OptionKind kind, char shortflag, const char* longflag,
101 const char* help, const char* metavar)
102 : Option(kind, shortflag, longflag, help), metavar(metavar) {}
103
104 virtual ~ValuedOption() = 0;
isValuedValuedOption105 virtual bool isValued() const override { return true; }
106 };
107
~ValuedOption()108 inline ValuedOption::~ValuedOption() {}
109
110 struct StringOption : public ValuedOption {
111 const char* value;
112
StringOptionStringOption113 StringOption(char shortflag, const char* longflag, const char* help,
114 const char* metavar)
115 : ValuedOption(OptionKindString, shortflag, longflag, help, metavar),
116 value(nullptr) {}
117
~StringOptionStringOption118 virtual ~StringOption() {}
119 };
120
121 struct IntOption : public ValuedOption {
122 int value;
123
IntOptionIntOption124 IntOption(char shortflag, const char* longflag, const char* help,
125 const char* metavar, int defaultValue)
126 : ValuedOption(OptionKindInt, shortflag, longflag, help, metavar),
127 value(defaultValue) {}
128
~IntOptionIntOption129 virtual ~IntOption() {}
130 };
131
132 struct StringArg {
133 char* value;
134 size_t argno;
135
StringArgStringArg136 StringArg(char* value, size_t argno) : value(value), argno(argno) {}
137 };
138
139 struct MultiStringOption : public ValuedOption {
140 Vector<StringArg, 0, SystemAllocPolicy> strings;
141
MultiStringOptionMultiStringOption142 MultiStringOption(char shortflag, const char* longflag, const char* help,
143 const char* metavar)
144 : ValuedOption(OptionKindMultiString, shortflag, longflag, help,
145 metavar) {}
146
~MultiStringOptionMultiStringOption147 virtual ~MultiStringOption() {}
148
isVariadicMultiStringOption149 virtual bool isVariadic() const override { return true; }
150 };
151
152 } /* namespace detail */
153
154 class MultiStringRange {
155 typedef detail::StringArg StringArg;
156 const StringArg* cur;
157 const StringArg* end;
158
159 public:
MultiStringRange(const StringArg * cur,const StringArg * end)160 explicit MultiStringRange(const StringArg* cur, const StringArg* end)
161 : cur(cur), end(end) {
162 MOZ_ASSERT(end - cur >= 0);
163 }
164
empty()165 bool empty() const { return cur == end; }
popFront()166 void popFront() {
167 MOZ_ASSERT(!empty());
168 ++cur;
169 }
front()170 char* front() const {
171 MOZ_ASSERT(!empty());
172 return cur->value;
173 }
argno()174 size_t argno() const {
175 MOZ_ASSERT(!empty());
176 return cur->argno;
177 }
178 };
179
180 /*
181 * Builder for describing a command line interface and parsing the resulting
182 * specification.
183 *
184 * - A multi-option is an option that can appear multiple times and still
185 * parse as valid command line arguments.
186 * - An "optional argument" is supported for backwards compatibility with prior
187 * command line interface usage. Once one optional argument has been added,
188 * *only* optional arguments may be added.
189 */
190 class OptionParser {
191 public:
192 enum Result {
193 Okay = 0,
194 Fail, /* As in, allocation fail. */
195 ParseError, /* Successfully parsed but with an error. */
196 EarlyExit /* Successfully parsed but exits the program,
197 * for example with --help and --version. */
198 };
199
200 private:
201 typedef Vector<detail::Option*, 0, SystemAllocPolicy> Options;
202 typedef detail::Option Option;
203 typedef detail::BoolOption BoolOption;
204
205 Options options;
206 Options arguments;
207 BoolOption helpOption;
208 BoolOption versionOption;
209 const char* usage;
210 const char* version;
211 const char* descr;
212 size_t descrWidth;
213 size_t helpWidth;
214 size_t nextArgument;
215
216 // If '--' is passed, all remaining arguments should be interpreted as the
217 // argument at index 'restArgument'. Defaults to the next unassigned
218 // argument.
219 int restArgument;
220
221 static const char prognameMeta[];
222
223 Option* findOption(char shortflag);
224 const Option* findOption(char shortflag) const;
225 Option* findOption(const char* longflag);
226 const Option* findOption(const char* longflag) const;
227 int findArgumentIndex(const char* name) const;
228 Option* findArgument(const char* name);
229 const Option* findArgument(const char* name) const;
230
231 Result error(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
232 Result extractValue(size_t argc, char** argv, size_t* i, char** value);
233 Result handleArg(size_t argc, char** argv, size_t* i, bool* optsAllowed);
234 Result handleOption(Option* opt, size_t argc, char** argv, size_t* i,
235 bool* optsAllowed);
236
237 public:
OptionParser(const char * usage)238 explicit OptionParser(const char* usage)
239 : helpOption('h', "help", "Display help information"),
240 versionOption('v', "version", "Display version information and exit"),
241 usage(usage),
242 version(nullptr),
243 descr(nullptr),
244 descrWidth(80),
245 helpWidth(80),
246 nextArgument(0),
247 restArgument(-1) {}
248
249 ~OptionParser();
250
251 Result parseArgs(int argc, char** argv);
252 Result printHelp(const char* progname);
253 Result printVersion();
254
255 /* Metadata */
256
setVersion(const char * v)257 void setVersion(const char* v) { version = v; }
setHelpWidth(size_t width)258 void setHelpWidth(size_t width) { helpWidth = width; }
setDescriptionWidth(size_t width)259 void setDescriptionWidth(size_t width) { descrWidth = width; }
setDescription(const char * description)260 void setDescription(const char* description) { descr = description; }
261 void setHelpOption(char shortflag, const char* longflag, const char* help);
262 void setArgTerminatesOptions(const char* name, bool enabled);
263 void setArgCapturesRest(const char* name);
264
265 /* Arguments: no further arguments may be added after a variadic argument. */
266
267 bool addOptionalStringArg(const char* name, const char* help);
268 bool addOptionalMultiStringArg(const char* name, const char* help);
269
270 const char* getStringArg(const char* name) const;
271 MultiStringRange getMultiStringArg(const char* name) const;
272
273 /* Options */
274
275 bool addBoolOption(char shortflag, const char* longflag, const char* help);
276 bool addStringOption(char shortflag, const char* longflag, const char* help,
277 const char* metavar);
278 bool addIntOption(char shortflag, const char* longflag, const char* help,
279 const char* metavar, int defaultValue);
280 bool addMultiStringOption(char shortflag, const char* longflag,
281 const char* help, const char* metavar);
282 bool addOptionalVariadicArg(const char* name);
283
284 int getIntOption(char shortflag) const;
285 int getIntOption(const char* longflag) const;
286 const char* getStringOption(char shortflag) const;
287 const char* getStringOption(const char* longflag) const;
288 bool getBoolOption(char shortflag) const;
289 bool getBoolOption(const char* longflag) const;
290 MultiStringRange getMultiStringOption(char shortflag) const;
291 MultiStringRange getMultiStringOption(const char* longflag) const;
292
293 /*
294 * Return whether the help option was present (and thus help was already
295 * displayed during parse_args).
296 */
297 bool getHelpOption() const;
298 };
299
300 } /* namespace cli */
301 } /* namespace js */
302
303 #endif /* shell_jsoptparse_h */
304