michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "base/command_line.h" michael@0: michael@0: #if defined(OS_WIN) michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: #include "base/logging.h" michael@0: #include "base/singleton.h" michael@0: #include "base/string_piece.h" michael@0: #include "base/string_util.h" michael@0: #include "base/sys_string_conversions.h" michael@0: michael@0: CommandLine* CommandLine::current_process_commandline_ = NULL; michael@0: michael@0: // Since we use a lazy match, make sure that longer versions (like L"--") michael@0: // are listed before shorter versions (like L"-") of similar prefixes. michael@0: #if defined(OS_WIN) michael@0: const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; michael@0: const wchar_t kSwitchTerminator[] = L"--"; michael@0: const wchar_t kSwitchValueSeparator[] = L"="; michael@0: #elif defined(OS_POSIX) michael@0: // Unixes don't use slash as a switch. michael@0: const char* const kSwitchPrefixes[] = {"--", "-"}; michael@0: const char kSwitchTerminator[] = "--"; michael@0: const char kSwitchValueSeparator[] = "="; michael@0: #endif michael@0: michael@0: #if defined(OS_WIN) michael@0: // Lowercase a string. This is used to lowercase switch names. michael@0: // Is this what we really want? It seems crazy to me. I've left it in michael@0: // for backwards compatibility on Windows. michael@0: static void Lowercase(std::wstring* parameter) { michael@0: transform(parameter->begin(), parameter->end(), parameter->begin(), michael@0: tolower); michael@0: } michael@0: #endif michael@0: michael@0: #if defined(OS_WIN) michael@0: void CommandLine::ParseFromString(const std::wstring& command_line) { michael@0: TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); michael@0: michael@0: if (command_line_string_.empty()) michael@0: return; michael@0: michael@0: int num_args = 0; michael@0: wchar_t** args = NULL; michael@0: michael@0: args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); michael@0: michael@0: // Populate program_ with the trimmed version of the first arg. michael@0: TrimWhitespace(args[0], TRIM_ALL, &program_); michael@0: michael@0: bool parse_switches = true; michael@0: for (int i = 1; i < num_args; ++i) { michael@0: std::wstring arg; michael@0: TrimWhitespace(args[i], TRIM_ALL, &arg); michael@0: michael@0: if (!parse_switches) { michael@0: loose_values_.push_back(arg); michael@0: continue; michael@0: } michael@0: michael@0: if (arg == kSwitchTerminator) { michael@0: parse_switches = false; michael@0: continue; michael@0: } michael@0: michael@0: std::string switch_string; michael@0: std::wstring switch_value; michael@0: if (IsSwitch(arg, &switch_string, &switch_value)) { michael@0: switches_[switch_string] = switch_value; michael@0: } else { michael@0: loose_values_.push_back(arg); michael@0: } michael@0: } michael@0: michael@0: if (args) michael@0: LocalFree(args); michael@0: } michael@0: CommandLine::CommandLine(const std::wstring& program) { michael@0: if (!program.empty()) { michael@0: program_ = program; michael@0: command_line_string_ = L'"' + program + L'"'; michael@0: } michael@0: } michael@0: #elif defined(OS_POSIX) michael@0: CommandLine::CommandLine(int argc, const char* const* argv) { michael@0: for (int i = 0; i < argc; ++i) michael@0: argv_.push_back(argv[i]); michael@0: InitFromArgv(); michael@0: } michael@0: CommandLine::CommandLine(const std::vector& argv) { michael@0: argv_ = argv; michael@0: InitFromArgv(); michael@0: } michael@0: michael@0: void CommandLine::InitFromArgv() { michael@0: bool parse_switches = true; michael@0: for (size_t i = 1; i < argv_.size(); ++i) { michael@0: const std::string& arg = argv_[i]; michael@0: michael@0: if (!parse_switches) { michael@0: loose_values_.push_back(arg); michael@0: continue; michael@0: } michael@0: michael@0: if (arg == kSwitchTerminator) { michael@0: parse_switches = false; michael@0: continue; michael@0: } michael@0: michael@0: std::string switch_string; michael@0: std::string switch_value; michael@0: if (IsSwitch(arg, &switch_string, &switch_value)) { michael@0: switches_[switch_string] = switch_value; michael@0: } else { michael@0: loose_values_.push_back(arg); michael@0: } michael@0: } michael@0: } michael@0: michael@0: CommandLine::CommandLine(const std::wstring& program) { michael@0: argv_.push_back(WideToASCII(program)); michael@0: } michael@0: #endif michael@0: michael@0: // static michael@0: bool CommandLine::IsSwitch(const StringType& parameter_string, michael@0: std::string* switch_string, michael@0: StringType* switch_value) { michael@0: switch_string->clear(); michael@0: switch_value->clear(); michael@0: michael@0: for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { michael@0: StringType prefix(kSwitchPrefixes[i]); michael@0: if (parameter_string.find(prefix) != 0) michael@0: continue; michael@0: michael@0: const size_t switch_start = prefix.length(); michael@0: const size_t equals_position = parameter_string.find( michael@0: kSwitchValueSeparator, switch_start); michael@0: StringType switch_native; michael@0: if (equals_position == StringType::npos) { michael@0: switch_native = parameter_string.substr(switch_start); michael@0: } else { michael@0: switch_native = parameter_string.substr( michael@0: switch_start, equals_position - switch_start); michael@0: *switch_value = parameter_string.substr(equals_position + 1); michael@0: } michael@0: #if defined(OS_WIN) michael@0: Lowercase(&switch_native); michael@0: *switch_string = WideToASCII(switch_native); michael@0: #else michael@0: *switch_string = switch_native; michael@0: #endif michael@0: michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // static michael@0: void CommandLine::Init(int argc, const char* const* argv) { michael@0: DCHECK(current_process_commandline_ == NULL); michael@0: #if defined(OS_WIN) michael@0: current_process_commandline_ = new CommandLine; michael@0: current_process_commandline_->ParseFromString(::GetCommandLineW()); michael@0: #elif defined(OS_POSIX) michael@0: current_process_commandline_ = new CommandLine(argc, argv); michael@0: #endif michael@0: } michael@0: michael@0: void CommandLine::Terminate() { michael@0: DCHECK(current_process_commandline_ != NULL); michael@0: delete current_process_commandline_; michael@0: current_process_commandline_ = NULL; michael@0: } michael@0: michael@0: bool CommandLine::HasSwitch(const std::wstring& switch_string) const { michael@0: std::wstring lowercased_switch(switch_string); michael@0: #if defined(OS_WIN) michael@0: Lowercase(&lowercased_switch); michael@0: #endif michael@0: return switches_.find(WideToASCII(lowercased_switch)) != switches_.end(); michael@0: } michael@0: michael@0: std::wstring CommandLine::GetSwitchValue( michael@0: const std::wstring& switch_string) const { michael@0: std::wstring lowercased_switch(switch_string); michael@0: #if defined(OS_WIN) michael@0: Lowercase(&lowercased_switch); michael@0: #endif michael@0: michael@0: std::map::const_iterator result = michael@0: switches_.find(WideToASCII(lowercased_switch)); michael@0: michael@0: if (result == switches_.end()) { michael@0: return L""; michael@0: } else { michael@0: #if defined(OS_WIN) michael@0: return result->second; michael@0: #else michael@0: return ASCIIToWide(result->second); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: #if defined(OS_WIN) michael@0: std::vector CommandLine::GetLooseValues() const { michael@0: return loose_values_; michael@0: } michael@0: std::wstring CommandLine::program() const { michael@0: return program_; michael@0: } michael@0: #else michael@0: std::vector CommandLine::GetLooseValues() const { michael@0: std::vector values; michael@0: for (size_t i = 0; i < loose_values_.size(); ++i) michael@0: values.push_back(ASCIIToWide(loose_values_[i])); michael@0: return values; michael@0: } michael@0: std::wstring CommandLine::program() const { michael@0: DCHECK(argv_.size() > 0); michael@0: return ASCIIToWide(argv_[0]); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: // static michael@0: std::wstring CommandLine::PrefixedSwitchString( michael@0: const std::wstring& switch_string) { michael@0: return StringPrintf(L"%ls%ls", michael@0: kSwitchPrefixes[0], michael@0: switch_string.c_str()); michael@0: } michael@0: michael@0: // static michael@0: std::wstring CommandLine::PrefixedSwitchStringWithValue( michael@0: const std::wstring& switch_string, const std::wstring& value_string) { michael@0: if (value_string.empty()) { michael@0: return PrefixedSwitchString(switch_string); michael@0: } michael@0: michael@0: return StringPrintf(L"%ls%ls%ls%ls", michael@0: kSwitchPrefixes[0], michael@0: switch_string.c_str(), michael@0: kSwitchValueSeparator, michael@0: value_string.c_str()); michael@0: } michael@0: michael@0: #if defined(OS_WIN) michael@0: void CommandLine::AppendSwitch(const std::wstring& switch_string) { michael@0: std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string); michael@0: command_line_string_.append(L" "); michael@0: command_line_string_.append(prefixed_switch_string); michael@0: switches_[WideToASCII(switch_string)] = L""; michael@0: } michael@0: michael@0: // Quote a string if necessary, such that CommandLineToArgvW() will michael@0: // always process it as a single argument. michael@0: static std::wstring WindowsStyleQuote(const std::wstring& arg) { michael@0: // We follow the quoting rules of CommandLineToArgvW. michael@0: // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx michael@0: if (arg.find_first_of(L" \\\"\t") == std::wstring::npos) { michael@0: // No quoting necessary. michael@0: return arg; michael@0: } michael@0: michael@0: std::wstring out; michael@0: out.push_back(L'"'); michael@0: for (size_t i = 0; i < arg.size(); ++i) { michael@0: if (arg[i] == '\\') { michael@0: // Find the extent of this run of backslashes. michael@0: size_t start = i, end = start + 1; michael@0: for (; end < arg.size() && arg[end] == '\\'; ++end) michael@0: /* empty */; michael@0: size_t backslash_count = end - start; michael@0: michael@0: // Backslashes are escapes only if the run is followed by a double quote. michael@0: // Since we also will end the string with a double quote, we escape for michael@0: // either a double quote or the end of the string. michael@0: if (end == arg.size() || arg[end] == '"') { michael@0: // To quote, we need to output 2x as many backslashes. michael@0: backslash_count *= 2; michael@0: } michael@0: for (size_t j = 0; j < backslash_count; ++j) michael@0: out.push_back('\\'); michael@0: michael@0: // Advance i to one before the end to balance i++ in loop. michael@0: i = end - 1; michael@0: } else if (arg[i] == '"') { michael@0: out.push_back('\\'); michael@0: out.push_back('"'); michael@0: } else { michael@0: out.push_back(arg[i]); michael@0: } michael@0: } michael@0: out.push_back('"'); michael@0: michael@0: return out; michael@0: } michael@0: michael@0: void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, michael@0: const std::wstring& value_string) { michael@0: std::wstring quoted_value_string = WindowsStyleQuote(value_string); michael@0: std::wstring combined_switch_string = michael@0: PrefixedSwitchStringWithValue(switch_string, quoted_value_string); michael@0: michael@0: command_line_string_.append(L" "); michael@0: command_line_string_.append(combined_switch_string); michael@0: michael@0: switches_[WideToASCII(switch_string)] = value_string; michael@0: } michael@0: michael@0: void CommandLine::AppendLooseValue(const std::wstring& value) { michael@0: command_line_string_.append(L" "); michael@0: command_line_string_.append(WindowsStyleQuote(value)); michael@0: } michael@0: michael@0: void CommandLine::AppendArguments(const CommandLine& other, michael@0: bool include_program) { michael@0: // Verify include_program is used correctly. michael@0: // Logic could be shorter but this is clearer. michael@0: DCHECK(include_program ? !other.program().empty() : other.program().empty()); michael@0: command_line_string_ += L" " + other.command_line_string_; michael@0: std::map::const_iterator i; michael@0: for (i = other.switches_.begin(); i != other.switches_.end(); ++i) michael@0: switches_[i->first] = i->second; michael@0: } michael@0: michael@0: void CommandLine::PrependWrapper(const std::wstring& wrapper) { michael@0: // The wrapper may have embedded arguments (like "gdb --args"). In this case, michael@0: // we don't pretend to do anything fancy, we just split on spaces. michael@0: std::vector wrapper_and_args; michael@0: SplitString(wrapper, ' ', &wrapper_and_args); michael@0: program_ = wrapper_and_args[0]; michael@0: command_line_string_ = wrapper + L" " + command_line_string_; michael@0: } michael@0: michael@0: #elif defined(OS_POSIX) michael@0: void CommandLine::AppendSwitch(const std::wstring& switch_string) { michael@0: std::string ascii_switch = WideToASCII(switch_string); michael@0: argv_.push_back(kSwitchPrefixes[0] + ascii_switch); michael@0: switches_[ascii_switch] = ""; michael@0: } michael@0: michael@0: void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, michael@0: const std::wstring& value_string) { michael@0: std::string ascii_switch = WideToASCII(switch_string); michael@0: std::string ascii_value = WideToASCII(value_string); michael@0: michael@0: argv_.push_back(kSwitchPrefixes[0] + ascii_switch + michael@0: kSwitchValueSeparator + ascii_value); michael@0: switches_[ascii_switch] = ascii_value; michael@0: } michael@0: michael@0: void CommandLine::AppendLooseValue(const std::wstring& value) { michael@0: argv_.push_back(WideToASCII(value)); michael@0: } michael@0: michael@0: void CommandLine::AppendArguments(const CommandLine& other, michael@0: bool include_program) { michael@0: // Verify include_program is used correctly. michael@0: // Logic could be shorter but this is clearer. michael@0: DCHECK(include_program ? !other.program().empty() : other.program().empty()); michael@0: michael@0: size_t first_arg = include_program ? 0 : 1; michael@0: for (size_t i = first_arg; i < other.argv_.size(); ++i) michael@0: argv_.push_back(other.argv_[i]); michael@0: std::map::const_iterator i; michael@0: for (i = other.switches_.begin(); i != other.switches_.end(); ++i) michael@0: switches_[i->first] = i->second; michael@0: } michael@0: michael@0: void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) { michael@0: // The wrapper may have embedded arguments (like "gdb --args"). In this case, michael@0: // we don't pretend to do anything fancy, we just split on spaces. michael@0: const std::string wrapper = WideToASCII(wrapper_wide); michael@0: std::vector wrapper_and_args; michael@0: SplitString(wrapper, ' ', &wrapper_and_args); michael@0: argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); michael@0: } michael@0: michael@0: #endif