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