blob: 6f0a34714ce3a4909d821534d812ce2bbd9328fb [file] [log] [blame]
[email protected]72e2e2422012-02-27 18:38:121// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
[email protected]f3adb5c2008-08-07 20:07:325#include "base/command_line.h"
6
[email protected]2edc2862011-04-04 18:04:377#include <ostream>
initial.commitd7cae122008-07-26 21:49:388
jdoerrie5c4dc4e2019-02-01 18:02:339#include "base/containers/span.h"
[email protected]57999812013-02-24 05:40:5210#include "base/files/file_path.h"
initial.commitd7cae122008-07-26 21:49:3811#include "base/logging.h"
Hans Wennborgafeb3902020-06-17 14:42:2912#include "base/notreached.h"
Anton Bikineeva61fb572020-10-18 08:54:4413#include "base/ranges/algorithm.h"
Jeremy Roman863386d2017-10-31 19:25:3814#include "base/stl_util.h"
Jan Wilken Dörriebd953ac2020-07-10 23:28:5415#include "base/strings/strcat.h"
[email protected]5ae0b763e2013-02-07 23:01:3916#include "base/strings/string_split.h"
skyostild851aa12017-03-29 17:38:3517#include "base/strings/string_tokenizer.h"
[email protected]251cd6e52013-06-11 13:36:3718#include "base/strings/string_util.h"
[email protected]a4ea1f12013-06-07 18:37:0719#include "base/strings/utf_string_conversions.h"
[email protected]74e9fa22010-12-29 21:06:4320#include "build/build_config.h"
initial.commitd7cae122008-07-26 21:49:3821
[email protected]74e9fa22010-12-29 21:06:4322#if defined(OS_WIN)
23#include <windows.h>
24#include <shellapi.h>
Jan Wilken Dörrieb630aca2019-12-04 10:59:1125
26#include "base/strings/string_util_win.h"
Jesse McKenna036150c2020-07-17 21:11:1727#endif // defined(OS_WIN)
[email protected]7f113f32009-09-10 18:02:1728
[email protected]2f3b1cc2014-03-17 23:07:1529namespace base {
[email protected]04af979a2013-02-16 04:12:2630
Ivan Kotenkova16212a52017-11-08 12:37:3331CommandLine* CommandLine::current_process_commandline_ = nullptr;
initial.commitd7cae122008-07-26 21:49:3832
[email protected]06cc083a2011-03-01 02:28:4233namespace {
[email protected]2f3b1cc2014-03-17 23:07:1534
Jan Wilken Dörrieda77fd432019-10-24 21:40:3435constexpr CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
36constexpr CommandLine::CharType kSwitchValueSeparator[] =
37 FILE_PATH_LITERAL("=");
[email protected]bf98a0e12013-09-25 23:36:0038
[email protected]06cc083a2011-03-01 02:28:4239// Since we use a lazy match, make sure that longer versions (like "--") are
40// listed before shorter versions (like "-") of similar prefixes.
[email protected]5d426332008-08-08 20:46:2141#if defined(OS_WIN)
[email protected]bf98a0e12013-09-25 23:36:0042// By putting slash last, we can control whether it is treaded as a switch
43// value by changing the value of switch_prefix_count to be one less than
44// the array size.
Jan Wilken Dörrieda77fd432019-10-24 21:40:3445constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {L"--", L"-", L"/"};
Fabrice de Gans-Riberi306871de2018-05-16 19:38:3946#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
[email protected]1a48f312008-08-12 01:14:3747// Unixes don't use slash as a switch.
Jan Wilken Dörrieda77fd432019-10-24 21:40:3448constexpr CommandLine::StringPieceType kSwitchPrefixes[] = {"--", "-"};
[email protected]5d426332008-08-08 20:46:2149#endif
deepak1556bb31c182019-10-29 23:15:5850size_t switch_prefix_count = base::size(kSwitchPrefixes);
initial.commitd7cae122008-07-26 21:49:3851
Jesse McKenna036150c2020-07-17 21:11:1752#if defined(OS_WIN)
53// Switch string that specifies the single argument to the command line.
54// If present, everything after this switch is interpreted as a single
55// argument regardless of whitespace, quotes, etc. Used for launches from the
56// Windows shell, which may have arguments with unencoded quotes that could
57// otherwise unexpectedly be split into multiple arguments
58// (https://crbug.com/937179).
59constexpr CommandLine::CharType kSingleArgument[] =
60 FILE_PATH_LITERAL("single-argument");
61#endif // defined(OS_WIN)
62
Pavol Markobf16b812019-06-14 00:53:1263size_t GetSwitchPrefixLength(CommandLine::StringPieceType string) {
[email protected]bf98a0e12013-09-25 23:36:0064 for (size_t i = 0; i < switch_prefix_count; ++i) {
[email protected]a40ca4302011-05-14 01:10:2465 CommandLine::StringType prefix(kSwitchPrefixes[i]);
Pavol Markobf16b812019-06-14 00:53:1266 if (string.substr(0, prefix.length()) == prefix)
[email protected]a40ca4302011-05-14 01:10:2467 return prefix.length();
68 }
69 return 0;
initial.commitd7cae122008-07-26 21:49:3870}
[email protected]0fd23af2011-02-20 06:33:0471
[email protected]a40ca4302011-05-14 01:10:2472// Fills in |switch_string| and |switch_value| if |string| is a switch.
73// This will preserve the input switch prefix in the output |switch_string|.
74bool IsSwitch(const CommandLine::StringType& string,
75 CommandLine::StringType* switch_string,
76 CommandLine::StringType* switch_value) {
77 switch_string->clear();
78 switch_value->clear();
[email protected]21e342f2012-10-19 06:19:5979 size_t prefix_length = GetSwitchPrefixLength(string);
80 if (prefix_length == 0 || prefix_length == string.length())
[email protected]a40ca4302011-05-14 01:10:2481 return false;
82
83 const size_t equals_position = string.find(kSwitchValueSeparator);
84 *switch_string = string.substr(0, equals_position);
85 if (equals_position != CommandLine::StringType::npos)
86 *switch_value = string.substr(equals_position + 1);
87 return true;
88}
89
Pavol Markobf16b812019-06-14 00:53:1290// Returns true iff |string| represents a switch with key
91// |switch_key_without_prefix|, regardless of value.
92bool IsSwitchWithKey(CommandLine::StringPieceType string,
93 CommandLine::StringPieceType switch_key_without_prefix) {
94 size_t prefix_length = GetSwitchPrefixLength(string);
95 if (prefix_length == 0 || prefix_length == string.length())
96 return false;
97
98 const size_t equals_position = string.find(kSwitchValueSeparator);
99 return string.substr(prefix_length, equals_position - prefix_length) ==
100 switch_key_without_prefix;
101}
102
tfarina023b1dcc2015-12-06 13:25:41103#if defined(OS_WIN)
Jesse McKenna036150c2020-07-17 21:11:17104// Quote a string as necessary for CommandLineToArgvW compatibility *on
105// Windows*.
106std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg) {
107 // Ensure that GetCommandLineString isn't used to generate command-line
108 // strings for the Windows shell by checking for Windows placeholders like
109 // "%1". GetCommandLineStringForShell should be used instead to get a string
110 // with the correct placeholder format for the shell.
111 DCHECK(arg.size() != 2 || arg[0] != L'%');
[email protected]a40ca4302011-05-14 01:10:24112
[email protected]0fd23af2011-02-20 06:33:04113 // We follow the quoting rules of CommandLineToArgvW.
114 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
Jan Wilken Dörrieda77fd432019-10-24 21:40:34115 std::wstring quotable_chars(L" \\\"");
Jan Wilken Dörrieda77fd432019-10-24 21:40:34116 if (arg.find_first_of(quotable_chars) == std::wstring::npos) {
[email protected]0fd23af2011-02-20 06:33:04117 // No quoting necessary.
118 return arg;
119 }
120
Jan Wilken Dörrieda77fd432019-10-24 21:40:34121 std::wstring out;
jdoerrie5c4dc4e2019-02-01 18:02:33122 out.push_back('"');
[email protected]0fd23af2011-02-20 06:33:04123 for (size_t i = 0; i < arg.size(); ++i) {
124 if (arg[i] == '\\') {
125 // Find the extent of this run of backslashes.
126 size_t start = i, end = start + 1;
thestig8badc792014-12-04 22:14:22127 for (; end < arg.size() && arg[end] == '\\'; ++end) {}
[email protected]0fd23af2011-02-20 06:33:04128 size_t backslash_count = end - start;
129
130 // Backslashes are escapes only if the run is followed by a double quote.
131 // Since we also will end the string with a double quote, we escape for
132 // either a double quote or the end of the string.
133 if (end == arg.size() || arg[end] == '"') {
134 // To quote, we need to output 2x as many backslashes.
135 backslash_count *= 2;
136 }
137 for (size_t j = 0; j < backslash_count; ++j)
138 out.push_back('\\');
139
140 // Advance i to one before the end to balance i++ in loop.
141 i = end - 1;
142 } else if (arg[i] == '"') {
143 out.push_back('\\');
144 out.push_back('"');
145 } else {
146 out.push_back(arg[i]);
147 }
148 }
149 out.push_back('"');
150
151 return out;
152}
Jesse McKenna036150c2020-07-17 21:11:17153#endif // defined(OS_WIN)
initial.commitd7cae122008-07-26 21:49:38154
[email protected]06cc083a2011-03-01 02:28:42155} // namespace
156
[email protected]a40ca4302011-05-14 01:10:24157CommandLine::CommandLine(NoProgram no_program)
158 : argv_(1),
159 begin_args_(1) {
[email protected]06cc083a2011-03-01 02:28:42160}
161
[email protected]a40ca4302011-05-14 01:10:24162CommandLine::CommandLine(const FilePath& program)
163 : argv_(1),
164 begin_args_(1) {
165 SetProgram(program);
[email protected]06cc083a2011-03-01 02:28:42166}
167
[email protected]a40ca4302011-05-14 01:10:24168CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
169 : argv_(1),
170 begin_args_(1) {
[email protected]06cc083a2011-03-01 02:28:42171 InitFromArgv(argc, argv);
172}
173
[email protected]a40ca4302011-05-14 01:10:24174CommandLine::CommandLine(const StringVector& argv)
175 : argv_(1),
176 begin_args_(1) {
[email protected]06cc083a2011-03-01 02:28:42177 InitFromArgv(argv);
178}
[email protected]06cc083a2011-03-01 02:28:42179
Chris Watkinsbb7211c2017-11-29 07:16:38180CommandLine::CommandLine(const CommandLine& other) = default;
jackhou1bd9da92015-05-21 04:48:00181
Chris Watkinsbb7211c2017-11-29 07:16:38182CommandLine& CommandLine::operator=(const CommandLine& other) = default;
jackhou1bd9da92015-05-21 04:48:00183
Chris Watkinsbb7211c2017-11-29 07:16:38184CommandLine::~CommandLine() = default;
[email protected]acbeb3d2011-03-01 20:47:58185
[email protected]bf98a0e12013-09-25 23:36:00186#if defined(OS_WIN)
187// static
188void CommandLine::set_slash_is_not_a_switch() {
189 // The last switch prefix should be slash, so adjust the size to skip it.
Jan Wilken Dörrieda77fd432019-10-24 21:40:34190 static_assert(base::make_span(kSwitchPrefixes).back() == L"/",
191 "Error: Last switch prefix is not a slash.");
deepak1556bb31c182019-10-29 23:15:58192 switch_prefix_count = base::size(kSwitchPrefixes) - 1;
[email protected]bf98a0e12013-09-25 23:36:00193}
anantad936bc12016-06-22 21:40:31194
195// static
196void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) {
197 DCHECK(!current_process_commandline_);
198 current_process_commandline_ = new CommandLine(NO_PROGRAM);
Jan Wilken Dörrieda77fd432019-10-24 21:40:34199 // On Windows we need to convert the command line arguments to std::wstring.
jdoerrie5c4dc4e2019-02-01 18:02:33200 CommandLine::StringVector argv_vector;
anantad936bc12016-06-22 21:40:31201 for (int i = 0; i < argc; ++i)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34202 argv_vector.push_back(UTF8ToWide(argv[i]));
anantad936bc12016-06-22 21:40:31203 current_process_commandline_->InitFromArgv(argv_vector);
204}
Jesse McKenna036150c2020-07-17 21:11:17205#endif // defined(OS_WIN)
[email protected]bf98a0e12013-09-25 23:36:00206
[email protected]06cc083a2011-03-01 02:28:42207// static
[email protected]72e2e2422012-02-27 18:38:12208bool CommandLine::Init(int argc, const char* const* argv) {
[email protected]f96fe2c42011-07-13 18:03:34209 if (current_process_commandline_) {
210 // If this is intentional, Reset() must be called first. If we are using
211 // the shared build mode, we have to share a single object across multiple
212 // shared libraries.
[email protected]72e2e2422012-02-27 18:38:12213 return false;
[email protected]f96fe2c42011-07-13 18:03:34214 }
215
[email protected]a40ca4302011-05-14 01:10:24216 current_process_commandline_ = new CommandLine(NO_PROGRAM);
[email protected]06cc083a2011-03-01 02:28:42217#if defined(OS_WIN)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34218 current_process_commandline_->ParseFromString(::GetCommandLineW());
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39219#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
[email protected]06cc083a2011-03-01 02:28:42220 current_process_commandline_->InitFromArgv(argc, argv);
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39221#else
222#error Unsupported platform
[email protected]06cc083a2011-03-01 02:28:42223#endif
[email protected]72e2e2422012-02-27 18:38:12224
225 return true;
[email protected]06cc083a2011-03-01 02:28:42226}
227
228// static
229void CommandLine::Reset() {
230 DCHECK(current_process_commandline_);
231 delete current_process_commandline_;
Ivan Kotenkova16212a52017-11-08 12:37:33232 current_process_commandline_ = nullptr;
[email protected]06cc083a2011-03-01 02:28:42233}
234
235// static
236CommandLine* CommandLine::ForCurrentProcess() {
237 DCHECK(current_process_commandline_);
238 return current_process_commandline_;
[email protected]3a3d47472010-07-15 21:03:54239}
240
[email protected]2bf64a92013-07-11 23:10:40241// static
242bool CommandLine::InitializedForCurrentProcess() {
243 return !!current_process_commandline_;
244}
245
[email protected]f3adb5c2008-08-07 20:07:32246#if defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:42247// static
Jan Wilken Dörrieda77fd432019-10-24 21:40:34248CommandLine CommandLine::FromString(StringPieceType command_line) {
[email protected]a40ca4302011-05-14 01:10:24249 CommandLine cmd(NO_PROGRAM);
[email protected]06cc083a2011-03-01 02:28:42250 cmd.ParseFromString(command_line);
251 return cmd;
252}
Jesse McKenna036150c2020-07-17 21:11:17253#endif // defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:42254
[email protected]a40ca4302011-05-14 01:10:24255void CommandLine::InitFromArgv(int argc,
256 const CommandLine::CharType* const* argv) {
257 StringVector new_argv;
[email protected]06cc083a2011-03-01 02:28:42258 for (int i = 0; i < argc; ++i)
[email protected]a40ca4302011-05-14 01:10:24259 new_argv.push_back(argv[i]);
260 InitFromArgv(new_argv);
[email protected]51343d5a2009-10-26 22:39:33261}
262
[email protected]06cc083a2011-03-01 02:28:42263void CommandLine::InitFromArgv(const StringVector& argv) {
[email protected]a40ca4302011-05-14 01:10:24264 argv_ = StringVector(1);
[email protected]93660ab32013-06-18 08:19:18265 switches_.clear();
[email protected]a40ca4302011-05-14 01:10:24266 begin_args_ = 1;
267 SetProgram(argv.empty() ? FilePath() : FilePath(argv[0]));
Jesse McKenna036150c2020-07-17 21:11:17268 AppendSwitchesAndArguments(argv);
[email protected]06cc083a2011-03-01 02:28:42269}
[email protected]06cc083a2011-03-01 02:28:42270
[email protected]06cc083a2011-03-01 02:28:42271FilePath CommandLine::GetProgram() const {
[email protected]06cc083a2011-03-01 02:28:42272 return FilePath(argv_[0]);
[email protected]a40ca4302011-05-14 01:10:24273}
274
275void CommandLine::SetProgram(const FilePath& program) {
tfarina023b1dcc2015-12-06 13:25:41276#if defined(OS_WIN)
Jan Wilken Dörrieb630aca2019-12-04 10:59:11277 argv_[0] = StringType(TrimWhitespace(program.value(), TRIM_ALL));
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39278#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
tfarina023b1dcc2015-12-06 13:25:41279 TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]);
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39280#else
281#error Unsupported platform
tfarina023b1dcc2015-12-06 13:25:41282#endif
[email protected]06cc083a2011-03-01 02:28:42283}
284
jdoerrie5c4dc4e2019-02-01 18:02:33285bool CommandLine::HasSwitch(const StringPiece& switch_string) const {
brettwfce8d192015-08-10 19:07:51286 DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
Jan Wilken Dörrief61e74c2019-06-07 08:20:02287 return Contains(switches_, switch_string);
[email protected]06cc083a2011-03-01 02:28:42288}
289
jackhou1bd9da92015-05-21 04:48:00290bool CommandLine::HasSwitch(const char switch_constant[]) const {
jdoerrie5c4dc4e2019-02-01 18:02:33291 return HasSwitch(StringPiece(switch_constant));
tapted009a1dc82015-03-30 03:57:10292}
293
[email protected]06cc083a2011-03-01 02:28:42294std::string CommandLine::GetSwitchValueASCII(
jdoerrie5c4dc4e2019-02-01 18:02:33295 const StringPiece& switch_string) const {
[email protected]a40ca4302011-05-14 01:10:24296 StringType value = GetSwitchValueNative(switch_string);
Jan Wilken Dörrieda77fd432019-10-24 21:40:34297#if defined(OS_WIN)
298 if (!IsStringASCII(base::AsStringPiece16(value))) {
299#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
[email protected]bd6fc2f2014-03-17 23:55:43300 if (!IsStringASCII(value)) {
Jan Wilken Dörrieda77fd432019-10-24 21:40:34301#endif
[email protected]a42d4632011-10-26 21:48:00302 DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
303 return std::string();
[email protected]06cc083a2011-03-01 02:28:42304 }
305#if defined(OS_WIN)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34306 return WideToUTF8(value);
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39307#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
[email protected]06cc083a2011-03-01 02:28:42308 return value;
309#endif
310}
311
312FilePath CommandLine::GetSwitchValuePath(
jdoerrie5c4dc4e2019-02-01 18:02:33313 const StringPiece& switch_string) const {
[email protected]06cc083a2011-03-01 02:28:42314 return FilePath(GetSwitchValueNative(switch_string));
315}
316
317CommandLine::StringType CommandLine::GetSwitchValueNative(
jdoerrie5c4dc4e2019-02-01 18:02:33318 const StringPiece& switch_string) const {
brettwfce8d192015-08-10 19:07:51319 DCHECK_EQ(ToLowerASCII(switch_string), switch_string);
Jeremy Roman863386d2017-10-31 19:25:38320 auto result = switches_.find(switch_string);
321 return result == switches_.end() ? StringType() : result->second;
[email protected]06cc083a2011-03-01 02:28:42322}
323
[email protected]06cc083a2011-03-01 02:28:42324void CommandLine::AppendSwitch(const std::string& switch_string) {
[email protected]a40ca4302011-05-14 01:10:24325 AppendSwitchNative(switch_string, StringType());
[email protected]06cc083a2011-03-01 02:28:42326}
327
328void CommandLine::AppendSwitchPath(const std::string& switch_string,
329 const FilePath& path) {
330 AppendSwitchNative(switch_string, path.value());
331}
332
333void CommandLine::AppendSwitchNative(const std::string& switch_string,
Jan Wilken Dörriebd953ac2020-07-10 23:28:54334 CommandLine::StringPieceType value) {
[email protected]06cc083a2011-03-01 02:28:42335#if defined(OS_WIN)
brettwfce8d192015-08-10 19:07:51336 const std::string switch_key = ToLowerASCII(switch_string);
Jan Wilken Dörrieda77fd432019-10-24 21:40:34337 StringType combined_switch_string(UTF8ToWide(switch_key));
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39338#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
jackhou1bd9da92015-05-21 04:48:00339 const std::string& switch_key = switch_string;
340 StringType combined_switch_string(switch_key);
[email protected]a40ca4302011-05-14 01:10:24341#endif
342 size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
Jan Wilken Dörriebd953ac2020-07-10 23:28:54343 base::InsertOrAssign(switches_, switch_key.substr(prefix_length),
344 StringType(value));
[email protected]a40ca4302011-05-14 01:10:24345 // Preserve existing switch prefixes in |argv_|; only append one if necessary.
Jan Wilken Dörrieda77fd432019-10-24 21:40:34346 if (prefix_length == 0) {
347 combined_switch_string.insert(0, kSwitchPrefixes[0].data(),
348 kSwitchPrefixes[0].size());
349 }
[email protected]06cc083a2011-03-01 02:28:42350 if (!value.empty())
Jan Wilken Dörriebd953ac2020-07-10 23:28:54351 base::StrAppend(&combined_switch_string, {kSwitchValueSeparator, value});
[email protected]a40ca4302011-05-14 01:10:24352 // Append the switch and update the switches/arguments divider |begin_args_|.
353 argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
[email protected]06cc083a2011-03-01 02:28:42354}
355
356void CommandLine::AppendSwitchASCII(const std::string& switch_string,
357 const std::string& value_string) {
358#if defined(OS_WIN)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34359 AppendSwitchNative(switch_string, UTF8ToWide(value_string));
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39360#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
[email protected]06cc083a2011-03-01 02:28:42361 AppendSwitchNative(switch_string, value_string);
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39362#else
363#error Unsupported platform
[email protected]06cc083a2011-03-01 02:28:42364#endif
365}
366
Pavol Markobf16b812019-06-14 00:53:12367void CommandLine::RemoveSwitch(base::StringPiece switch_key_without_prefix) {
368#if defined(OS_WIN)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34369 StringType switch_key_native = UTF8ToWide(switch_key_without_prefix);
Pavol Markobf16b812019-06-14 00:53:12370#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
371 StringType switch_key_native = switch_key_without_prefix.as_string();
372#endif
373
374 DCHECK_EQ(ToLowerASCII(switch_key_without_prefix), switch_key_without_prefix);
375 DCHECK_EQ(0u, GetSwitchPrefixLength(switch_key_native));
376 size_t erased_from_switches =
377 switches_.erase(switch_key_without_prefix.as_string());
378 DCHECK(erased_from_switches <= 1);
379 if (!erased_from_switches)
380 return;
381
382 // Also erase from the switches section of |argv_| and update |begin_args_|
383 // accordingly.
384 // Switches in |argv_| have indices [1, begin_args_).
385 auto argv_switches_begin = argv_.begin() + 1;
386 auto argv_switches_end = argv_.begin() + begin_args_;
387 DCHECK(argv_switches_begin <= argv_switches_end);
388 DCHECK(argv_switches_end <= argv_.end());
Andrei Polushin2ec89bc2019-07-30 20:47:17389 auto expell = std::remove_if(argv_switches_begin, argv_switches_end,
Pavol Markobf16b812019-06-14 00:53:12390 [&switch_key_native](const StringType& arg) {
391 return IsSwitchWithKey(arg, switch_key_native);
392 });
Andrei Polushin2ec89bc2019-07-30 20:47:17393 if (expell == argv_switches_end) {
Pavol Markobf16b812019-06-14 00:53:12394 NOTREACHED();
395 return;
396 }
Andrei Polushin2ec89bc2019-07-30 20:47:17397 begin_args_ -= argv_switches_end - expell;
398 argv_.erase(expell, argv_switches_end);
Avi Drissman1aa6cb92019-01-23 15:58:38399}
400
[email protected]06cc083a2011-03-01 02:28:42401void CommandLine::CopySwitchesFrom(const CommandLine& source,
402 const char* const switches[],
403 size_t count) {
404 for (size_t i = 0; i < count; ++i) {
[email protected]a40ca4302011-05-14 01:10:24405 if (source.HasSwitch(switches[i]))
406 AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i]));
[email protected]06cc083a2011-03-01 02:28:42407 }
408}
409
[email protected]75f1c782011-07-13 23:41:22410CommandLine::StringVector CommandLine::GetArgs() const {
[email protected]a40ca4302011-05-14 01:10:24411 // Gather all arguments after the last switch (may include kSwitchTerminator).
412 StringVector args(argv_.begin() + begin_args_, argv_.end());
413 // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
Anton Bikineeva61fb572020-10-18 08:54:44414 auto switch_terminator = ranges::find(args, kSwitchTerminator);
[email protected]a40ca4302011-05-14 01:10:24415 if (switch_terminator != args.end())
416 args.erase(switch_terminator);
417 return args;
418}
419
[email protected]06cc083a2011-03-01 02:28:42420void CommandLine::AppendArg(const std::string& value) {
421#if defined(OS_WIN)
[email protected]bd6fc2f2014-03-17 23:55:43422 DCHECK(IsStringUTF8(value));
Jan Wilken Dörrieda77fd432019-10-24 21:40:34423 AppendArgNative(UTF8ToWide(value));
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39424#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
[email protected]06cc083a2011-03-01 02:28:42425 AppendArgNative(value);
Fabrice de Gans-Riberi306871de2018-05-16 19:38:39426#else
427#error Unsupported platform
[email protected]06cc083a2011-03-01 02:28:42428#endif
429}
430
431void CommandLine::AppendArgPath(const FilePath& path) {
432 AppendArgNative(path.value());
433}
434
435void CommandLine::AppendArgNative(const CommandLine::StringType& value) {
[email protected]06cc083a2011-03-01 02:28:42436 argv_.push_back(value);
[email protected]06cc083a2011-03-01 02:28:42437}
438
439void CommandLine::AppendArguments(const CommandLine& other,
440 bool include_program) {
[email protected]06cc083a2011-03-01 02:28:42441 if (include_program)
[email protected]a40ca4302011-05-14 01:10:24442 SetProgram(other.GetProgram());
Jesse McKenna036150c2020-07-17 21:11:17443 AppendSwitchesAndArguments(other.argv());
[email protected]06cc083a2011-03-01 02:28:42444}
445
446void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) {
[email protected]06cc083a2011-03-01 02:28:42447 if (wrapper.empty())
448 return;
skyostild851aa12017-03-29 17:38:35449 // Split the wrapper command based on whitespace (with quoting).
450 using CommandLineTokenizer =
451 StringTokenizerT<StringType, StringType::const_iterator>;
452 CommandLineTokenizer tokenizer(wrapper, FILE_PATH_LITERAL(" "));
453 tokenizer.set_quote_chars(FILE_PATH_LITERAL("'\""));
454 std::vector<StringType> wrapper_argv;
455 while (tokenizer.GetNext())
456 wrapper_argv.emplace_back(tokenizer.token());
457
[email protected]a40ca4302011-05-14 01:10:24458 // Prepend the wrapper and update the switches/arguments |begin_args_|.
459 argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end());
460 begin_args_ += wrapper_argv.size();
[email protected]06cc083a2011-03-01 02:28:42461}
462
463#if defined(OS_WIN)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34464void CommandLine::ParseFromString(StringPieceType command_line) {
jdoerrie90787012019-02-07 16:22:02465 command_line = TrimWhitespace(command_line, TRIM_ALL);
466 if (command_line.empty())
[email protected]bb975362009-01-21 01:00:22467 return;
Jesse McKenna036150c2020-07-17 21:11:17468 raw_command_line_string_ = command_line;
[email protected]bb975362009-01-21 01:00:22469
470 int num_args = 0;
471 wchar_t** args = NULL;
Cliff Smolinskyc5c52102019-05-03 20:51:54472 // When calling CommandLineToArgvW, use the apiset if available.
473 // Doing so will bypass loading shell32.dll on Win8+.
474 HMODULE downlevel_shell32_dll =
475 ::LoadLibraryEx(L"api-ms-win-downlevel-shell32-l1-1-0.dll", nullptr,
476 LOAD_LIBRARY_SEARCH_SYSTEM32);
477 if (downlevel_shell32_dll) {
478 auto command_line_to_argv_w_proc =
479 reinterpret_cast<decltype(::CommandLineToArgvW)*>(
480 ::GetProcAddress(downlevel_shell32_dll, "CommandLineToArgvW"));
481 if (command_line_to_argv_w_proc)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34482 args = command_line_to_argv_w_proc(command_line.data(), &num_args);
Cliff Smolinskyc5c52102019-05-03 20:51:54483 } else {
484 // Since the apiset is not available, allow the delayload of shell32.dll
485 // to take place.
Jan Wilken Dörrieda77fd432019-10-24 21:40:34486 args = ::CommandLineToArgvW(command_line.data(), &num_args);
Cliff Smolinskyc5c52102019-05-03 20:51:54487 }
[email protected]bb975362009-01-21 01:00:22488
[email protected]a42d4632011-10-26 21:48:00489 DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
Jan Wilken Dörrieda77fd432019-10-24 21:40:34490 << command_line;
491 StringVector argv(args, args + num_args);
jdoerrie5c4dc4e2019-02-01 18:02:33492 InitFromArgv(argv);
Jesse McKenna036150c2020-07-17 21:11:17493 raw_command_line_string_ = StringPieceType();
[email protected]a40ca4302011-05-14 01:10:24494 LocalFree(args);
Chris Davisa36cb122020-01-21 05:34:00495
496 if (downlevel_shell32_dll)
497 ::FreeLibrary(downlevel_shell32_dll);
[email protected]bb975362009-01-21 01:00:22498}
Jesse McKenna036150c2020-07-17 21:11:17499#endif // defined(OS_WIN)
500
501void CommandLine::AppendSwitchesAndArguments(
502 const CommandLine::StringVector& argv) {
503 bool parse_switches = true;
504#if defined(OS_WIN)
505 const bool is_parsed_from_string = !raw_command_line_string_.empty();
506#endif
507 for (size_t i = 1; i < argv.size(); ++i) {
508 CommandLine::StringType arg = argv[i];
509#if defined(OS_WIN)
510 arg = CommandLine::StringType(TrimWhitespace(arg, TRIM_ALL));
511#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
512 TrimWhitespaceASCII(arg, TRIM_ALL, &arg);
[email protected]f3adb5c2008-08-07 20:07:32513#endif
[email protected]2f3b1cc2014-03-17 23:07:15514
Jesse McKenna036150c2020-07-17 21:11:17515 CommandLine::StringType switch_string;
516 CommandLine::StringType switch_value;
517 parse_switches &= (arg != kSwitchTerminator);
518 if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
519#if defined(OS_WIN)
520 if (is_parsed_from_string &&
521 IsSwitchWithKey(switch_string, kSingleArgument)) {
522 ParseAsSingleArgument(switch_string);
523 return;
524 }
525 AppendSwitchNative(WideToUTF8(switch_string), switch_value);
526#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
527 AppendSwitchNative(switch_string, switch_value);
528#else
529#error Unsupported platform
530#endif
531 } else {
532 AppendArgNative(arg);
533 }
534 }
535}
536
537CommandLine::StringType CommandLine::GetCommandLineString() const {
mgiucac974d5102014-10-01 09:24:51538 StringType string(argv_[0]);
539#if defined(OS_WIN)
Jesse McKenna036150c2020-07-17 21:11:17540 string = QuoteForCommandLineToArgvW(string);
mgiucac974d5102014-10-01 09:24:51541#endif
Jesse McKenna036150c2020-07-17 21:11:17542 StringType params(GetArgumentsString());
mgiucac974d5102014-10-01 09:24:51543 if (!params.empty()) {
Jan Wilken Dörrieda77fd432019-10-24 21:40:34544 string.append(FILE_PATH_LITERAL(" "));
mgiucac974d5102014-10-01 09:24:51545 string.append(params);
546 }
547 return string;
548}
549
Jesse McKenna036150c2020-07-17 21:11:17550#if defined(OS_WIN)
551// NOTE: this function is used to set Chrome's open command in the registry
552// during update. Any change to the syntax must be compatible with the prior
553// version (i.e., any new syntax must be understood by older browsers expecting
554// the old syntax, and the new browser must still handle the old syntax), as
555// old versions are likely to persist, e.g., immediately after background
556// update, when parsing command lines for other channels, when uninstalling web
557// applications installed using the old syntax, etc.
558CommandLine::StringType CommandLine::GetCommandLineStringForShell() const {
559 DCHECK(GetArgs().empty());
560 StringType command_line_string = GetCommandLineString();
561 return command_line_string + FILE_PATH_LITERAL(" ") +
Jan Wilken Dörrie4aa966b2020-08-24 18:20:29562 StringType(kSwitchPrefixes[0]) + kSingleArgument +
Jesse McKenna036150c2020-07-17 21:11:17563 FILE_PATH_LITERAL(" %1");
564}
565#endif // defined(OS_WIN)
566
567CommandLine::StringType CommandLine::GetArgumentsString() const {
mgiucac974d5102014-10-01 09:24:51568 StringType params;
569 // Append switches and arguments.
570 bool parse_switches = true;
571 for (size_t i = 1; i < argv_.size(); ++i) {
572 StringType arg = argv_[i];
573 StringType switch_string;
574 StringType switch_value;
575 parse_switches &= arg != kSwitchTerminator;
576 if (i > 1)
Jan Wilken Dörrieda77fd432019-10-24 21:40:34577 params.append(FILE_PATH_LITERAL(" "));
mgiucac974d5102014-10-01 09:24:51578 if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
579 params.append(switch_string);
580 if (!switch_value.empty()) {
581#if defined(OS_WIN)
Jesse McKenna036150c2020-07-17 21:11:17582 switch_value = QuoteForCommandLineToArgvW(switch_value);
mgiucac974d5102014-10-01 09:24:51583#endif
584 params.append(kSwitchValueSeparator + switch_value);
585 }
thestig8badc792014-12-04 22:14:22586 } else {
mgiucac974d5102014-10-01 09:24:51587#if defined(OS_WIN)
Jesse McKenna036150c2020-07-17 21:11:17588 arg = QuoteForCommandLineToArgvW(arg);
mgiucac974d5102014-10-01 09:24:51589#endif
590 params.append(arg);
591 }
592 }
593 return params;
594}
595
Jesse McKenna036150c2020-07-17 21:11:17596#if defined(OS_WIN)
597void CommandLine::ParseAsSingleArgument(
598 const CommandLine::StringType& single_arg_switch) {
599 DCHECK(!raw_command_line_string_.empty());
600
601 // Remove any previously parsed arguments.
602 argv_.resize(begin_args_);
603
604 // Locate "--single-argument" in the process's raw command line. Results are
605 // unpredictable if "--single-argument" appears as part of a previous
606 // argument or switch.
607 const size_t single_arg_switch_position =
608 raw_command_line_string_.find(single_arg_switch);
609 DCHECK_NE(single_arg_switch_position, StringType::npos);
610
611 // Append the portion of the raw command line that starts one character past
612 // "--single-argument" as the one and only argument, or return if no
613 // argument is present.
614 const size_t arg_position =
615 single_arg_switch_position + single_arg_switch.length() + 1;
616 if (arg_position >= raw_command_line_string_.length())
617 return;
618 const StringPieceType arg = raw_command_line_string_.substr(arg_position);
619 if (!arg.empty()) {
Jan Wilken Dörrie4aa966b2020-08-24 18:20:29620 AppendArgNative(StringType(arg));
Jesse McKenna036150c2020-07-17 21:11:17621 }
622}
623#endif // defined(OS_WIN)
624
[email protected]2f3b1cc2014-03-17 23:07:15625} // namespace base