| // Copyright (c) 2011 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" |
| |
| #include <algorithm> |
| #include <ostream> |
| |
| #include "base/basictypes.h" |
| #include "base/file_path.h" |
| #include "base/logging.h" |
| #include "base/string_split.h" |
| #include "base/string_util.h" |
| #include "base/utf_string_conversions.h" |
| #include "build/build_config.h" |
| |
| #if defined(OS_WIN) |
| #include <windows.h> |
| #include <shellapi.h> |
| #endif |
| |
| CommandLine* CommandLine::current_process_commandline_ = NULL; |
| |
| namespace { |
| typedef CommandLine::StringType::value_type CharType; |
| |
| const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); |
| const CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); |
| // Since we use a lazy match, make sure that longer versions (like "--") are |
| // listed before shorter versions (like "-") of similar prefixes. |
| #if defined(OS_WIN) |
| const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; |
| #elif defined(OS_POSIX) |
| // Unixes don't use slash as a switch. |
| const CharType* const kSwitchPrefixes[] = {"--", "-"}; |
| #endif |
| |
| #if defined(OS_WIN) |
| // Lowercase a string for case-insensitivity of switches. |
| // Is this desirable? It exists for backwards compatibility on Windows. |
| void Lowercase(std::string* arg) { |
| transform(arg->begin(), arg->end(), arg->begin(), tolower); |
| } |
| |
| // Quote a string if necessary, such that CommandLineToArgvW() will always |
| // process it as a single argument. |
| std::wstring WindowsStyleQuote(const std::wstring& arg) { |
| // We follow the quoting rules of CommandLineToArgvW. |
| // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx |
| if (arg.find_first_of(L" \\\"") == std::wstring::npos) { |
| // No quoting necessary. |
| return arg; |
| } |
| |
| std::wstring out; |
| out.push_back(L'"'); |
| for (size_t i = 0; i < arg.size(); ++i) { |
| if (arg[i] == '\\') { |
| // Find the extent of this run of backslashes. |
| size_t start = i, end = start + 1; |
| for (; end < arg.size() && arg[end] == '\\'; ++end) |
| /* empty */; |
| size_t backslash_count = end - start; |
| |
| // Backslashes are escapes only if the run is followed by a double quote. |
| // Since we also will end the string with a double quote, we escape for |
| // either a double quote or the end of the string. |
| if (end == arg.size() || arg[end] == '"') { |
| // To quote, we need to output 2x as many backslashes. |
| backslash_count *= 2; |
| } |
| for (size_t j = 0; j < backslash_count; ++j) |
| out.push_back('\\'); |
| |
| // Advance i to one before the end to balance i++ in loop. |
| i = end - 1; |
| } else if (arg[i] == '"') { |
| out.push_back('\\'); |
| out.push_back('"'); |
| } else { |
| out.push_back(arg[i]); |
| } |
| } |
| out.push_back('"'); |
| |
| return out; |
| } |
| #endif |
| |
| // Returns true and fills in |switch_string| and |switch_value| if |
| // |parameter_string| represents a switch. |
| bool IsSwitch(const CommandLine::StringType& parameter_string, |
| std::string* switch_string, |
| CommandLine::StringType* switch_value) { |
| switch_string->clear(); |
| switch_value->clear(); |
| |
| for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { |
| CommandLine::StringType prefix(kSwitchPrefixes[i]); |
| if (parameter_string.find(prefix) != 0) |
| continue; |
| |
| const size_t switch_start = prefix.length(); |
| const size_t equals_position = parameter_string.find( |
| kSwitchValueSeparator, switch_start); |
| CommandLine::StringType switch_native; |
| if (equals_position == CommandLine::StringType::npos) { |
| switch_native = parameter_string.substr(switch_start); |
| } else { |
| switch_native = parameter_string.substr( |
| switch_start, equals_position - switch_start); |
| *switch_value = parameter_string.substr(equals_position + 1); |
| } |
| #if defined(OS_WIN) |
| *switch_string = WideToASCII(switch_native); |
| Lowercase(switch_string); |
| #else |
| *switch_string = switch_native; |
| #endif |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| } // namespace |
| |
| CommandLine::CommandLine(NoProgram no_program) { |
| #if defined(OS_POSIX) |
| // Push an empty argument, because we always assume argv_[0] is a program. |
| argv_.push_back(""); |
| #endif |
| } |
| |
| CommandLine::CommandLine(const FilePath& program) { |
| #if defined(OS_WIN) |
| if (!program.empty()) { |
| program_ = program.value(); |
| // TODO(evanm): proper quoting here. |
| command_line_string_ = L'"' + program.value() + L'"'; |
| } |
| #elif defined(OS_POSIX) |
| argv_.push_back(program.value()); |
| #endif |
| } |
| |
| #if defined(OS_POSIX) |
| CommandLine::CommandLine(int argc, const char* const* argv) { |
| InitFromArgv(argc, argv); |
| } |
| |
| CommandLine::CommandLine(const StringVector& argv) { |
| InitFromArgv(argv); |
| } |
| #endif // OS_POSIX |
| |
| CommandLine::~CommandLine() { |
| } |
| |
| // static |
| void CommandLine::Init(int argc, const char* const* argv) { |
| delete current_process_commandline_; |
| current_process_commandline_ = new CommandLine; |
| #if defined(OS_WIN) |
| current_process_commandline_->ParseFromString(::GetCommandLineW()); |
| #elif defined(OS_POSIX) |
| current_process_commandline_->InitFromArgv(argc, argv); |
| #endif |
| } |
| |
| // static |
| void CommandLine::Reset() { |
| DCHECK(current_process_commandline_); |
| delete current_process_commandline_; |
| current_process_commandline_ = NULL; |
| } |
| |
| // static |
| CommandLine* CommandLine::ForCurrentProcess() { |
| DCHECK(current_process_commandline_); |
| return current_process_commandline_; |
| } |
| |
| #if defined(OS_WIN) |
| // static |
| CommandLine CommandLine::FromString(const std::wstring& command_line) { |
| CommandLine cmd; |
| cmd.ParseFromString(command_line); |
| return cmd; |
| } |
| #endif // OS_WIN |
| |
| #if defined(OS_POSIX) |
| void CommandLine::InitFromArgv(int argc, const char* const* argv) { |
| for (int i = 0; i < argc; ++i) |
| argv_.push_back(argv[i]); |
| InitFromArgv(argv_); |
| } |
| |
| void CommandLine::InitFromArgv(const StringVector& argv) { |
| argv_ = argv; |
| bool parse_switches = true; |
| for (size_t i = 1; i < argv_.size(); ++i) { |
| const std::string& arg = argv_[i]; |
| |
| if (!parse_switches) { |
| args_.push_back(arg); |
| continue; |
| } |
| |
| if (arg == kSwitchTerminator) { |
| parse_switches = false; |
| continue; |
| } |
| |
| std::string switch_string; |
| StringType switch_value; |
| if (IsSwitch(arg, &switch_string, &switch_value)) { |
| switches_[switch_string] = switch_value; |
| } else { |
| args_.push_back(arg); |
| } |
| } |
| } |
| #endif // OS_POSIX |
| |
| CommandLine::StringType CommandLine::command_line_string() const { |
| #if defined(OS_WIN) |
| return command_line_string_; |
| #elif defined(OS_POSIX) |
| return JoinString(argv_, ' '); |
| #endif |
| } |
| |
| FilePath CommandLine::GetProgram() const { |
| #if defined(OS_WIN) |
| return FilePath(program_); |
| #else |
| DCHECK_GT(argv_.size(), 0U); |
| return FilePath(argv_[0]); |
| #endif |
| } |
| |
| bool CommandLine::HasSwitch(const std::string& switch_string) const { |
| std::string lowercased_switch(switch_string); |
| #if defined(OS_WIN) |
| Lowercase(&lowercased_switch); |
| #endif |
| return switches_.find(lowercased_switch) != switches_.end(); |
| } |
| |
| std::string CommandLine::GetSwitchValueASCII( |
| const std::string& switch_string) const { |
| CommandLine::StringType value = GetSwitchValueNative(switch_string); |
| if (!IsStringASCII(value)) { |
| LOG(WARNING) << "Value of --" << switch_string << " must be ASCII."; |
| return ""; |
| } |
| #if defined(OS_WIN) |
| return WideToASCII(value); |
| #else |
| return value; |
| #endif |
| } |
| |
| FilePath CommandLine::GetSwitchValuePath( |
| const std::string& switch_string) const { |
| return FilePath(GetSwitchValueNative(switch_string)); |
| } |
| |
| CommandLine::StringType CommandLine::GetSwitchValueNative( |
| const std::string& switch_string) const { |
| std::string lowercased_switch(switch_string); |
| #if defined(OS_WIN) |
| Lowercase(&lowercased_switch); |
| #endif |
| |
| SwitchMap::const_iterator result = switches_.find(lowercased_switch); |
| |
| if (result == switches_.end()) { |
| return CommandLine::StringType(); |
| } else { |
| return result->second; |
| } |
| } |
| |
| size_t CommandLine::GetSwitchCount() const { |
| return switches_.size(); |
| } |
| |
| void CommandLine::AppendSwitch(const std::string& switch_string) { |
| #if defined(OS_WIN) |
| command_line_string_.append(L" "); |
| command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string)); |
| switches_[switch_string] = L""; |
| #elif defined(OS_POSIX) |
| argv_.push_back(kSwitchPrefixes[0] + switch_string); |
| switches_[switch_string] = ""; |
| #endif |
| } |
| |
| void CommandLine::AppendSwitchPath(const std::string& switch_string, |
| const FilePath& path) { |
| AppendSwitchNative(switch_string, path.value()); |
| } |
| |
| void CommandLine::AppendSwitchNative(const std::string& switch_string, |
| const CommandLine::StringType& value) { |
| #if defined(OS_WIN) |
| StringType combined_switch_string = |
| kSwitchPrefixes[0] + ASCIIToWide(switch_string); |
| if (!value.empty()) |
| combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value); |
| |
| command_line_string_.append(L" "); |
| command_line_string_.append(combined_switch_string); |
| |
| switches_[switch_string] = value; |
| #elif defined(OS_POSIX) |
| StringType combined_switch_string = kSwitchPrefixes[0] + switch_string; |
| if (!value.empty()) |
| combined_switch_string += kSwitchValueSeparator + value; |
| argv_.push_back(combined_switch_string); |
| switches_[switch_string] = value; |
| #endif |
| } |
| |
| void CommandLine::AppendSwitchASCII(const std::string& switch_string, |
| const std::string& value_string) { |
| #if defined(OS_WIN) |
| AppendSwitchNative(switch_string, ASCIIToWide(value_string)); |
| #elif defined(OS_POSIX) |
| AppendSwitchNative(switch_string, value_string); |
| #endif |
| } |
| |
| void CommandLine::AppendSwitches(const CommandLine& other) { |
| SwitchMap::const_iterator i; |
| for (i = other.switches_.begin(); i != other.switches_.end(); ++i) |
| AppendSwitchNative(i->first, i->second); |
| } |
| |
| void CommandLine::CopySwitchesFrom(const CommandLine& source, |
| const char* const switches[], |
| size_t count) { |
| for (size_t i = 0; i < count; ++i) { |
| if (source.HasSwitch(switches[i])) { |
| StringType value = source.GetSwitchValueNative(switches[i]); |
| AppendSwitchNative(switches[i], value); |
| } |
| } |
| } |
| |
| void CommandLine::AppendArg(const std::string& value) { |
| #if defined(OS_WIN) |
| DCHECK(IsStringUTF8(value)); |
| AppendArgNative(UTF8ToWide(value)); |
| #elif defined(OS_POSIX) |
| AppendArgNative(value); |
| #endif |
| } |
| |
| void CommandLine::AppendArgPath(const FilePath& path) { |
| AppendArgNative(path.value()); |
| } |
| |
| void CommandLine::AppendArgNative(const CommandLine::StringType& value) { |
| #if defined(OS_WIN) |
| command_line_string_.append(L" "); |
| command_line_string_.append(WindowsStyleQuote(value)); |
| args_.push_back(value); |
| #elif defined(OS_POSIX) |
| DCHECK(IsStringUTF8(value)); |
| argv_.push_back(value); |
| #endif |
| } |
| |
| void CommandLine::AppendArgs(const CommandLine& other) { |
| if(other.args_.size() <= 0) |
| return; |
| #if defined(OS_WIN) |
| command_line_string_.append(L" --"); |
| #endif // OS_WIN |
| StringVector::const_iterator i; |
| for (i = other.args_.begin(); i != other.args_.end(); ++i) |
| AppendArgNative(*i); |
| } |
| |
| void CommandLine::AppendArguments(const CommandLine& other, |
| bool include_program) { |
| #if defined(OS_WIN) |
| // Verify include_program is used correctly. |
| DCHECK(!include_program || !other.GetProgram().empty()); |
| if (include_program) |
| program_ = other.program_; |
| |
| if (!command_line_string_.empty()) |
| command_line_string_ += L' '; |
| |
| command_line_string_ += other.command_line_string_; |
| #elif defined(OS_POSIX) |
| // Verify include_program is used correctly. |
| // Logic could be shorter but this is clearer. |
| DCHECK_EQ(include_program, !other.GetProgram().empty()); |
| |
| if (include_program) |
| argv_[0] = other.argv_[0]; |
| |
| // Skip the first arg when copying since it's the program but push all |
| // arguments to our arg vector. |
| for (size_t i = 1; i < other.argv_.size(); ++i) |
| argv_.push_back(other.argv_[i]); |
| #endif |
| |
| SwitchMap::const_iterator i; |
| for (i = other.switches_.begin(); i != other.switches_.end(); ++i) |
| switches_[i->first] = i->second; |
| } |
| |
| void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { |
| // The wrapper may have embedded arguments (like "gdb --args"). In this case, |
| // we don't pretend to do anything fancy, we just split on spaces. |
| if (wrapper.empty()) |
| return; |
| StringVector wrapper_and_args; |
| #if defined(OS_WIN) |
| base::SplitString(wrapper, ' ', &wrapper_and_args); |
| program_ = wrapper_and_args[0]; |
| command_line_string_ = wrapper + L" " + command_line_string_; |
| #elif defined(OS_POSIX) |
| base::SplitString(wrapper, ' ', &wrapper_and_args); |
| argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); |
| #endif |
| } |
| |
| #if defined(OS_WIN) |
| void CommandLine::ParseFromString(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) { |
| std::wstring arg; |
| TrimWhitespace(args[i], TRIM_ALL, &arg); |
| |
| if (!parse_switches) { |
| args_.push_back(arg); |
| continue; |
| } |
| |
| if (arg == kSwitchTerminator) { |
| parse_switches = false; |
| continue; |
| } |
| |
| std::string switch_string; |
| std::wstring switch_value; |
| if (IsSwitch(arg, &switch_string, &switch_value)) { |
| switches_[switch_string] = switch_value; |
| } else { |
| args_.push_back(arg); |
| } |
| } |
| |
| if (args) |
| LocalFree(args); |
| } |
| #endif |
| |
| CommandLine::CommandLine() { |
| } |