1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/fml/command_line.h"
6
7 namespace fml {
8
9 // CommandLine -----------------------------------------------------------------
10
Option(const std::string & name)11 CommandLine::Option::Option(const std::string& name) : name(name) {}
12
Option(const std::string & name,const std::string & value)13 CommandLine::Option::Option(const std::string& name, const std::string& value)
14 : name(name), value(value) {}
15
16 CommandLine::CommandLine() = default;
17
18 CommandLine::CommandLine(const CommandLine& from) = default;
19
20 CommandLine::CommandLine(CommandLine&& from) = default;
21
CommandLine(const std::string & argv0,const std::vector<Option> & options,const std::vector<std::string> & positional_args)22 CommandLine::CommandLine(const std::string& argv0,
23 const std::vector<Option>& options,
24 const std::vector<std::string>& positional_args)
25 : has_argv0_(true),
26 argv0_(argv0),
27 options_(options),
28 positional_args_(positional_args) {
29 for (size_t i = 0; i < options_.size(); i++)
30 option_index_[options_[i].name] = i;
31 }
32
33 CommandLine::~CommandLine() = default;
34
35 CommandLine& CommandLine::operator=(const CommandLine& from) = default;
36
37 CommandLine& CommandLine::operator=(CommandLine&& from) = default;
38
HasOption(std::string_view name,size_t * index) const39 bool CommandLine::HasOption(std::string_view name, size_t* index) const {
40 auto it = option_index_.find(name.data());
41 if (it == option_index_.end())
42 return false;
43 if (index)
44 *index = it->second;
45 return true;
46 }
47
GetOptionValue(std::string_view name,std::string * value) const48 bool CommandLine::GetOptionValue(std::string_view name,
49 std::string* value) const {
50 size_t index;
51 if (!HasOption(name, &index))
52 return false;
53 *value = options_[index].value;
54 return true;
55 }
56
GetOptionValues(std::string_view name) const57 std::vector<std::string_view> CommandLine::GetOptionValues(
58 std::string_view name) const {
59 std::vector<std::string_view> ret;
60 for (const auto& option : options_) {
61 if (option.name == name)
62 ret.push_back(option.value);
63 }
64 return ret;
65 }
66
GetOptionValueWithDefault(std::string_view name,std::string_view default_value) const67 std::string CommandLine::GetOptionValueWithDefault(
68 std::string_view name,
69 std::string_view default_value) const {
70 size_t index;
71 if (!HasOption(name, &index))
72 return {default_value.data(), default_value.size()};
73 return options_[index].value;
74 }
75
76 // Factory functions (etc.) ----------------------------------------------------
77
78 namespace internal {
79
CommandLineBuilder()80 CommandLineBuilder::CommandLineBuilder() {}
~CommandLineBuilder()81 CommandLineBuilder::~CommandLineBuilder() {}
82
ProcessArg(const std::string & arg)83 bool CommandLineBuilder::ProcessArg(const std::string& arg) {
84 if (!has_argv0_) {
85 has_argv0_ = true;
86 argv0_ = arg;
87 return false;
88 }
89
90 // If we've seen a positional argument, then the remaining arguments are also
91 // positional.
92 if (started_positional_args_) {
93 bool rv = positional_args_.empty();
94 positional_args_.push_back(arg);
95 return rv;
96 }
97
98 // Anything that doesn't start with "--" is a positional argument.
99 if (arg.size() < 2u || arg[0] != '-' || arg[1] != '-') {
100 bool rv = positional_args_.empty();
101 started_positional_args_ = true;
102 positional_args_.push_back(arg);
103 return rv;
104 }
105
106 // "--" ends option processing, but isn't stored as a positional argument.
107 if (arg.size() == 2u) {
108 started_positional_args_ = true;
109 return false;
110 }
111
112 // Note: The option name *must* be at least one character, so start at
113 // position 3 -- "--=foo" will yield a name of "=foo" and no value. (Passing a
114 // starting |pos| that's "too big" is OK.)
115 size_t equals_pos = arg.find('=', 3u);
116 if (equals_pos == std::string::npos) {
117 options_.push_back(CommandLine::Option(arg.substr(2u)));
118 return false;
119 }
120
121 options_.push_back(CommandLine::Option(arg.substr(2u, equals_pos - 2u),
122 arg.substr(equals_pos + 1u)));
123 return false;
124 }
125
Build() const126 CommandLine CommandLineBuilder::Build() const {
127 if (!has_argv0_)
128 return CommandLine();
129 return CommandLine(argv0_, options_, positional_args_);
130 }
131
132 } // namespace internal
133
CommandLineToArgv(const CommandLine & command_line)134 std::vector<std::string> CommandLineToArgv(const CommandLine& command_line) {
135 if (!command_line.has_argv0())
136 return std::vector<std::string>();
137
138 std::vector<std::string> argv;
139 const std::vector<CommandLine::Option>& options = command_line.options();
140 const std::vector<std::string>& positional_args =
141 command_line.positional_args();
142 // Reserve space for argv[0], options, maybe a "--" (if needed), and the
143 // positional arguments.
144 argv.reserve(1u + options.size() + 1u + positional_args.size());
145
146 argv.push_back(command_line.argv0());
147 for (const auto& option : options) {
148 if (option.value.empty())
149 argv.push_back("--" + option.name);
150 else
151 argv.push_back("--" + option.name + "=" + option.value);
152 }
153
154 if (!positional_args.empty()) {
155 // Insert a "--" if necessary.
156 if (positional_args[0].size() >= 2u && positional_args[0][0] == '-' &&
157 positional_args[0][1] == '-')
158 argv.push_back("--");
159
160 argv.insert(argv.end(), positional_args.begin(), positional_args.end());
161 }
162
163 return argv;
164 }
165
166 } // namespace fml
167