#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/command_line.h"
#include <ostream>
#include <string_view>
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/containers/span.h"
#include "base/debug/debugging_buildflags.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/numerics/checked_math.h"
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include <shellapi.h>
#include "base/strings/string_util_win.h"
#endif
namespace base {
CommandLine* CommandLine::current_process_commandline_ = …;
namespace {
DuplicateSwitchHandler* g_duplicate_switch_handler = …;
constexpr CommandLine::CharType kSwitchTerminator[] = …);
constexpr CommandLine::CharType kSwitchValueSeparator[] = …);
#if BUILDFLAG(IS_WIN)
constexpr CommandLine::StringViewType kSwitchPrefixes[] = {L"--", L"-", L"/"};
#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
constexpr CommandLine::StringViewType kSwitchPrefixes[] = …;
#endif
size_t switch_prefix_count = …;
#if BUILDFLAG(IS_WIN)
constexpr CommandLine::CharType kSingleArgument[] =
FILE_PATH_LITERAL("single-argument");
#endif
size_t GetSwitchPrefixLength(CommandLine::StringViewType string) { … }
bool IsSwitch(const CommandLine::StringType& string,
CommandLine::StringType* switch_string,
CommandLine::StringType* switch_value) { … }
bool IsSwitchWithKey(CommandLine::StringViewType string,
CommandLine::StringViewType switch_key_without_prefix) { … }
#if BUILDFLAG(IS_WIN)
std::wstring QuoteForCommandLineToArgvWInternal(
const std::wstring& arg,
bool allow_unsafe_insert_sequences) {
DCHECK(arg.size() != 2 || arg[0] != L'%' || allow_unsafe_insert_sequences);
constexpr wchar_t kQuotableCharacters[] = L" \t\\\"";
if (arg.find_first_of(kQuotableCharacters) == std::wstring::npos) {
return arg;
}
std::wstring out(1, L'"');
for (size_t i = 0; i < arg.size(); ++i) {
if (arg[i] == L'\\') {
size_t end = i + 1;
while (end < arg.size() && arg[end] == L'\\') {
++end;
}
const size_t backslash_count = end - i;
const size_t backslash_multiplier =
(end == arg.size() || arg[end] == L'"') ? 2 : 1;
out.append(std::wstring(backslash_count * backslash_multiplier, L'\\'));
i = end - 1;
} else if (arg[i] == L'"') {
out.append(LR"(\")");
} else {
out.push_back(arg[i]);
}
}
out.push_back(L'"');
return out;
}
#endif
}
void CommandLine::SetDuplicateSwitchHandler(
std::unique_ptr<DuplicateSwitchHandler> new_duplicate_switch_handler) { … }
CommandLine::CommandLine(NoProgram no_program) : … { … }
CommandLine::CommandLine(const FilePath& program)
: … { … }
CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
: … { … }
CommandLine::CommandLine(const StringVector& argv)
: … { … }
CommandLine::CommandLine(const CommandLine& other) = default;
CommandLine::CommandLine(CommandLine&& other) noexcept
: … { … }
CommandLine& CommandLine::operator=(const CommandLine& other) = default;
CommandLine& CommandLine::operator=(CommandLine&& other) noexcept { … }
CommandLine::~CommandLine() = default;
#if BUILDFLAG(IS_WIN)
void CommandLine::set_slash_is_not_a_switch() {
static_assert(base::make_span(kSwitchPrefixes).back() == L"/",
"Error: Last switch prefix is not a slash.");
switch_prefix_count = std::size(kSwitchPrefixes) - 1;
}
void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) {
DCHECK(!current_process_commandline_);
current_process_commandline_ = new CommandLine(NO_PROGRAM);
CommandLine::StringVector argv_vector;
for (int i = 0; i < argc; ++i)
argv_vector.push_back(UTF8ToWide(argv[i]));
current_process_commandline_->InitFromArgv(argv_vector);
}
#endif
bool CommandLine::Init(int argc, const char* const* argv) { … }
void CommandLine::Reset() { … }
CommandLine* CommandLine::ForCurrentProcess() { … }
bool CommandLine::InitializedForCurrentProcess() { … }
CommandLine CommandLine::FromArgvWithoutProgram(const StringVector& argv) { … }
#if BUILDFLAG(IS_WIN)
CommandLine CommandLine::FromString(StringViewType command_line) {
CommandLine cmd(NO_PROGRAM);
cmd.ParseFromString(command_line);
return cmd;
}
#endif
void CommandLine::InitFromArgv(int argc,
const CommandLine::CharType* const* argv) { … }
void CommandLine::InitFromArgv(const StringVector& argv) { … }
FilePath CommandLine::GetProgram() const { … }
void CommandLine::SetProgram(const FilePath& program) { … }
bool CommandLine::HasSwitch(std::string_view switch_string) const { … }
bool CommandLine::HasSwitch(const char switch_constant[]) const { … }
std::string CommandLine::GetSwitchValueASCII(
std::string_view switch_string) const { … }
FilePath CommandLine::GetSwitchValuePath(std::string_view switch_string) const { … }
CommandLine::StringType CommandLine::GetSwitchValueNative(
std::string_view switch_string) const { … }
void CommandLine::AppendSwitch(std::string_view switch_string) { … }
void CommandLine::AppendSwitchPath(std::string_view switch_string,
const FilePath& path) { … }
void CommandLine::AppendSwitchNative(std::string_view switch_string,
CommandLine::StringViewType value) { … }
void CommandLine::AppendSwitchASCII(std::string_view switch_string,
std::string_view value_string) { … }
void CommandLine::RemoveSwitch(std::string_view switch_key_without_prefix) { … }
void CommandLine::CopySwitchesFrom(const CommandLine& source,
span<const char* const> switches) { … }
CommandLine::StringVector CommandLine::GetArgs() const { … }
void CommandLine::AppendArg(std::string_view value) { … }
void CommandLine::AppendArgPath(const FilePath& path) { … }
void CommandLine::AppendArgNative(StringViewType value) { … }
void CommandLine::AppendArguments(const CommandLine& other,
bool include_program) { … }
void CommandLine::PrependWrapper(StringViewType wrapper) { … }
#if BUILDFLAG(IS_WIN)
void CommandLine::ParseFromString(StringViewType command_line) {
command_line = TrimWhitespace(command_line, TRIM_ALL);
if (command_line.empty())
return;
raw_command_line_string_ = command_line;
int num_args = 0;
wchar_t** args = NULL;
HMODULE downlevel_shell32_dll =
::LoadLibraryEx(L"api-ms-win-downlevel-shell32-l1-1-0.dll", nullptr,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (downlevel_shell32_dll) {
auto command_line_to_argv_w_proc =
reinterpret_cast<decltype(::CommandLineToArgvW)*>(
::GetProcAddress(downlevel_shell32_dll, "CommandLineToArgvW"));
if (command_line_to_argv_w_proc)
args = command_line_to_argv_w_proc(command_line.data(), &num_args);
} else {
args = ::CommandLineToArgvW(command_line.data(), &num_args);
}
DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
<< command_line;
StringVector argv(args, args + num_args);
InitFromArgv(argv);
raw_command_line_string_ = StringViewType();
LocalFree(args);
if (downlevel_shell32_dll)
::FreeLibrary(downlevel_shell32_dll);
}
#endif
void CommandLine::AppendSwitchesAndArguments(span<const StringType> argv) { … }
CommandLine::StringType CommandLine::GetArgumentsStringInternal(
bool allow_unsafe_insert_sequences) const { … }
CommandLine::StringType CommandLine::GetCommandLineString() const { … }
#if BUILDFLAG(IS_WIN)
std::wstring CommandLine::QuoteForCommandLineToArgvW(const std::wstring& arg) {
return QuoteForCommandLineToArgvWInternal(
arg, false);
}
CommandLine::StringType CommandLine::GetCommandLineStringForShell() const {
DCHECK(GetArgs().empty());
StringType command_line_string = GetCommandLineString();
return command_line_string + FILE_PATH_LITERAL(" ") +
StringType(kSwitchPrefixes[0]) + kSingleArgument +
FILE_PATH_LITERAL(" %1");
}
CommandLine::StringType
CommandLine::GetCommandLineStringWithUnsafeInsertSequences() const {
StringType string(argv_[0]);
string = QuoteForCommandLineToArgvWInternal(
string,
true);
StringType params(
GetArgumentsStringInternal(true));
if (!params.empty()) {
string.append(FILE_PATH_LITERAL(" "));
string.append(params);
}
return string;
}
#endif
CommandLine::StringType CommandLine::GetArgumentsString() const { … }
#if BUILDFLAG(IS_WIN)
void CommandLine::ParseAsSingleArgument(
const CommandLine::StringType& single_arg_switch) {
DCHECK(!raw_command_line_string_.empty());
argv_.resize(static_cast<size_t>(begin_args_));
const size_t single_arg_switch_position =
raw_command_line_string_.find(single_arg_switch);
DCHECK_NE(single_arg_switch_position, StringType::npos);
const size_t arg_position =
single_arg_switch_position + single_arg_switch.length() + 1;
if (arg_position >= raw_command_line_string_.length())
return;
has_single_argument_switch_ = true;
const StringViewType arg = raw_command_line_string_.substr(arg_position);
if (!arg.empty()) {
AppendArgNative(arg);
}
}
#endif
void CommandLine::DetachFromCurrentSequence() { … }
}