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" |
deanm@google.com | a65ec9f | 2008-08-19 22:19:24 +0900 | [diff] [blame^] | 41 | #include "base/string_piece.h" |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 42 | #include "base/string_util.h" |
pinkerton@google.com | d5fb8e9 | 2008-08-08 07:28:24 +0900 | [diff] [blame] | 43 | #include "base/sys_string_conversions.h" |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 44 | |
| 45 | using namespace std; |
| 46 | |
| 47 | // Since we use a lazy match, make sure that longer versions (like L"--") |
| 48 | // are listed before shorter versions (like L"-") of similar prefixes. |
pinkerton@google.com | 0c0e01c | 2008-08-09 05:46:21 +0900 | [diff] [blame] | 49 | #if defined(OS_WIN) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 50 | const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"}; |
pinkerton@google.com | 0c0e01c | 2008-08-09 05:46:21 +0900 | [diff] [blame] | 51 | #elif defined(OS_POSIX) |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 52 | // Unixes don't use slash as a switch. |
pinkerton@google.com | 0c0e01c | 2008-08-09 05:46:21 +0900 | [diff] [blame] | 53 | const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-"}; |
| 54 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 55 | |
| 56 | const wchar_t CommandLine::kSwitchValueSeparator[] = L"="; |
| 57 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 58 | // Needed to avoid a typecast on the tolower() function pointer in Lowercase(). |
| 59 | // MSVC accepts it as-is but GCC requires the typecast. |
| 60 | static int ToLower(int c) { |
| 61 | return tolower(c); |
| 62 | } |
| 63 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 64 | static void Lowercase(wstring* parameter) { |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 65 | transform(parameter->begin(), parameter->end(), parameter->begin(), |
| 66 | ToLower); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | // CommandLine::Data |
| 70 | // |
| 71 | // This object holds the parsed data for a command line. We hold this in a |
| 72 | // separate object from |CommandLine| so that we can share the parsed data |
| 73 | // across multiple |CommandLine| objects. When we share |Data|, we might be |
| 74 | // accessing this object on multiple threads. To ensure thread safety, the |
| 75 | // public interface of this object is const only. |
| 76 | // |
| 77 | // Do NOT add any non-const methods to this object. You have been warned. |
| 78 | class CommandLine::Data { |
| 79 | public: |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 80 | #if defined(OS_WIN) |
| 81 | Data() { |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 82 | Init(GetCommandLineW()); |
| 83 | } |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 84 | #elif defined(OS_POSIX) |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 85 | Data() { |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 86 | // Owner must call Init(). |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 87 | } |
| 88 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 89 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 90 | #if defined(OS_WIN) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 91 | Data(const wstring& command_line) { |
| 92 | Init(command_line); |
| 93 | } |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 94 | #elif defined(OS_POSIX) |
tc@google.com | 1bc8d52 | 2008-08-15 07:09:39 +0900 | [diff] [blame] | 95 | Data(int argc, const char* const* argv) { |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 96 | Init(argc, argv); |
| 97 | } |
| 98 | #endif |
| 99 | |
| 100 | #if defined(OS_WIN) |
| 101 | // Does the actual parsing of the command line. |
| 102 | void Init(const std::wstring& command_line) { |
| 103 | TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); |
| 104 | |
| 105 | if (command_line_string_.empty()) |
| 106 | return; |
| 107 | |
| 108 | int num_args = 0; |
| 109 | wchar_t** args = NULL; |
| 110 | |
| 111 | args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); |
| 112 | |
| 113 | // Populate program_ with the trimmed version of the first arg. |
| 114 | TrimWhitespace(args[0], TRIM_ALL, &program_); |
| 115 | |
| 116 | for (int i = 1; i < num_args; ++i) { |
| 117 | wstring arg; |
| 118 | TrimWhitespace(args[i], TRIM_ALL, &arg); |
| 119 | |
| 120 | wstring switch_string; |
| 121 | wstring switch_value; |
| 122 | if (IsSwitch(arg, &switch_string, &switch_value)) { |
| 123 | switches_[switch_string] = switch_value; |
| 124 | } else { |
| 125 | loose_values_.push_back(arg); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | if (args) |
| 130 | LocalFree(args); |
| 131 | } |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 132 | |
| 133 | #elif defined(OS_POSIX) |
| 134 | // Does the actual parsing of the command line. |
tc@google.com | 1bc8d52 | 2008-08-15 07:09:39 +0900 | [diff] [blame] | 135 | void Init(int argc, const char* const* argv) { |
pinkerton@google.com | 0c0e01c | 2008-08-09 05:46:21 +0900 | [diff] [blame] | 136 | if (argc < 1) |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 137 | return; |
pinkerton@google.com | d5fb8e9 | 2008-08-08 07:28:24 +0900 | [diff] [blame] | 138 | program_ = base::SysNativeMBToWide(argv[0]); |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 139 | command_line_string_ = program_; |
| 140 | |
| 141 | for (int i = 1; i < argc; ++i) { |
pinkerton@google.com | d5fb8e9 | 2008-08-08 07:28:24 +0900 | [diff] [blame] | 142 | std::wstring arg = base::SysNativeMBToWide(argv[i]); |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 143 | command_line_string_.append(L" "); |
| 144 | command_line_string_.append(arg); |
| 145 | |
| 146 | wstring switch_string; |
| 147 | wstring switch_value; |
| 148 | if (IsSwitch(arg, &switch_string, &switch_value)) { |
| 149 | switches_[switch_string] = switch_value; |
| 150 | } else { |
| 151 | loose_values_.push_back(arg); |
| 152 | } |
| 153 | } |
| 154 | } |
| 155 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 156 | |
| 157 | const std::wstring& command_line_string() const { |
| 158 | return command_line_string_; |
| 159 | } |
| 160 | |
| 161 | const std::wstring& program() const { |
| 162 | return program_; |
| 163 | } |
| 164 | |
| 165 | const std::map<std::wstring, std::wstring>& switches() const { |
| 166 | return switches_; |
| 167 | } |
| 168 | |
| 169 | const std::vector<std::wstring>& loose_values() const { |
| 170 | return loose_values_; |
| 171 | } |
| 172 | |
| 173 | private: |
| 174 | // Returns true if parameter_string represents a switch. If true, |
| 175 | // switch_string and switch_value are set. (If false, both are |
| 176 | // set to the empty string.) |
| 177 | static bool IsSwitch(const wstring& parameter_string, |
| 178 | wstring* switch_string, |
| 179 | wstring* switch_value) { |
| 180 | |
| 181 | *switch_string = L""; |
| 182 | *switch_value = L""; |
| 183 | |
| 184 | for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { |
| 185 | std::wstring prefix(kSwitchPrefixes[i]); |
| 186 | if (parameter_string.find(prefix) != 0) // check prefix |
| 187 | continue; |
| 188 | |
| 189 | const size_t switch_start = prefix.length(); |
| 190 | const size_t equals_position = parameter_string.find( |
| 191 | kSwitchValueSeparator, switch_start); |
| 192 | if (equals_position == wstring::npos) { |
| 193 | *switch_string = parameter_string.substr(switch_start); |
| 194 | } else { |
| 195 | *switch_string = parameter_string.substr( |
| 196 | switch_start, equals_position - switch_start); |
| 197 | *switch_value = parameter_string.substr(equals_position + 1); |
| 198 | } |
| 199 | Lowercase(switch_string); |
| 200 | |
| 201 | return true; |
| 202 | } |
| 203 | |
| 204 | return false; |
| 205 | } |
| 206 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 207 | std::wstring command_line_string_; |
| 208 | std::wstring program_; |
| 209 | std::map<std::wstring, std::wstring> switches_; |
| 210 | std::vector<std::wstring> loose_values_; |
| 211 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 212 | DISALLOW_EVIL_CONSTRUCTORS(Data); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 213 | }; |
| 214 | |
| 215 | CommandLine::CommandLine() |
| 216 | : we_own_data_(false), // The Singleton class will manage it for us. |
| 217 | data_(Singleton<Data>::get()) { |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 218 | DCHECK(!data_->command_line_string().empty()) << |
| 219 | "You must call CommandLine::SetArgcArgv before making any CommandLine " |
| 220 | "calls."; |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 221 | } |
| 222 | |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 223 | #if defined(OS_WIN) |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 224 | CommandLine::CommandLine(const wstring& command_line) |
| 225 | : we_own_data_(true), |
| 226 | data_(new Data(command_line)) { |
| 227 | } |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 228 | #elif defined(OS_POSIX) |
tc@google.com | 1bc8d52 | 2008-08-15 07:09:39 +0900 | [diff] [blame] | 229 | CommandLine::CommandLine(const int argc, const char* const* argv) |
evanm@google.com | 91cdff8 | 2008-08-08 05:07:32 +0900 | [diff] [blame] | 230 | : we_own_data_(true), |
| 231 | data_(new Data(argc, argv)) { |
| 232 | } |
| 233 | #endif |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 234 | |
| 235 | CommandLine::~CommandLine() { |
| 236 | if (we_own_data_) |
| 237 | delete data_; |
| 238 | } |
| 239 | |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 240 | // static |
tc@google.com | 1bc8d52 | 2008-08-15 07:09:39 +0900 | [diff] [blame] | 241 | void CommandLine::SetArgcArgv(int argc, const char* const* argv) { |
evanm@google.com | 1c0026a | 2008-08-12 10:27:35 +0900 | [diff] [blame] | 242 | #if !defined(OS_WIN) |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 243 | Singleton<Data>::get()->Init(argc, argv); |
evanm@google.com | 1c0026a | 2008-08-12 10:27:35 +0900 | [diff] [blame] | 244 | #endif |
evanm@google.com | 638e9fb | 2008-08-12 10:14:37 +0900 | [diff] [blame] | 245 | } |
| 246 | |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 247 | bool CommandLine::HasSwitch(const wstring& switch_string) const { |
| 248 | wstring lowercased_switch(switch_string); |
| 249 | Lowercase(&lowercased_switch); |
| 250 | return data_->switches().find(lowercased_switch) != data_->switches().end(); |
| 251 | } |
| 252 | |
| 253 | wstring CommandLine::GetSwitchValue(const wstring& switch_string) const { |
| 254 | wstring lowercased_switch(switch_string); |
| 255 | Lowercase(&lowercased_switch); |
| 256 | |
| 257 | const map<wstring, wstring>::const_iterator result = |
| 258 | data_->switches().find(lowercased_switch); |
| 259 | |
| 260 | if (result == data_->switches().end()) { |
| 261 | return L""; |
| 262 | } else { |
| 263 | return result->second; |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | size_t CommandLine::GetLooseValueCount() const { |
| 268 | return data_->loose_values().size(); |
| 269 | } |
| 270 | |
| 271 | CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const { |
| 272 | return data_->loose_values().begin(); |
| 273 | } |
| 274 | |
| 275 | CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const { |
| 276 | return data_->loose_values().end(); |
| 277 | } |
| 278 | |
| 279 | std::wstring CommandLine::command_line_string() const { |
| 280 | return data_->command_line_string(); |
| 281 | } |
| 282 | |
| 283 | std::wstring CommandLine::program() const { |
| 284 | return data_->program(); |
| 285 | } |
| 286 | |
| 287 | // static |
| 288 | void CommandLine::AppendSwitch(wstring* command_line_string, |
| 289 | const wstring& switch_string) { |
| 290 | DCHECK(command_line_string); |
| 291 | command_line_string->append(L" "); |
| 292 | command_line_string->append(kSwitchPrefixes[0]); |
| 293 | command_line_string->append(switch_string); |
| 294 | } |
| 295 | |
| 296 | // static |
| 297 | void CommandLine::AppendSwitchWithValue(wstring* command_line_string, |
| 298 | const wstring& switch_string, |
| 299 | const wstring& value_string) { |
| 300 | AppendSwitch(command_line_string, switch_string); |
| 301 | |
| 302 | if (value_string.empty()) |
| 303 | return; |
| 304 | |
| 305 | command_line_string->append(kSwitchValueSeparator); |
| 306 | // NOTE(jhughes): If the value contains a quotation mark at one |
| 307 | // end but not both, you may get unusable output. |
| 308 | if ((value_string.find(L" ") != std::wstring::npos) && |
| 309 | (value_string[0] != L'"') && |
| 310 | (value_string[value_string.length() - 1] != L'"')) { |
| 311 | // need to provide quotes |
mmentovai@google.com | aeff944 | 2008-08-14 09:41:45 +0900 | [diff] [blame] | 312 | StringAppendF(command_line_string, L"\"%ls\"", value_string.c_str()); |
initial.commit | 3f4a732 | 2008-07-27 06:49:38 +0900 | [diff] [blame] | 313 | } else { |
| 314 | command_line_string->append(value_string); |
| 315 | } |
| 316 | } |