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