initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 1 | // Copyright 2008, Google Inc. |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are |
| 6 | // met: |
| 7 | // |
| 8 | // * Redistributions of source code must retain the above copyright |
| 9 | // notice, this list of conditions and the following disclaimer. |
| 10 | // * Redistributions in binary form must reproduce the above |
| 11 | // copyright notice, this list of conditions and the following disclaimer |
| 12 | // in the documentation and/or other materials provided with the |
| 13 | // distribution. |
| 14 | // * Neither the name of Google Inc. nor the names of its |
| 15 | // contributors may be used to endorse or promote products derived from |
| 16 | // this software without specific prior written permission. |
| 17 | // |
| 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 30 | #include "base/command_line.h" |
| 31 | |
| 32 | #if defined(OS_WIN) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 33 | #include <windows.h> |
| 34 | #include <shellapi.h> |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 35 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 36 | |
| 37 | #include <algorithm> |
| 38 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 39 | #include "base/logging.h" |
| 40 | #include "base/singleton.h" |
| 41 | #include "base/string_util.h" |
pinkerton@google.com | d5fb8e9 | 2008-08-08 07:28:24 +0900 | [diff] [blame^] | 42 | #include "base/sys_string_conversions.h" |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 43 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 44 | extern "C" { |
| 45 | #if defined(OS_MACOSX) |
| 46 | const char** NXArgv; |
| 47 | int NXArgc; |
| 48 | #elif defined(OS_LINUX) |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 49 | const char** __libc_argv; |
pinkerton@google.com | d5fb8e9 | 2008-08-08 07:28:24 +0900 | [diff] [blame^] | 50 | int __libc_argc; |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 51 | #endif |
| 52 | } // extern "C" |
| 53 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 54 | using namespace std; |
| 55 | |
| 56 | // Since we use a lazy match, make sure that longer versions (like L"--") |
| 57 | // are listed before shorter versions (like L"-") of similar prefixes. |
| 58 | const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"}; |
| 59 | |
| 60 | const wchar_t CommandLine::kSwitchValueSeparator[] = L"="; |
| 61 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 62 | // Needed to avoid a typecast on the tolower() function pointer in Lowercase(). |
| 63 | // MSVC accepts it as-is but GCC requires the typecast. |
| 64 | static int ToLower(int c) { |
| 65 | return tolower(c); |
| 66 | } |
| 67 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 68 | static void Lowercase(wstring* parameter) { |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 69 | transform(parameter->begin(), parameter->end(), parameter->begin(), |
| 70 | ToLower); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 71 | } |
| 72 | |
| 73 | // CommandLine::Data |
| 74 | // |
| 75 | // This object holds the parsed data for a command line. We hold this in a |
| 76 | // separate object from |CommandLine| so that we can share the parsed data |
| 77 | // across multiple |CommandLine| objects. When we share |Data|, we might be |
| 78 | // accessing this object on multiple threads. To ensure thread safety, the |
| 79 | // public interface of this object is const only. |
| 80 | // |
| 81 | // Do NOT add any non-const methods to this object. You have been warned. |
| 82 | class CommandLine::Data { |
| 83 | public: |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 84 | #if defined(OS_WIN) |
| 85 | Data() { |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 86 | Init(GetCommandLineW()); |
| 87 | } |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 88 | #elif defined(OS_MACOSX) |
| 89 | Data() { |
| 90 | Init(NXArgc, NXArgv); |
| 91 | } |
| 92 | #elif defined(OS_LINUX) |
| 93 | Data() { |
| 94 | Init(__gnuc_argc, __gnuc_argv); |
| 95 | } |
| 96 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 97 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 98 | #if defined(OS_WIN) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 99 | Data(const wstring& command_line) { |
| 100 | Init(command_line); |
| 101 | } |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 102 | #elif defined(OS_POSIX) |
| 103 | Data(const int argc, const char* argv[]) { |
| 104 | Init(argc, argv); |
| 105 | } |
| 106 | #endif |
| 107 | |
| 108 | #if defined(OS_WIN) |
| 109 | // Does the actual parsing of the command line. |
| 110 | void Init(const std::wstring& command_line) { |
| 111 | TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); |
| 112 | |
| 113 | if (command_line_string_.empty()) |
| 114 | return; |
| 115 | |
| 116 | int num_args = 0; |
| 117 | wchar_t** args = NULL; |
| 118 | |
| 119 | args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); |
| 120 | |
| 121 | // Populate program_ with the trimmed version of the first arg. |
| 122 | TrimWhitespace(args[0], TRIM_ALL, &program_); |
| 123 | |
| 124 | for (int i = 1; i < num_args; ++i) { |
| 125 | wstring arg; |
| 126 | TrimWhitespace(args[i], TRIM_ALL, &arg); |
| 127 | |
| 128 | wstring switch_string; |
| 129 | wstring switch_value; |
| 130 | if (IsSwitch(arg, &switch_string, &switch_value)) { |
| 131 | switches_[switch_string] = switch_value; |
| 132 | } else { |
| 133 | loose_values_.push_back(arg); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | if (args) |
| 138 | LocalFree(args); |
| 139 | } |
| 140 | #elif defined(OS_POSIX) // Does the actual parsing of the command line. |
| 141 | void Init(int argc, const char* argv[]) { |
| 142 | if (argc <= 1) |
| 143 | return; |
| 144 | |
pinkerton@google.com | d5fb8e9 | 2008-08-08 07:28:24 +0900 | [diff] [blame^] | 145 | program_ = base::SysNativeMBToWide(argv[0]); |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 146 | command_line_string_ = program_; |
| 147 | |
| 148 | for (int i = 1; i < argc; ++i) { |
pinkerton@google.com | d5fb8e9 | 2008-08-08 07:28:24 +0900 | [diff] [blame^] | 149 | std::wstring arg = base::SysNativeMBToWide(argv[i]); |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 150 | command_line_string_.append(L" "); |
| 151 | command_line_string_.append(arg); |
| 152 | |
| 153 | wstring switch_string; |
| 154 | wstring switch_value; |
| 155 | if (IsSwitch(arg, &switch_string, &switch_value)) { |
| 156 | switches_[switch_string] = switch_value; |
| 157 | } else { |
| 158 | loose_values_.push_back(arg); |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 163 | |
| 164 | const std::wstring& command_line_string() const { |
| 165 | return command_line_string_; |
| 166 | } |
| 167 | |
| 168 | const std::wstring& program() const { |
| 169 | return program_; |
| 170 | } |
| 171 | |
| 172 | const std::map<std::wstring, std::wstring>& switches() const { |
| 173 | return switches_; |
| 174 | } |
| 175 | |
| 176 | const std::vector<std::wstring>& loose_values() const { |
| 177 | return loose_values_; |
| 178 | } |
| 179 | |
| 180 | private: |
| 181 | // Returns true if parameter_string represents a switch. If true, |
| 182 | // switch_string and switch_value are set. (If false, both are |
| 183 | // set to the empty string.) |
| 184 | static bool IsSwitch(const wstring& parameter_string, |
| 185 | wstring* switch_string, |
| 186 | wstring* switch_value) { |
| 187 | |
| 188 | *switch_string = L""; |
| 189 | *switch_value = L""; |
| 190 | |
| 191 | for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { |
| 192 | std::wstring prefix(kSwitchPrefixes[i]); |
| 193 | if (parameter_string.find(prefix) != 0) // check prefix |
| 194 | continue; |
| 195 | |
| 196 | const size_t switch_start = prefix.length(); |
| 197 | const size_t equals_position = parameter_string.find( |
| 198 | kSwitchValueSeparator, switch_start); |
| 199 | if (equals_position == wstring::npos) { |
| 200 | *switch_string = parameter_string.substr(switch_start); |
| 201 | } else { |
| 202 | *switch_string = parameter_string.substr( |
| 203 | switch_start, equals_position - switch_start); |
| 204 | *switch_value = parameter_string.substr(equals_position + 1); |
| 205 | } |
| 206 | Lowercase(switch_string); |
| 207 | |
| 208 | return true; |
| 209 | } |
| 210 | |
| 211 | return false; |
| 212 | } |
| 213 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 214 | std::wstring command_line_string_; |
| 215 | std::wstring program_; |
| 216 | std::map<std::wstring, std::wstring> switches_; |
| 217 | std::vector<std::wstring> loose_values_; |
| 218 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 219 | DISALLOW_EVIL_CONSTRUCTORS(Data); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 220 | }; |
| 221 | |
| 222 | CommandLine::CommandLine() |
| 223 | : we_own_data_(false), // The Singleton class will manage it for us. |
| 224 | data_(Singleton<Data>::get()) { |
| 225 | } |
| 226 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 227 | #if defined(OS_WIN) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 228 | CommandLine::CommandLine(const wstring& command_line) |
| 229 | : we_own_data_(true), |
| 230 | data_(new Data(command_line)) { |
| 231 | } |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 232 | #elif defined(OS_POSIX) |
| 233 | CommandLine::CommandLine(const int argc, const char* argv[]) |
| 234 | : we_own_data_(true), |
| 235 | data_(new Data(argc, argv)) { |
| 236 | } |
| 237 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 238 | |
| 239 | CommandLine::~CommandLine() { |
| 240 | if (we_own_data_) |
| 241 | delete data_; |
| 242 | } |
| 243 | |
| 244 | bool CommandLine::HasSwitch(const wstring& switch_string) const { |
| 245 | wstring lowercased_switch(switch_string); |
| 246 | Lowercase(&lowercased_switch); |
| 247 | return data_->switches().find(lowercased_switch) != data_->switches().end(); |
| 248 | } |
| 249 | |
| 250 | wstring CommandLine::GetSwitchValue(const wstring& switch_string) const { |
| 251 | wstring lowercased_switch(switch_string); |
| 252 | Lowercase(&lowercased_switch); |
| 253 | |
| 254 | const map<wstring, wstring>::const_iterator result = |
| 255 | data_->switches().find(lowercased_switch); |
| 256 | |
| 257 | if (result == data_->switches().end()) { |
| 258 | return L""; |
| 259 | } else { |
| 260 | return result->second; |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | size_t CommandLine::GetLooseValueCount() const { |
| 265 | return data_->loose_values().size(); |
| 266 | } |
| 267 | |
| 268 | CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const { |
| 269 | return data_->loose_values().begin(); |
| 270 | } |
| 271 | |
| 272 | CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const { |
| 273 | return data_->loose_values().end(); |
| 274 | } |
| 275 | |
| 276 | std::wstring CommandLine::command_line_string() const { |
| 277 | return data_->command_line_string(); |
| 278 | } |
| 279 | |
| 280 | std::wstring CommandLine::program() const { |
| 281 | return data_->program(); |
| 282 | } |
| 283 | |
| 284 | // static |
| 285 | void CommandLine::AppendSwitch(wstring* command_line_string, |
| 286 | const wstring& switch_string) { |
| 287 | DCHECK(command_line_string); |
| 288 | command_line_string->append(L" "); |
| 289 | command_line_string->append(kSwitchPrefixes[0]); |
| 290 | command_line_string->append(switch_string); |
| 291 | } |
| 292 | |
| 293 | // static |
| 294 | void CommandLine::AppendSwitchWithValue(wstring* command_line_string, |
| 295 | const wstring& switch_string, |
| 296 | const wstring& value_string) { |
| 297 | AppendSwitch(command_line_string, switch_string); |
| 298 | |
| 299 | if (value_string.empty()) |
| 300 | return; |
| 301 | |
| 302 | command_line_string->append(kSwitchValueSeparator); |
| 303 | // NOTE(jhughes): If the value contains a quotation mark at one |
| 304 | // end but not both, you may get unusable output. |
| 305 | if ((value_string.find(L" ") != std::wstring::npos) && |
| 306 | (value_string[0] != L'"') && |
| 307 | (value_string[value_string.length() - 1] != L'"')) { |
| 308 | // need to provide quotes |
| 309 | StringAppendF(command_line_string, L"\"%s\"", value_string.c_str()); |
| 310 | } else { |
| 311 | command_line_string->append(value_string); |
| 312 | } |
| 313 | } |