| // 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 |