blob: 6e02e1d8aa9d14ea0f2db1fabf3ece63630b815f [file] [log] [blame]
// Copyright (c) 2006-2008 The Chromium 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 "base/command_line.h"
#if defined(OS_WIN)
#include <windows.h>
#include <shellapi.h>
#endif
#include <algorithm>
#include "base/logging.h"
#include "base/singleton.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
using namespace std;
// Since we use a lazy match, make sure that longer versions (like L"--")
// are listed before shorter versions (like L"-") of similar prefixes.
#if defined(OS_WIN)
const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"};
#elif defined(OS_POSIX)
// Unixes don't use slash as a switch.
const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-"};
#endif
const wchar_t CommandLine::kSwitchValueSeparator[] = L"=";
const wchar_t CommandLine::kSwitchTerminator[] = L"--";
// Needed to avoid a typecast on the tolower() function pointer in Lowercase().
// MSVC accepts it as-is but GCC requires the typecast.
static int ToLower(int c) {
return tolower(c);
}
static void Lowercase(wstring* parameter) {
transform(parameter->begin(), parameter->end(), parameter->begin(),
ToLower);
}
// CommandLine::Data
//
// This object holds the parsed data for a command line. We hold this in a
// separate object from |CommandLine| so that we can share the parsed data
// across multiple |CommandLine| objects. When we share |Data|, we might be
// accessing this object on multiple threads. To ensure thread safety, the
// public interface of this object is const only.
//
// Do NOT add any non-const methods to this object. You have been warned.
class CommandLine::Data {
public:
#if defined(OS_WIN)
Data() {
Init(GetCommandLineW());
}
Data(const wstring& command_line) {
Init(command_line);
}
#elif defined(OS_POSIX)
Data() {
// Owner must call Init().
}
Data(int argc, const char* const* argv) {
Init(argc, argv);
}
#endif // defined(OS_POSIX)
#if defined(OS_WIN)
// Does the actual parsing of the command line.
void Init(const std::wstring& command_line) {
TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
if (command_line_string_.empty())
return;
int num_args = 0;
wchar_t** args = NULL;
args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
// Populate program_ with the trimmed version of the first arg.
TrimWhitespace(args[0], TRIM_ALL, &program_);
bool parse_switches = true;
for (int i = 1; i < num_args; ++i) {
wstring arg;
TrimWhitespace(args[i], TRIM_ALL, &arg);
if (!parse_switches) {
loose_values_.push_back(arg);
continue;
}
if (arg == kSwitchTerminator) {
parse_switches = false;
continue;
}
wstring switch_string;
wstring switch_value;
if (IsSwitch(arg, &switch_string, &switch_value)) {
switches_[switch_string] = switch_value;
} else {
loose_values_.push_back(arg);
}
}
if (args)
LocalFree(args);
}
#elif defined(OS_POSIX)
// Does the actual parsing of the command line.
void Init(int argc, const char* const* argv) {
if (argc < 1)
return;
program_ = base::SysNativeMBToWide(argv[0]);
argv_.push_back(std::string(argv[0]));
command_line_string_ = program_;
bool parse_switches = true;
for (int i = 1; i < argc; ++i) {
std::wstring arg = base::SysNativeMBToWide(argv[i]);
argv_.push_back(argv[i]);
command_line_string_.append(L" ");
command_line_string_.append(arg);
if (!parse_switches) {
loose_values_.push_back(arg);
continue;
}
if (arg == kSwitchTerminator) {
parse_switches = false;
continue;
}
wstring switch_string;
wstring switch_value;
if (IsSwitch(arg, &switch_string, &switch_value)) {
switches_[switch_string] = switch_value;
} else {
loose_values_.push_back(arg);
}
}
}
#endif
const std::wstring& command_line_string() const {
return command_line_string_;
}
const std::wstring& program() const {
return program_;
}
const std::map<std::wstring, std::wstring>& switches() const {
return switches_;
}
const std::vector<std::wstring>& loose_values() const {
return loose_values_;
}
#if defined(OS_POSIX)
const std::vector<std::string>& argv() const {
return argv_;
}
#endif
private:
// Returns true if parameter_string represents a switch. If true,
// switch_string and switch_value are set. (If false, both are
// set to the empty string.)
static bool IsSwitch(const wstring& parameter_string,
wstring* switch_string,
wstring* switch_value) {
*switch_string = L"";
*switch_value = L"";
for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
std::wstring prefix(kSwitchPrefixes[i]);
if (parameter_string.find(prefix) != 0) // check prefix
continue;
const size_t switch_start = prefix.length();
const size_t equals_position = parameter_string.find(
kSwitchValueSeparator, switch_start);
if (equals_position == wstring::npos) {
*switch_string = parameter_string.substr(switch_start);
} else {
*switch_string = parameter_string.substr(
switch_start, equals_position - switch_start);
*switch_value = parameter_string.substr(equals_position + 1);
}
Lowercase(switch_string);
return true;
}
return false;
}
std::wstring command_line_string_;
std::wstring program_;
std::map<std::wstring, std::wstring> switches_;
std::vector<std::wstring> loose_values_;
std::vector<std::string> argv_;
DISALLOW_EVIL_CONSTRUCTORS(Data);
};
CommandLine::CommandLine()
: we_own_data_(false), // The Singleton class will manage it for us.
data_(Singleton<Data>::get()) {
DCHECK(!data_->command_line_string().empty()) <<
"You must call CommandLine::SetArgcArgv before making any CommandLine "
"calls.";
}
#if defined(OS_WIN)
CommandLine::CommandLine(const wstring& command_line)
: we_own_data_(true),
data_(new Data(command_line)) {
}
#elif defined(OS_POSIX)
CommandLine::CommandLine(const int argc, const char* const* argv)
: we_own_data_(true),
data_(new Data(argc, argv)) {
}
CommandLine::CommandLine(const std::vector<std::string>& argv)
: we_own_data_(true) {
const char* argv_copy[argv.size()];
for (size_t i = 0; i < argv.size(); i++) {
argv_copy[i] = argv[i].c_str();
}
data_ = new Data(argv.size(), argv_copy);
}
#endif
CommandLine::~CommandLine() {
if (we_own_data_)
delete data_;
}
// static
void CommandLine::SetArgcArgv(int argc, const char* const* argv) {
#if !defined(OS_WIN)
Singleton<Data>::get()->Init(argc, argv);
#endif
}
bool CommandLine::HasSwitch(const wstring& switch_string) const {
wstring lowercased_switch(switch_string);
Lowercase(&lowercased_switch);
return data_->switches().find(lowercased_switch) != data_->switches().end();
}
wstring CommandLine::GetSwitchValue(const wstring& switch_string) const {
wstring lowercased_switch(switch_string);
Lowercase(&lowercased_switch);
const map<wstring, wstring>::const_iterator result =
data_->switches().find(lowercased_switch);
if (result == data_->switches().end()) {
return L"";
} else {
return result->second;
}
}
size_t CommandLine::GetLooseValueCount() const {
return data_->loose_values().size();
}
CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const {
return data_->loose_values().begin();
}
CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const {
return data_->loose_values().end();
}
std::wstring CommandLine::command_line_string() const {
return data_->command_line_string();
}
#if defined(OS_POSIX)
const std::vector<std::string>& CommandLine::argv() const {
return data_->argv();
}
#endif
std::wstring CommandLine::program() const {
return data_->program();
}
// static
wstring CommandLine::PrefixedSwitchString(const wstring& switch_string) {
return StringPrintf(L"%ls%ls",
kSwitchPrefixes[0],
switch_string.c_str());
}
// static
wstring CommandLine::PrefixedSwitchStringWithValue(
const wstring& switch_string, const wstring& value_string) {
if (value_string.empty()) {
return PrefixedSwitchString(switch_string);
}
return StringPrintf(L"%ls%ls%ls%ls",
kSwitchPrefixes[0],
switch_string.c_str(),
kSwitchValueSeparator,
value_string.c_str());
}
// static
void CommandLine::AppendSwitch(wstring* command_line_string,
const wstring& switch_string) {
DCHECK(command_line_string);
wstring prefixed_switch_string = PrefixedSwitchString(switch_string);
command_line_string->append(L" ");
command_line_string->append(prefixed_switch_string);
}
// static
void CommandLine::AppendSwitchWithValue(wstring* command_line_string,
const wstring& switch_string,
const wstring& value_string) {
wstring value_string_edit;
// NOTE(jhughes): If the value contains a quotation mark at one
// end but not both, you may get unusable output.
if (!value_string.empty() &&
(value_string.find(L" ") != std::wstring::npos) &&
(value_string[0] != L'"') &&
(value_string[value_string.length() - 1] != L'"')) {
// need to provide quotes
value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str());
} else {
value_string_edit = value_string;
}
wstring combined_switch_string =
PrefixedSwitchStringWithValue(switch_string, value_string_edit);
command_line_string->append(L" ");
command_line_string->append(combined_switch_string);
}