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