1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/command_line.h" 6 7 #include <algorithm> 8 #include <ostream> 9 10 #include "base/basictypes.h" 11 #include "base/file_path.h" 12 #include "base/logging.h" 13 #include "base/string_split.h" 14 #include "base/string_util.h" 15 #include "base/utf_string_conversions.h" 16 #include "build/build_config.h" 17 18 #if defined(OS_WIN) 19 #include <windows.h> 20 #include <shellapi.h> 21 #endif 22 23 CommandLine* CommandLine::current_process_commandline_ = NULL; 24 25 namespace { 26 typedef CommandLine::StringType::value_type CharType; 27 28 const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); 29 const CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); 30 // Since we use a lazy match, make sure that longer versions (like "--") are 31 // listed before shorter versions (like "-") of similar prefixes. 32 #if defined(OS_WIN) 33 const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; 34 #elif defined(OS_POSIX) 35 // Unixes don't use slash as a switch. 36 const CharType* const kSwitchPrefixes[] = {"--", "-"}; 37 #endif 38 39 #if defined(OS_WIN) 40 // Lowercase a string for case-insensitivity of switches. 41 // Is this desirable? It exists for backwards compatibility on Windows. 42 void Lowercase(std::string* arg) { 43 transform(arg->begin(), arg->end(), arg->begin(), tolower); 44 } 45 46 // Quote a string if necessary, such that CommandLineToArgvW() will always 47 // process it as a single argument. 48 std::wstring WindowsStyleQuote(const std::wstring& arg) { 49 // We follow the quoting rules of CommandLineToArgvW. 50 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx 51 if (arg.find_first_of(L" \\\"") == std::wstring::npos) { 52 // No quoting necessary. 53 return arg; 54 } 55 56 std::wstring out; 57 out.push_back(L'"'); 58 for (size_t i = 0; i < arg.size(); ++i) { 59 if (arg[i] == '\\') { 60 // Find the extent of this run of backslashes. 61 size_t start = i, end = start + 1; 62 for (; end < arg.size() && arg[end] == '\\'; ++end) 63 /* empty */; 64 size_t backslash_count = end - start; 65 66 // Backslashes are escapes only if the run is followed by a double quote. 67 // Since we also will end the string with a double quote, we escape for 68 // either a double quote or the end of the string. 69 if (end == arg.size() || arg[end] == '"') { 70 // To quote, we need to output 2x as many backslashes. 71 backslash_count *= 2; 72 } 73 for (size_t j = 0; j < backslash_count; ++j) 74 out.push_back('\\'); 75 76 // Advance i to one before the end to balance i++ in loop. 77 i = end - 1; 78 } else if (arg[i] == '"') { 79 out.push_back('\\'); 80 out.push_back('"'); 81 } else { 82 out.push_back(arg[i]); 83 } 84 } 85 out.push_back('"'); 86 87 return out; 88 } 89 #endif 90 91 // Returns true and fills in |switch_string| and |switch_value| if 92 // |parameter_string| represents a switch. 93 bool IsSwitch(const CommandLine::StringType& parameter_string, 94 std::string* switch_string, 95 CommandLine::StringType* switch_value) { 96 switch_string->clear(); 97 switch_value->clear(); 98 99 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { 100 CommandLine::StringType prefix(kSwitchPrefixes[i]); 101 if (parameter_string.find(prefix) != 0) 102 continue; 103 104 const size_t switch_start = prefix.length(); 105 const size_t equals_position = parameter_string.find( 106 kSwitchValueSeparator, switch_start); 107 CommandLine::StringType switch_native; 108 if (equals_position == CommandLine::StringType::npos) { 109 switch_native = parameter_string.substr(switch_start); 110 } else { 111 switch_native = parameter_string.substr( 112 switch_start, equals_position - switch_start); 113 *switch_value = parameter_string.substr(equals_position + 1); 114 } 115 #if defined(OS_WIN) 116 *switch_string = WideToASCII(switch_native); 117 Lowercase(switch_string); 118 #else 119 *switch_string = switch_native; 120 #endif 121 122 return true; 123 } 124 125 return false; 126 } 127 128 } // namespace 129 130 CommandLine::CommandLine(NoProgram no_program) { 131 #if defined(OS_POSIX) 132 // Push an empty argument, because we always assume argv_[0] is a program. 133 argv_.push_back(""); 134 #endif 135 } 136 137 CommandLine::CommandLine(const FilePath& program) { 138 #if defined(OS_WIN) 139 if (!program.empty()) { 140 program_ = program.value(); 141 // TODO(evanm): proper quoting here. 142 command_line_string_ = L'"' + program.value() + L'"'; 143 } 144 #elif defined(OS_POSIX) 145 argv_.push_back(program.value()); 146 #endif 147 } 148 149 #if defined(OS_POSIX) 150 CommandLine::CommandLine(int argc, const char* const* argv) { 151 InitFromArgv(argc, argv); 152 } 153 154 CommandLine::CommandLine(const StringVector& argv) { 155 InitFromArgv(argv); 156 } 157 #endif // OS_POSIX 158 159 CommandLine::~CommandLine() { 160 } 161 162 // static 163 void CommandLine::Init(int argc, const char* const* argv) { 164 delete current_process_commandline_; 165 current_process_commandline_ = new CommandLine; 166 #if defined(OS_WIN) 167 current_process_commandline_->ParseFromString(::GetCommandLineW()); 168 #elif defined(OS_POSIX) 169 current_process_commandline_->InitFromArgv(argc, argv); 170 #endif 171 } 172 173 // static 174 void CommandLine::Reset() { 175 DCHECK(current_process_commandline_); 176 delete current_process_commandline_; 177 current_process_commandline_ = NULL; 178 } 179 180 // static 181 CommandLine* CommandLine::ForCurrentProcess() { 182 DCHECK(current_process_commandline_); 183 return current_process_commandline_; 184 } 185 186 #if defined(OS_WIN) 187 // static 188 CommandLine CommandLine::FromString(const std::wstring& command_line) { 189 CommandLine cmd; 190 cmd.ParseFromString(command_line); 191 return cmd; 192 } 193 #endif // OS_WIN 194 195 #if defined(OS_POSIX) 196 void CommandLine::InitFromArgv(int argc, const char* const* argv) { 197 for (int i = 0; i < argc; ++i) 198 argv_.push_back(argv[i]); 199 InitFromArgv(argv_); 200 } 201 202 void CommandLine::InitFromArgv(const StringVector& argv) { 203 argv_ = argv; 204 bool parse_switches = true; 205 for (size_t i = 1; i < argv_.size(); ++i) { 206 const std::string& arg = argv_[i]; 207 208 if (!parse_switches) { 209 args_.push_back(arg); 210 continue; 211 } 212 213 if (arg == kSwitchTerminator) { 214 parse_switches = false; 215 continue; 216 } 217 218 std::string switch_string; 219 StringType switch_value; 220 if (IsSwitch(arg, &switch_string, &switch_value)) { 221 switches_[switch_string] = switch_value; 222 } else { 223 args_.push_back(arg); 224 } 225 } 226 } 227 #endif // OS_POSIX 228 229 CommandLine::StringType CommandLine::command_line_string() const { 230 #if defined(OS_WIN) 231 return command_line_string_; 232 #elif defined(OS_POSIX) 233 return JoinString(argv_, ' '); 234 #endif 235 } 236 237 FilePath CommandLine::GetProgram() const { 238 #if defined(OS_WIN) 239 return FilePath(program_); 240 #else 241 DCHECK_GT(argv_.size(), 0U); 242 return FilePath(argv_[0]); 243 #endif 244 } 245 246 bool CommandLine::HasSwitch(const std::string& switch_string) const { 247 std::string lowercased_switch(switch_string); 248 #if defined(OS_WIN) 249 Lowercase(&lowercased_switch); 250 #endif 251 return switches_.find(lowercased_switch) != switches_.end(); 252 } 253 254 std::string CommandLine::GetSwitchValueASCII( 255 const std::string& switch_string) const { 256 CommandLine::StringType value = GetSwitchValueNative(switch_string); 257 if (!IsStringASCII(value)) { 258 LOG(WARNING) << "Value of --" << switch_string << " must be ASCII."; 259 return ""; 260 } 261 #if defined(OS_WIN) 262 return WideToASCII(value); 263 #else 264 return value; 265 #endif 266 } 267 268 FilePath CommandLine::GetSwitchValuePath( 269 const std::string& switch_string) const { 270 return FilePath(GetSwitchValueNative(switch_string)); 271 } 272 273 CommandLine::StringType CommandLine::GetSwitchValueNative( 274 const std::string& switch_string) const { 275 std::string lowercased_switch(switch_string); 276 #if defined(OS_WIN) 277 Lowercase(&lowercased_switch); 278 #endif 279 280 SwitchMap::const_iterator result = switches_.find(lowercased_switch); 281 282 if (result == switches_.end()) { 283 return CommandLine::StringType(); 284 } else { 285 return result->second; 286 } 287 } 288 289 size_t CommandLine::GetSwitchCount() const { 290 return switches_.size(); 291 } 292 293 void CommandLine::AppendSwitch(const std::string& switch_string) { 294 #if defined(OS_WIN) 295 command_line_string_.append(L" "); 296 command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string)); 297 switches_[switch_string] = L""; 298 #elif defined(OS_POSIX) 299 argv_.push_back(kSwitchPrefixes[0] + switch_string); 300 switches_[switch_string] = ""; 301 #endif 302 } 303 304 void CommandLine::AppendSwitchPath(const std::string& switch_string, 305 const FilePath& path) { 306 AppendSwitchNative(switch_string, path.value()); 307 } 308 309 void CommandLine::AppendSwitchNative(const std::string& switch_string, 310 const CommandLine::StringType& value) { 311 #if defined(OS_WIN) 312 StringType combined_switch_string = 313 kSwitchPrefixes[0] + ASCIIToWide(switch_string); 314 if (!value.empty()) 315 combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value); 316 317 command_line_string_.append(L" "); 318 command_line_string_.append(combined_switch_string); 319 320 switches_[switch_string] = value; 321 #elif defined(OS_POSIX) 322 StringType combined_switch_string = kSwitchPrefixes[0] + switch_string; 323 if (!value.empty()) 324 combined_switch_string += kSwitchValueSeparator + value; 325 argv_.push_back(combined_switch_string); 326 switches_[switch_string] = value; 327 #endif 328 } 329 330 void CommandLine::AppendSwitchASCII(const std::string& switch_string, 331 const std::string& value_string) { 332 #if defined(OS_WIN) 333 AppendSwitchNative(switch_string, ASCIIToWide(value_string)); 334 #elif defined(OS_POSIX) 335 AppendSwitchNative(switch_string, value_string); 336 #endif 337 } 338 339 void CommandLine::AppendSwitches(const CommandLine& other) { 340 SwitchMap::const_iterator i; 341 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) 342 AppendSwitchNative(i->first, i->second); 343 } 344 345 void CommandLine::CopySwitchesFrom(const CommandLine& source, 346 const char* const switches[], 347 size_t count) { 348 for (size_t i = 0; i < count; ++i) { 349 if (source.HasSwitch(switches[i])) { 350 StringType value = source.GetSwitchValueNative(switches[i]); 351 AppendSwitchNative(switches[i], value); 352 } 353 } 354 } 355 356 void CommandLine::AppendArg(const std::string& value) { 357 #if defined(OS_WIN) 358 DCHECK(IsStringUTF8(value)); 359 AppendArgNative(UTF8ToWide(value)); 360 #elif defined(OS_POSIX) 361 AppendArgNative(value); 362 #endif 363 } 364 365 void CommandLine::AppendArgPath(const FilePath& path) { 366 AppendArgNative(path.value()); 367 } 368 369 void CommandLine::AppendArgNative(const CommandLine::StringType& value) { 370 #if defined(OS_WIN) 371 command_line_string_.append(L" "); 372 command_line_string_.append(WindowsStyleQuote(value)); 373 args_.push_back(value); 374 #elif defined(OS_POSIX) 375 DCHECK(IsStringUTF8(value)); 376 argv_.push_back(value); 377 #endif 378 } 379 380 void CommandLine::AppendArgs(const CommandLine& other) { 381 if(other.args_.size() <= 0) 382 return; 383 #if defined(OS_WIN) 384 command_line_string_.append(L" --"); 385 #endif // OS_WIN 386 StringVector::const_iterator i; 387 for (i = other.args_.begin(); i != other.args_.end(); ++i) 388 AppendArgNative(*i); 389 } 390 391 void CommandLine::AppendArguments(const CommandLine& other, 392 bool include_program) { 393 #if defined(OS_WIN) 394 // Verify include_program is used correctly. 395 DCHECK(!include_program || !other.GetProgram().empty()); 396 if (include_program) 397 program_ = other.program_; 398 399 if (!command_line_string_.empty()) 400 command_line_string_ += L' '; 401 402 command_line_string_ += other.command_line_string_; 403 #elif defined(OS_POSIX) 404 // Verify include_program is used correctly. 405 // Logic could be shorter but this is clearer. 406 DCHECK_EQ(include_program, !other.GetProgram().empty()); 407 408 if (include_program) 409 argv_[0] = other.argv_[0]; 410 411 // Skip the first arg when copying since it's the program but push all 412 // arguments to our arg vector. 413 for (size_t i = 1; i < other.argv_.size(); ++i) 414 argv_.push_back(other.argv_[i]); 415 #endif 416 417 SwitchMap::const_iterator i; 418 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) 419 switches_[i->first] = i->second; 420 } 421 422 void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { 423 // The wrapper may have embedded arguments (like "gdb --args"). In this case, 424 // we don't pretend to do anything fancy, we just split on spaces. 425 if (wrapper.empty()) 426 return; 427 StringVector wrapper_and_args; 428 #if defined(OS_WIN) 429 base::SplitString(wrapper, ' ', &wrapper_and_args); 430 program_ = wrapper_and_args[0]; 431 command_line_string_ = wrapper + L" " + command_line_string_; 432 #elif defined(OS_POSIX) 433 base::SplitString(wrapper, ' ', &wrapper_and_args); 434 argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); 435 #endif 436 } 437 438 #if defined(OS_WIN) 439 void CommandLine::ParseFromString(const std::wstring& command_line) { 440 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); 441 442 if (command_line_string_.empty()) 443 return; 444 445 int num_args = 0; 446 wchar_t** args = NULL; 447 448 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); 449 450 // Populate program_ with the trimmed version of the first arg. 451 TrimWhitespace(args[0], TRIM_ALL, &program_); 452 453 bool parse_switches = true; 454 for (int i = 1; i < num_args; ++i) { 455 std::wstring arg; 456 TrimWhitespace(args[i], TRIM_ALL, &arg); 457 458 if (!parse_switches) { 459 args_.push_back(arg); 460 continue; 461 } 462 463 if (arg == kSwitchTerminator) { 464 parse_switches = false; 465 continue; 466 } 467 468 std::string switch_string; 469 std::wstring switch_value; 470 if (IsSwitch(arg, &switch_string, &switch_value)) { 471 switches_[switch_string] = switch_value; 472 } else { 473 args_.push_back(arg); 474 } 475 } 476 477 if (args) 478 LocalFree(args); 479 } 480 #endif 481 482 CommandLine::CommandLine() { 483 } 484