Flutter Engine
command_line.h
Go to the documentation of this file.
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 <cstddef>
40 #include <initializer_list>
41 #include <string>
42 #include <string_view>
43 #include <unordered_map>
44 #include <vector>
45 
46 #include "flutter/fml/macros.h"
47 
48 namespace fml {
49 
50 // CommandLine -----------------------------------------------------------------
51 
52 // Class that stores processed command lines ("argv[0]", options, and positional
53 // arguments) and provides access to them. For more details, see the file-level
54 // comment above. This class is thread-safe.
55 class CommandLine final {
56  private:
57  class ConstructionHelper;
58 
59  public:
60  struct Option {
61  Option() {}
62  explicit Option(const std::string& name);
63  Option(const std::string& name, const std::string& value);
64 
65  bool operator==(const Option& other) const {
66  return name == other.name && value == other.value;
67  }
68  bool operator!=(const Option& other) const { return !operator==(other); }
69 
70  std::string name;
71  std::string value;
72  };
73 
74  // Default, copy, and move constructors (to be out-of-lined).
75  CommandLine();
76  CommandLine(const CommandLine& from);
77  CommandLine(CommandLine&& from);
78 
79  // Constructs a |CommandLine| from its "components". This is especially useful
80  // for creating a new |CommandLine| based on an existing |CommandLine| (e.g.,
81  // adding options or arguments).
82  explicit CommandLine(const std::string& argv0,
83  const std::vector<Option>& options,
84  const std::vector<std::string>& positional_args);
85 
86  ~CommandLine();
87 
88  // Copy and move assignment (to be out-of-lined).
89  CommandLine& operator=(const CommandLine& from);
91 
92  bool has_argv0() const { return has_argv0_; }
93  const std::string& argv0() const { return argv0_; }
94  const std::vector<Option>& options() const { return options_; }
95  const std::vector<std::string>& positional_args() const {
96  return positional_args_;
97  }
98 
99  bool operator==(const CommandLine& other) const {
100  // No need to compare |option_index_|.
101  return has_argv0_ == other.has_argv0_ && argv0_ == other.argv0_ &&
102  options_ == other.options_ &&
103  positional_args_ == other.positional_args_;
104  }
105  bool operator!=(const CommandLine& other) const { return !operator==(other); }
106 
107  // Returns true if this command line has the option |name| (and if |index| is
108  // non-null, sets |*index| to the index of the *last* occurrence of the given
109  // option in |options()|) and false if not.
110  bool HasOption(std::string_view name, size_t* index = nullptr) const;
111 
112  // Gets the value of the option |name|. Returns true (and sets |*value|) on
113  // success and false (leaving |*value| alone) on failure.
114  bool GetOptionValue(std::string_view name, std::string* value) const;
115 
116  // Gets all values of the option |name|. Returns all values, which may be
117  // empty if the option is not specified.
118  std::vector<std::string_view> GetOptionValues(std::string_view name) const;
119 
120  // Gets the value of the option |name|, with a default if the option is not
121  // specified. (Note: This doesn't return a const reference, since this would
122  // make the |default_value| argument inconvenient/dangerous.)
123  std::string GetOptionValueWithDefault(std::string_view name,
124  std::string_view default_value) const;
125 
126  private:
127  bool has_argv0_ = false;
128  // The following should all be empty if |has_argv0_| is false.
129  std::string argv0_;
130  std::vector<Option> options_;
131  std::vector<std::string> positional_args_;
132 
133  // Maps option names to position in |options_|. If a given name occurs
134  // multiple times, the index will be to the *last* occurrence.
135  std::unordered_map<std::string, size_t> option_index_;
136 
137  // Allow copy and assignment.
138 };
139 
140 // Factory functions (etc.) ----------------------------------------------------
141 
142 namespace internal {
143 
144 // Helper class for building command lines (finding options, etc.) from raw
145 // arguments.
146 class CommandLineBuilder final {
147  public:
150 
151  // Processes an additional argument in the command line. Returns true if |arg|
152  // is the *first* positional argument.
153  bool ProcessArg(const std::string& arg);
154 
155  // Builds a |CommandLine| from the arguments processed so far.
156  CommandLine Build() const;
157 
158  private:
159  bool has_argv0_ = false;
160  std::string argv0_;
161  std::vector<CommandLine::Option> options_;
162  std::vector<std::string> positional_args_;
163 
164  // True if we've started processing positional arguments.
165  bool started_positional_args_ = false;
166 
168 };
169 
170 } // namespace internal
171 
172 // The following factory functions create |CommandLine|s from raw arguments in
173 // accordance with the rules outlined at the top of this file. (Other ways of
174 // transforming raw arguments into options and positional arguments are
175 // possible.)
176 
177 // Like |CommandLineFromIterators()| (see below), but sets
178 // |*first_positional_arg| to point to the first positional argument seen (or
179 // |last| if none are seen). This is useful for processing "subcommands".
180 template <typename InputIterator>
182  InputIterator first,
183  InputIterator last,
184  InputIterator* first_positional_arg) {
185  if (first_positional_arg)
186  *first_positional_arg = last;
188  for (auto it = first; it < last; ++it) {
189  if (builder.ProcessArg(*it)) {
190  if (first_positional_arg)
191  *first_positional_arg = it;
192  }
193  }
194  return builder.Build();
195 }
196 
197 // Builds a |CommandLine| from first/last iterators (where |last| is really
198 // one-past-the-last, as usual) to |std::string|s or things that implicitly
199 // convert to |std::string|.
200 template <typename InputIterator>
201 inline CommandLine CommandLineFromIterators(InputIterator first,
202  InputIterator last) {
203  return CommandLineFromIteratorsFindFirstPositionalArg<InputIterator>(
204  first, last, nullptr);
205 }
206 
207 // Builds a |CommandLine| from first/last iterators (where |last| is really
208 // one-past-the-last, as usual) to |std::string|s or things that implicitly
209 // convert to |std::string|, where argv[0] is provided separately.
210 template <typename InputIterator>
212  InputIterator first,
213  InputIterator last) {
215  builder.ProcessArg(argv0);
216  for (auto it = first; it < last; ++it)
217  builder.ProcessArg(*it);
218  return builder.Build();
219 }
220 
221 // Builds a |CommandLine| from the usual argc/argv.
222 inline CommandLine CommandLineFromArgcArgv(int argc, const char* const* argv) {
223  return CommandLineFromIterators(argv, argv + argc);
224 }
225 
226 // Builds a |CommandLine| from an initializer list of |std::string|s or things
227 // that implicitly convert to |std::string|.
228 template <typename StringType>
230  std::initializer_list<StringType> argv) {
231  return CommandLineFromIterators(argv.begin(), argv.end());
232 }
233 
234 // This is the "opposite" of the above factory functions, transforming a
235 // |CommandLine| into a vector of argument strings according to the rules
236 // outlined at the top of this file.
237 std::vector<std::string> CommandLineToArgv(const CommandLine& command_line);
238 
239 } // namespace fml
240 
241 #endif // LIB_FML_COMMAND_LINE_H_
bool has_argv0() const
Definition: command_line.h:92
bool operator!=(const Option &other) const
Definition: command_line.h:68
CommandLine CommandLineFromIteratorsWithArgv0(const std::string &argv0, InputIterator first, InputIterator last)
Definition: command_line.h:211
std::vector< std::string > CommandLineToArgv(const CommandLine &command_line)
CommandLine CommandLineFromArgcArgv(int argc, const char *const *argv)
Definition: command_line.h:222
bool GetOptionValue(std::string_view name, std::string *value) const
Definition: command_line.cc:51
CommandLine CommandLineFromIteratorsFindFirstPositionalArg(InputIterator first, InputIterator last, InputIterator *first_positional_arg)
Definition: command_line.h:181
bool operator!=(const CommandLine &other) const
Definition: command_line.h:105
Definition: ascii_trie.cc:9
CommandLine & operator=(const CommandLine &from)
const std::vector< Option > & options() const
Definition: command_line.h:94
const std::string & argv0() const
Definition: command_line.h:93
bool operator==(const Option &other) const
Definition: command_line.h:65
bool operator==(const CommandLine &other) const
Definition: command_line.h:99
const std::vector< std::string > & positional_args() const
Definition: command_line.h:95
std::vector< std::string_view > GetOptionValues(std::string_view name) const
Definition: command_line.cc:61
CommandLine CommandLineFromInitializerList(std::initializer_list< StringType > argv)
Definition: command_line.h:229
bool ProcessArg(const std::string &arg)
Definition: command_line.cc:89
std::string GetOptionValueWithDefault(std::string_view name, std::string_view default_value) const
Definition: command_line.cc:72
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:27
CommandLine CommandLineFromIterators(InputIterator first, InputIterator last)
Definition: command_line.h:201
bool HasOption(std::string_view name, size_t *index=nullptr) const
Definition: command_line.cc:40