| // 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); |
| } |
| } |