blob: c5f0bf4ad4e4e65a56d27d9b6c8bdd9a4193141e [file] [log] [blame]
// 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.
#include "flutter/fml/command_line.h"
namespace fml {
// CommandLine -----------------------------------------------------------------
CommandLine::Option::Option(const std::string& name) : name(name) {}
CommandLine::Option::Option(const std::string& name, const std::string& value)
: name(name), value(value) {}
CommandLine::CommandLine() = default;
CommandLine::CommandLine(const CommandLine& from) = default;
CommandLine::CommandLine(CommandLine&& from) = default;
CommandLine::CommandLine(const std::string& argv0,
const std::vector<Option>& options,
const std::vector<std::string>& positional_args)
: has_argv0_(true),
argv0_(argv0),
options_(options),
positional_args_(positional_args) {
for (size_t i = 0; i < options_.size(); i++)
option_index_[options_[i].name] = i;
}
CommandLine::~CommandLine() = default;
CommandLine& CommandLine::operator=(const CommandLine& from) = default;
CommandLine& CommandLine::operator=(CommandLine&& from) = default;
bool CommandLine::HasOption(std::string_view name, size_t* index) const {
auto it = option_index_.find(name.data());
if (it == option_index_.end())
return false;
if (index)
*index = it->second;
return true;
}
bool CommandLine::GetOptionValue(std::string_view name,
std::string* value) const {
size_t index;
if (!HasOption(name, &index))
return false;
*value = options_[index].value;
return true;
}
std::vector<std::string_view> CommandLine::GetOptionValues(
std::string_view name) const {
std::vector<std::string_view> ret;
for (const auto& option : options_) {
if (option.name == name)
ret.push_back(option.value);
}
return ret;
}
std::string CommandLine::GetOptionValueWithDefault(
std::string_view name,
std::string_view default_value) const {
size_t index;
if (!HasOption(name, &index))
return {default_value.data(), default_value.size()};
return options_[index].value;
}
// Factory functions (etc.) ----------------------------------------------------
namespace internal {
CommandLineBuilder::CommandLineBuilder() {}
CommandLineBuilder::~CommandLineBuilder() {}
bool CommandLineBuilder::ProcessArg(const std::string& arg) {
if (!has_argv0_) {
has_argv0_ = true;
argv0_ = arg;
return false;
}
// If we've seen a positional argument, then the remaining arguments are also
// positional.
if (started_positional_args_) {
bool rv = positional_args_.empty();
positional_args_.push_back(arg);
return rv;
}
// Anything that doesn't start with "--" is a positional argument.
if (arg.size() < 2u || arg[0] != '-' || arg[1] != '-') {
bool rv = positional_args_.empty();
started_positional_args_ = true;
positional_args_.push_back(arg);
return rv;
}
// "--" ends option processing, but isn't stored as a positional argument.
if (arg.size() == 2u) {
started_positional_args_ = true;
return false;
}
// Note: The option name *must* be at least one character, so start at
// position 3 -- "--=foo" will yield a name of "=foo" and no value. (Passing a
// starting |pos| that's "too big" is OK.)
size_t equals_pos = arg.find('=', 3u);
if (equals_pos == std::string::npos) {
options_.push_back(CommandLine::Option(arg.substr(2u)));
return false;
}
options_.push_back(CommandLine::Option(arg.substr(2u, equals_pos - 2u),
arg.substr(equals_pos + 1u)));
return false;
}
CommandLine CommandLineBuilder::Build() const {
if (!has_argv0_)
return CommandLine();
return CommandLine(argv0_, options_, positional_args_);
}
} // namespace internal
std::vector<std::string> CommandLineToArgv(const CommandLine& command_line) {
if (!command_line.has_argv0())
return std::vector<std::string>();
std::vector<std::string> argv;
const std::vector<CommandLine::Option>& options = command_line.options();
const std::vector<std::string>& positional_args =
command_line.positional_args();
// Reserve space for argv[0], options, maybe a "--" (if needed), and the
// positional arguments.
argv.reserve(1u + options.size() + 1u + positional_args.size());
argv.push_back(command_line.argv0());
for (const auto& option : options) {
if (option.value.empty())
argv.push_back("--" + option.name);
else
argv.push_back("--" + option.name + "=" + option.value);
}
if (!positional_args.empty()) {
// Insert a "--" if necessary.
if (positional_args[0].size() >= 2u && positional_args[0][0] == '-' &&
positional_args[0][1] == '-')
argv.push_back("--");
argv.insert(argv.end(), positional_args.begin(), positional_args.end());
}
return argv;
}
} // namespace fml