• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Provides a simple class, |CommandLine|, for dealing with command lines (and
6 // flags and positional arguments).
7 //
8 // * Options (a.k.a. flags or switches) are all of the form "--name=<value>" (or
9 //   "--name", but this is indistinguishable from "--name="), where <value> is a
10 //   string. Not supported: "-name", "-n", "--name <value>", "-n <value>", etc.
11 // * Option order is preserved.
12 // * Option processing is stopped after the first positional argument[*]. Thus
13 //   in the command line "my_program --foo bar --baz", only "--foo" is an option
14 //   ("bar" and "--baz" are positional arguments).
15 // * Options can be looked up by name. If the same option occurs multiple times,
16 //   convention is to use the last occurrence (and the provided look-up
17 //   functions behave this way).
18 // * "--" may also be used to separate options from positional arguments. Thus
19 //   in the command line "my_program --foo -- --bar", "--bar" is a positional
20 //   argument.
21 // * |CommandLine|s store |argv[0]| and distinguish between not having |argv[0]|
22 //   and |argv[0]| being empty.
23 // * Apart from being copyable and movable, |CommandLine|s are immutable.
24 //
25 // There are factory functions to turn raw arguments into |CommandLine|s, in
26 // accordance with the above rules. However, |CommandLine|s may be used more
27 // generically (with the user transforming arguments using different rules,
28 // e.g., accepting "-name" as an option), subject to certain limitations (e.g.,
29 // not being able to distinguish "no value" from "empty value").
30 //
31 // [*] This is somewhat annoying for users, but: a. it's standard Unix behavior
32 // for most command line parsers, b. it makes "my_program *" (etc.) safer (which
33 // mostly explains a.), c. it makes parsing "subcommands", like "my_program
34 // --flag_for_my_program subcommand --flag_for_subcommand" saner.
35 
36 #ifndef LIB_FML_COMMAND_LINE_H_
37 #define LIB_FML_COMMAND_LINE_H_
38 
39 #include <stddef.h>
40 
41 #include <initializer_list>
42 #include <string>
43 #include <string_view>
44 #include <unordered_map>
45 #include <vector>
46 
47 #include "flutter/fml/macros.h"
48 
49 namespace fml {
50 
51 // CommandLine -----------------------------------------------------------------
52 
53 // Class that stores processed command lines ("argv[0]", options, and positional
54 // arguments) and provides access to them. For more details, see the file-level
55 // comment above. This class is thread-safe.
56 class CommandLine final {
57  private:
58   class ConstructionHelper;
59 
60  public:
61   struct Option {
OptionOption62     Option() {}
63     explicit Option(const std::string& name);
64     Option(const std::string& name, const std::string& value);
65 
66     bool operator==(const Option& other) const {
67       return name == other.name && value == other.value;
68     }
69     bool operator!=(const Option& other) const { return !operator==(other); }
70 
71     std::string name;
72     std::string value;
73   };
74 
75   // Default, copy, and move constructors (to be out-of-lined).
76   CommandLine();
77   CommandLine(const CommandLine& from);
78   CommandLine(CommandLine&& from);
79 
80   // Constructs a |CommandLine| from its "components". This is especially useful
81   // for creating a new |CommandLine| based on an existing |CommandLine| (e.g.,
82   // adding options or arguments).
83   explicit CommandLine(const std::string& argv0,
84                        const std::vector<Option>& options,
85                        const std::vector<std::string>& positional_args);
86 
87   ~CommandLine();
88 
89   // Copy and move assignment (to be out-of-lined).
90   CommandLine& operator=(const CommandLine& from);
91   CommandLine& operator=(CommandLine&& from);
92 
has_argv0()93   bool has_argv0() const { return has_argv0_; }
argv0()94   const std::string& argv0() const { return argv0_; }
options()95   const std::vector<Option>& options() const { return options_; }
positional_args()96   const std::vector<std::string>& positional_args() const {
97     return positional_args_;
98   }
99 
100   bool operator==(const CommandLine& other) const {
101     // No need to compare |option_index_|.
102     return has_argv0_ == other.has_argv0_ && argv0_ == other.argv0_ &&
103            options_ == other.options_ &&
104            positional_args_ == other.positional_args_;
105   }
106   bool operator!=(const CommandLine& other) const { return !operator==(other); }
107 
108   // Returns true if this command line has the option |name| (and if |index| is
109   // non-null, sets |*index| to the index of the *last* occurrence of the given
110   // option in |options()|) and false if not.
111   bool HasOption(std::string_view name, size_t* index = nullptr) const;
112 
113   // Gets the value of the option |name|. Returns true (and sets |*value|) on
114   // success and false (leaving |*value| alone) on failure.
115   bool GetOptionValue(std::string_view name, std::string* value) const;
116 
117   // Gets all values of the option |name|. Returns all values, which may be
118   // empty if the option is not specified.
119   std::vector<std::string_view> GetOptionValues(std::string_view name) const;
120 
121   // Gets the value of the option |name|, with a default if the option is not
122   // specified. (Note: This doesn't return a const reference, since this would
123   // make the |default_value| argument inconvenient/dangerous.)
124   std::string GetOptionValueWithDefault(std::string_view name,
125                                         std::string_view default_value) const;
126 
127  private:
128   bool has_argv0_ = false;
129   // The following should all be empty if |has_argv0_| is false.
130   std::string argv0_;
131   std::vector<Option> options_;
132   std::vector<std::string> positional_args_;
133 
134   // Maps option names to position in |options_|. If a given name occurs
135   // multiple times, the index will be to the *last* occurrence.
136   std::unordered_map<std::string, size_t> option_index_;
137 
138   // Allow copy and assignment.
139 };
140 
141 // Factory functions (etc.) ----------------------------------------------------
142 
143 namespace internal {
144 
145 // Helper class for building command lines (finding options, etc.) from raw
146 // arguments.
147 class CommandLineBuilder final {
148  public:
149   CommandLineBuilder();
150   ~CommandLineBuilder();
151 
152   // Processes an additional argument in the command line. Returns true if |arg|
153   // is the *first* positional argument.
154   bool ProcessArg(const std::string& arg);
155 
156   // Builds a |CommandLine| from the arguments processed so far.
157   CommandLine Build() const;
158 
159  private:
160   bool has_argv0_ = false;
161   std::string argv0_;
162   std::vector<CommandLine::Option> options_;
163   std::vector<std::string> positional_args_;
164 
165   // True if we've started processing positional arguments.
166   bool started_positional_args_ = false;
167 
168   FML_DISALLOW_COPY_AND_ASSIGN(CommandLineBuilder);
169 };
170 
171 }  // namespace internal
172 
173 // The following factory functions create |CommandLine|s from raw arguments in
174 // accordance with the rules outlined at the top of this file. (Other ways of
175 // transforming raw arguments into options and positional arguments are
176 // possible.)
177 
178 // Like |CommandLineFromIterators()| (see below), but sets
179 // |*first_positional_arg| to point to the first positional argument seen (or
180 // |last| if none are seen). This is useful for processing "subcommands".
181 template <typename InputIterator>
CommandLineFromIteratorsFindFirstPositionalArg(InputIterator first,InputIterator last,InputIterator * first_positional_arg)182 inline CommandLine CommandLineFromIteratorsFindFirstPositionalArg(
183     InputIterator first,
184     InputIterator last,
185     InputIterator* first_positional_arg) {
186   if (first_positional_arg)
187     *first_positional_arg = last;
188   internal::CommandLineBuilder builder;
189   for (auto it = first; it < last; ++it) {
190     if (builder.ProcessArg(*it)) {
191       if (first_positional_arg)
192         *first_positional_arg = it;
193     }
194   }
195   return builder.Build();
196 }
197 
198 // Builds a |CommandLine| from first/last iterators (where |last| is really
199 // one-past-the-last, as usual) to |std::string|s or things that implicitly
200 // convert to |std::string|.
201 template <typename InputIterator>
CommandLineFromIterators(InputIterator first,InputIterator last)202 inline CommandLine CommandLineFromIterators(InputIterator first,
203                                             InputIterator last) {
204   return CommandLineFromIteratorsFindFirstPositionalArg<InputIterator>(
205       first, last, nullptr);
206 }
207 
208 // Builds a |CommandLine| from first/last iterators (where |last| is really
209 // one-past-the-last, as usual) to |std::string|s or things that implicitly
210 // convert to |std::string|, where argv[0] is provided separately.
211 template <typename InputIterator>
CommandLineFromIteratorsWithArgv0(const std::string & argv0,InputIterator first,InputIterator last)212 inline CommandLine CommandLineFromIteratorsWithArgv0(const std::string& argv0,
213                                                      InputIterator first,
214                                                      InputIterator last) {
215   internal::CommandLineBuilder builder;
216   builder.ProcessArg(argv0);
217   for (auto it = first; it < last; ++it)
218     builder.ProcessArg(*it);
219   return builder.Build();
220 }
221 
222 // Builds a |CommandLine| from the usual argc/argv.
CommandLineFromArgcArgv(int argc,const char * const * argv)223 inline CommandLine CommandLineFromArgcArgv(int argc, const char* const* argv) {
224   return CommandLineFromIterators(argv, argv + argc);
225 }
226 
227 // Builds a |CommandLine| from an initializer list of |std::string|s or things
228 // that implicitly convert to |std::string|.
229 template <typename StringType>
CommandLineFromInitializerList(std::initializer_list<StringType> argv)230 inline CommandLine CommandLineFromInitializerList(
231     std::initializer_list<StringType> argv) {
232   return CommandLineFromIterators(argv.begin(), argv.end());
233 }
234 
235 // This is the "opposite" of the above factory functions, transforming a
236 // |CommandLine| into a vector of argument strings according to the rules
237 // outlined at the top of this file.
238 std::vector<std::string> CommandLineToArgv(const CommandLine& command_line);
239 
240 }  // namespace fml
241 
242 #endif  // LIB_FML_COMMAND_LINE_H_
243