mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
267 lines
10 KiB
C++
267 lines
10 KiB
C++
// 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 FLUTTER_FML_COMMAND_LINE_H_
|
|
#define FLUTTER_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 // FLUTTER_FML_COMMAND_LINE_H_
|