blob: 0649ee852c6d34dd9777a6449175363105a8f163 [file] [log] [blame]
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#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_util.h"
#include "base/sys_string_conversions.h"
extern "C" {
#if defined(OS_MACOSX)
const char** NXArgv;
int NXArgc;
#elif defined(OS_LINUX)
const char** __libc_argv;
int __libc_argc;
#endif
} // extern "C"
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.
const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"};
const wchar_t CommandLine::kSwitchValueSeparator[] = 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());
}
#elif defined(OS_MACOSX)
Data() {
Init(NXArgc, NXArgv);
}
#elif defined(OS_LINUX)
Data() {
Init(__gnuc_argc, __gnuc_argv);
}
#endif
#if defined(OS_WIN)
Data(const wstring& command_line) {
Init(command_line);
}
#elif defined(OS_POSIX)
Data(const int argc, const char* argv[]) {
Init(argc, argv);
}
#endif
#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_);
for (int i = 1; i < num_args; ++i) {
wstring arg;
TrimWhitespace(args[i], TRIM_ALL, &arg);
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* argv[]) {
if (argc <= 1)
return;
program_ = base::SysNativeMBToWide(argv[0]);
command_line_string_ = program_;
for (int i = 1; i < argc; ++i) {
std::wstring arg = base::SysNativeMBToWide(argv[i]);
command_line_string_.append(L" ");
command_line_string_.append(arg);
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_;
}
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_;
DISALLOW_EVIL_CONSTRUCTORS(Data);
};
CommandLine::CommandLine()
: we_own_data_(false), // The Singleton class will manage it for us.
data_(Singleton<Data>::get()) {
}
#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* argv[])
: we_own_data_(true),
data_(new Data(argc, argv)) {
}
#endif
CommandLine::~CommandLine() {
if (we_own_data_)
delete data_;
}
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();
}
std::wstring CommandLine::program() const {
return data_->program();
}
// static
void CommandLine::AppendSwitch(wstring* command_line_string,
const wstring& switch_string) {
DCHECK(command_line_string);
command_line_string->append(L" ");
command_line_string->append(kSwitchPrefixes[0]);
command_line_string->append(switch_string);
}
// static
void CommandLine::AppendSwitchWithValue(wstring* command_line_string,
const wstring& switch_string,
const wstring& value_string) {
AppendSwitch(command_line_string, switch_string);
if (value_string.empty())
return;
command_line_string->append(kSwitchValueSeparator);
// NOTE(jhughes): If the value contains a quotation mark at one
// end but not both, you may get unusable output.
if ((value_string.find(L" ") != std::wstring::npos) &&
(value_string[0] != L'"') &&
(value_string[value_string.length() - 1] != L'"')) {
// need to provide quotes
StringAppendF(command_line_string, L"\"%s\"", value_string.c_str());
} else {
command_line_string->append(value_string);
}
}