// 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 #include #include #include "base/command_line.h" #include "base/logging.h" #include "base/singleton.h" #include "base/string_util.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. const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"}; const wchar_t CommandLine::kSwitchValueSeparator[] = L"="; 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: Data() { Init(GetCommandLineW()); } Data(const wstring& command_line) { Init(command_line); } const std::wstring& command_line_string() const { return command_line_string_; } const std::wstring& program() const { return program_; } const std::map& switches() const { return switches_; } const std::vector& 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; } // 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); } std::wstring command_line_string_; std::wstring program_; std::map switches_; std::vector loose_values_; DISALLOW_EVIL_CONSTRUCTORS(CommandLine::Data); }; CommandLine::CommandLine() : we_own_data_(false), // The Singleton class will manage it for us. data_(Singleton::get()) { } CommandLine::CommandLine(const wstring& command_line) : we_own_data_(true), data_(new Data(command_line)) { } 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::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); } }