Flutter Engine
 
Loading...
Searching...
No Matches
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 FLUTTER_FML_COMMAND_LINE_H_
37#define FLUTTER_FML_COMMAND_LINE_H_
38
39#include <cstddef>
40#include <initializer_list>
41#include <optional>
42#include <string>
43#include <string_view>
44#include <unordered_map>
45#include <vector>
46
47#include "flutter/fml/macros.h"
48
49namespace 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.
56class CommandLine final {
57 private:
58 class ConstructionHelper;
59
60 public:
61 struct Option {
62 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
70 std::string name;
71 std::string value;
72 };
73
74 // Default, copy, and move constructors (to be out-of-lined).
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
87
88 // Copy and move assignment (to be out-of-lined).
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
106 // Returns true if this command line has the option |name| (and if |index| is
107 // non-null, sets |*index| to the index of the *last* occurrence of the given
108 // option in |options()|) and false if not.
109 bool HasOption(std::string_view name, size_t* index = nullptr) const;
110
111 // Gets the value of the option |name|. Returns true (and sets |*value|) on
112 // success and false (leaving |*value| alone) on failure.
113 bool GetOptionValue(std::string_view name, std::string* value) const;
114
115 // Gets all values of the option |name|. Returns all values, which may be
116 // empty if the option is not specified.
117 std::vector<std::string_view> GetOptionValues(std::string_view name) const;
118
119 // Gets the value of the option |name|, with a default if the option is not
120 // specified. (Note: This doesn't return a const reference, since this would
121 // make the |default_value| argument inconvenient/dangerous.)
122 std::string GetOptionValueWithDefault(std::string_view name,
123 std::string_view default_value) const;
124
125 private:
126 bool has_argv0_ = false;
127 // The following should all be empty if |has_argv0_| is false.
128 std::string argv0_;
129 std::vector<Option> options_;
130 std::vector<std::string> positional_args_;
131
132 // Maps option names to position in |options_|. If a given name occurs
133 // multiple times, the index will be to the *last* occurrence.
134 std::unordered_map<std::string, size_t> option_index_;
135
136 // Allow copy and assignment.
137};
138
139// Factory functions (etc.) ----------------------------------------------------
140
141namespace internal {
142
143// Helper class for building command lines (finding options, etc.) from raw
144// arguments.
146 public:
149
150 // Processes an additional argument in the command line. Returns true if |arg|
151 // is the *first* positional argument.
152 bool ProcessArg(const std::string& arg);
153
154 // Builds a |CommandLine| from the arguments processed so far.
155 CommandLine Build() const;
156
157 private:
158 bool has_argv0_ = false;
159 std::string argv0_;
160 std::vector<CommandLine::Option> options_;
161 std::vector<std::string> positional_args_;
162
163 // True if we've started processing positional arguments.
164 bool started_positional_args_ = false;
165
167};
168
169} // namespace internal
170
171// The following factory functions create |CommandLine|s from raw arguments in
172// accordance with the rules outlined at the top of this file. (Other ways of
173// transforming raw arguments into options and positional arguments are
174// possible.)
175
176// Like |CommandLineFromIterators()| (see below), but sets
177// |*first_positional_arg| to point to the first positional argument seen (or
178// |last| if none are seen). This is useful for processing "subcommands".
179template <typename InputIterator>
181 InputIterator first,
182 InputIterator last,
183 InputIterator* first_positional_arg) {
184 if (first_positional_arg) {
185 *first_positional_arg = last;
186 }
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 }
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|.
201template <typename InputIterator>
202inline 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.
211template <typename InputIterator>
212inline CommandLine CommandLineFromIteratorsWithArgv0(const std::string& argv0,
213 InputIterator first,
214 InputIterator last) {
216 builder.ProcessArg(argv0);
217 for (auto it = first; it < last; ++it) {
218 builder.ProcessArg(*it);
219 }
220 return builder.Build();
221}
222
223// Builds a |CommandLine| by obtaining the arguments of the process using host
224// platform APIs. The resulting |CommandLine| will be encoded in UTF-8.
225// Returns an empty optional if this is not supported on the host platform.
226//
227// This can be useful on platforms where argv may not be provided as UTF-8.
228std::optional<CommandLine> CommandLineFromPlatform();
229
230// Builds a |CommandLine| from the usual argc/argv.
231inline CommandLine CommandLineFromArgcArgv(int argc, const char* const* argv) {
232 return CommandLineFromIterators(argv, argv + argc);
233}
234
235// Builds a |CommandLine| by first trying the platform specific implementation,
236// and then falling back to the argc/argv.
237//
238// If the platform provides a special way of getting arguments, this method may
239// discard the values passed in to argc/argv.
241 const char* const* argv) {
242 auto command_line = CommandLineFromPlatform();
243 if (command_line.has_value()) {
244 return *command_line;
245 }
246 return CommandLineFromArgcArgv(argc, argv);
247}
248
249// Builds a |CommandLine| from an initializer list of |std::string|s or things
250// that implicitly convert to |std::string|.
251template <typename StringType>
253 std::initializer_list<StringType> argv) {
254 return CommandLineFromIterators(argv.begin(), argv.end());
255}
256
257// This is the "opposite" of the above factory functions, transforming a
258// |CommandLine| into a vector of argument strings according to the rules
259// outlined at the top of this file.
260std::vector<std::string> CommandLineToArgv(const CommandLine& command_line);
261
262} // namespace fml
263
264#endif // FLUTTER_FML_COMMAND_LINE_H_
const std::string & argv0() const
CommandLine & operator=(const CommandLine &from)
const std::vector< Option > & options() const
std::vector< std::string_view > GetOptionValues(std::string_view name) const
const std::vector< std::string > & positional_args() const
std::string GetOptionValueWithDefault(std::string_view name, std::string_view default_value) const
bool operator==(const CommandLine &other) const
bool HasOption(std::string_view name, size_t *index=nullptr) const
bool GetOptionValue(std::string_view name, std::string *value) const
bool has_argv0() const
CommandLine & operator=(CommandLine &&from)
CommandLine(CommandLine &&from)
CommandLine(const CommandLine &from)
bool ProcessArg(const std::string &arg)
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
const char * name
Definition fuchsia.cc:49
char ** argv
Definition library.h:9
CommandLine CommandLineFromArgcArgv(int argc, const char *const *argv)
CommandLine CommandLineFromInitializerList(std::initializer_list< StringType > argv)
std::vector< std::string > CommandLineToArgv(const CommandLine &command_line)
std::optional< CommandLine > CommandLineFromPlatform()
CommandLine CommandLineFromPlatformOrArgcArgv(int argc, const char *const *argv)
CommandLine CommandLineFromIterators(InputIterator first, InputIterator last)
CommandLine CommandLineFromIteratorsFindFirstPositionalArg(InputIterator first, InputIterator last, InputIterator *first_positional_arg)
CommandLine CommandLineFromIteratorsWithArgv0(const std::string &argv0, InputIterator first, InputIterator last)
bool operator==(const Option &other) const