|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
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 "jsalloc.h" |
|
13 #include "jsutil.h" |
|
14 |
|
15 #include "js/Vector.h" |
|
16 |
|
17 namespace js { |
|
18 namespace cli { |
|
19 |
|
20 namespace detail { |
|
21 |
|
22 struct BoolOption; |
|
23 struct MultiStringOption; |
|
24 struct ValuedOption; |
|
25 struct StringOption; |
|
26 struct IntOption; |
|
27 |
|
28 enum OptionKind |
|
29 { |
|
30 OptionKindBool, |
|
31 OptionKindString, |
|
32 OptionKindInt, |
|
33 OptionKindMultiString, |
|
34 OptionKindInvalid |
|
35 }; |
|
36 |
|
37 struct Option |
|
38 { |
|
39 const char *longflag; |
|
40 const char *help; |
|
41 OptionKind kind; |
|
42 char shortflag; |
|
43 bool terminatesOptions; |
|
44 |
|
45 Option(OptionKind kind, char shortflag, const char *longflag, const char *help) |
|
46 : longflag(longflag), help(help), kind(kind), shortflag(shortflag), terminatesOptions(false) |
|
47 {} |
|
48 |
|
49 virtual ~Option() = 0; |
|
50 |
|
51 void setTerminatesOptions(bool enabled) { terminatesOptions = enabled; } |
|
52 bool getTerminatesOptions() const { return terminatesOptions; } |
|
53 |
|
54 virtual bool isValued() const { return false; } |
|
55 |
|
56 /* Only some valued options are variadic (like MultiStringOptions). */ |
|
57 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 */ |
|
63 bool isOptional() { return shortflag; } |
|
64 |
|
65 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 |
|
85 inline Option::~Option() {} |
|
86 |
|
87 struct BoolOption : public Option |
|
88 { |
|
89 size_t argno; |
|
90 bool value; |
|
91 |
|
92 BoolOption(char shortflag, const char *longflag, const char *help) |
|
93 : Option(OptionKindBool, shortflag, longflag, help), value(false) |
|
94 {} |
|
95 |
|
96 virtual ~BoolOption() {} |
|
97 }; |
|
98 |
|
99 struct ValuedOption : public Option |
|
100 { |
|
101 const char *metavar; |
|
102 |
|
103 ValuedOption(OptionKind kind, char shortflag, const char *longflag, const char *help, |
|
104 const char *metavar) |
|
105 : Option(kind, shortflag, longflag, help), metavar(metavar) |
|
106 {} |
|
107 |
|
108 virtual ~ValuedOption() = 0; |
|
109 virtual bool isValued() const { return true; } |
|
110 }; |
|
111 |
|
112 inline ValuedOption::~ValuedOption() {} |
|
113 |
|
114 struct StringOption : public ValuedOption |
|
115 { |
|
116 const char *value; |
|
117 |
|
118 StringOption(char shortflag, const char *longflag, const char *help, const char *metavar) |
|
119 : ValuedOption(OptionKindString, shortflag, longflag, help, metavar), value(nullptr) |
|
120 {} |
|
121 |
|
122 virtual ~StringOption() {} |
|
123 }; |
|
124 |
|
125 struct IntOption : public ValuedOption |
|
126 { |
|
127 int value; |
|
128 |
|
129 IntOption(char shortflag, const char *longflag, const char *help, const char *metavar, |
|
130 int defaultValue) |
|
131 : ValuedOption(OptionKindInt, shortflag, longflag, help, metavar), value(defaultValue) |
|
132 {} |
|
133 |
|
134 virtual ~IntOption() {} |
|
135 }; |
|
136 |
|
137 struct StringArg |
|
138 { |
|
139 char *value; |
|
140 size_t argno; |
|
141 |
|
142 StringArg(char *value, size_t argno) : value(value), argno(argno) {} |
|
143 }; |
|
144 |
|
145 struct MultiStringOption : public ValuedOption |
|
146 { |
|
147 Vector<StringArg, 0, SystemAllocPolicy> strings; |
|
148 |
|
149 MultiStringOption(char shortflag, const char *longflag, const char *help, const char *metavar) |
|
150 : ValuedOption(OptionKindMultiString, shortflag, longflag, help, metavar) |
|
151 {} |
|
152 |
|
153 virtual ~MultiStringOption() {} |
|
154 |
|
155 virtual bool isVariadic() const { return true; } |
|
156 }; |
|
157 |
|
158 } /* namespace detail */ |
|
159 |
|
160 class MultiStringRange |
|
161 { |
|
162 typedef detail::StringArg StringArg; |
|
163 const StringArg *cur; |
|
164 const StringArg *end; |
|
165 |
|
166 public: |
|
167 explicit MultiStringRange(const StringArg *cur, const StringArg *end) |
|
168 : cur(cur), end(end) { |
|
169 JS_ASSERT(end - cur >= 0); |
|
170 } |
|
171 |
|
172 bool empty() const { return cur == end; } |
|
173 void popFront() { JS_ASSERT(!empty()); ++cur; } |
|
174 char *front() const { JS_ASSERT(!empty()); return cur->value; } |
|
175 size_t argno() const { JS_ASSERT(!empty()); return cur->argno; } |
|
176 }; |
|
177 |
|
178 /* |
|
179 * Builder for describing a command line interface and parsing the resulting |
|
180 * specification. |
|
181 * |
|
182 * - A multi-option is an option that can appear multiple times and still |
|
183 * parse as valid command line arguments. |
|
184 * - An "optional argument" is supported for backwards compatibility with prior |
|
185 * command line interface usage. Once one optional argument has been added, |
|
186 * *only* optional arguments may be added. |
|
187 */ |
|
188 class OptionParser |
|
189 { |
|
190 public: |
|
191 enum Result |
|
192 { |
|
193 Okay = 0, |
|
194 Fail, /* As in, allocation fail. */ |
|
195 ParseError, /* Successfully parsed but with an error. */ |
|
196 ParseHelp /* Aborted on help flag. */ |
|
197 }; |
|
198 |
|
199 private: |
|
200 typedef Vector<detail::Option *, 0, SystemAllocPolicy> Options; |
|
201 typedef detail::Option Option; |
|
202 typedef detail::BoolOption BoolOption; |
|
203 |
|
204 Options options; |
|
205 Options arguments; |
|
206 BoolOption helpOption; |
|
207 const char *usage; |
|
208 const char *ver; |
|
209 const char *descr; |
|
210 size_t descrWidth; |
|
211 size_t helpWidth; |
|
212 size_t nextArgument; |
|
213 |
|
214 // If '--' is passed, all remaining arguments should be interpreted as the |
|
215 // argument at index 'restArgument'. Defaults to the next unassigned |
|
216 // argument. |
|
217 int restArgument; |
|
218 |
|
219 static const char prognameMeta[]; |
|
220 |
|
221 Option *findOption(char shortflag); |
|
222 const Option *findOption(char shortflag) const; |
|
223 Option *findOption(const char *longflag); |
|
224 const Option *findOption(const char *longflag) const; |
|
225 int findArgumentIndex(const char *name) const; |
|
226 Option *findArgument(const char *name); |
|
227 const Option *findArgument(const char *name) const; |
|
228 |
|
229 Result error(const char *fmt, ...); |
|
230 Result extractValue(size_t argc, char **argv, size_t *i, char **value); |
|
231 Result handleArg(size_t argc, char **argv, size_t *i, bool *optsAllowed); |
|
232 Result handleOption(Option *opt, size_t argc, char **argv, size_t *i, bool *optsAllowed); |
|
233 |
|
234 public: |
|
235 explicit OptionParser(const char *usage) |
|
236 : helpOption('h', "help", "Display help information"), |
|
237 usage(usage), ver(nullptr), descr(nullptr), descrWidth(80), helpWidth(80), |
|
238 nextArgument(0), restArgument(-1) |
|
239 {} |
|
240 |
|
241 ~OptionParser(); |
|
242 |
|
243 Result parseArgs(int argc, char **argv); |
|
244 Result printHelp(const char *progname); |
|
245 |
|
246 /* Metadata */ |
|
247 |
|
248 void setVersion(const char *version) { ver = version; } |
|
249 void setHelpWidth(size_t width) { helpWidth = width; } |
|
250 void setDescriptionWidth(size_t width) { descrWidth = width; } |
|
251 void setDescription(const char *description) { descr = description; } |
|
252 void setHelpOption(char shortflag, const char *longflag, const char *help); |
|
253 void setArgTerminatesOptions(const char *name, bool enabled); |
|
254 void setArgCapturesRest(const char *name); |
|
255 |
|
256 /* Arguments: no further arguments may be added after a variadic argument. */ |
|
257 |
|
258 bool addOptionalStringArg(const char *name, const char *help); |
|
259 bool addOptionalMultiStringArg(const char *name, const char *help); |
|
260 |
|
261 const char *getStringArg(const char *name) const; |
|
262 MultiStringRange getMultiStringArg(const char *name) const; |
|
263 |
|
264 /* Options */ |
|
265 |
|
266 bool addBoolOption(char shortflag, const char *longflag, const char *help); |
|
267 bool addStringOption(char shortflag, const char *longflag, const char *help, |
|
268 const char *metavar); |
|
269 bool addIntOption(char shortflag, const char *longflag, const char *help, |
|
270 const char *metavar, int defaultValue); |
|
271 bool addMultiStringOption(char shortflag, const char *longflag, const char *help, |
|
272 const char *metavar); |
|
273 bool addOptionalVariadicArg(const char *name); |
|
274 |
|
275 int getIntOption(char shortflag) const; |
|
276 int getIntOption(const char *longflag) const; |
|
277 const char *getStringOption(char shortflag) const; |
|
278 const char *getStringOption(const char *longflag) const; |
|
279 bool getBoolOption(char shortflag) const; |
|
280 bool getBoolOption(const char *longflag) const; |
|
281 MultiStringRange getMultiStringOption(char shortflag) const; |
|
282 MultiStringRange getMultiStringOption(const char *longflag) const; |
|
283 |
|
284 /* |
|
285 * Return whether the help option was present (and thus help was already |
|
286 * displayed during parse_args). |
|
287 */ |
|
288 bool getHelpOption() const; |
|
289 }; |
|
290 |
|
291 } /* namespace cli */ |
|
292 } /* namespace js */ |
|
293 |
|
294 #endif /* shell_jsoptparse_h */ |