blob: 58ab8a84bbcda6e1c30d2306bed16163c2e64a7e [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 "src/lib/fxl/command_line.h"
namespace fxl {
// 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(StringView name, size_t* index) const {
auto it = option_index_.find(name.ToString());
if (it == option_index_.end())
return false;
if (index)
*index = it->second;
return true;
}
bool CommandLine::GetOptionValue(StringView name, std::string* value) const {
size_t index;
if (!HasOption(name, &index))
return false;
*value = options_[index].value;
return true;
}
std::vector<fxl::StringView> CommandLine::GetOptionValues(StringView name) const {
std::vector<fxl::StringView> ret;
for (const auto& option : options_) {
if (option.name == name)
ret.push_back(option.value);
}
return ret;
}
std::string CommandLine::GetOptionValueWithDefault(StringView name,
StringView default_value) const {
size_t index;
if (!HasOption(name, &index))
return default_value.ToString();
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 fxl