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