1 // Copyright (c) 2012 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/files/file_path.h" 11 #include "base/logging.h" 12 #include "base/macros.h" 13 #include "base/strings/string_split.h" 14 #include "base/strings/string_util.h" 15 #include "base/strings/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 namespace base { 24 25 CommandLine* CommandLine::current_process_commandline_ = NULL; 26 27 namespace { 28 29 const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); 30 const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); 31 32 // Since we use a lazy match, make sure that longer versions (like "--") are 33 // listed before shorter versions (like "-") of similar prefixes. 34 #if defined(OS_WIN) 35 // By putting slash last, we can control whether it is treaded as a switch 36 // value by changing the value of switch_prefix_count to be one less than 37 // the array size. 38 const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; 39 #elif defined(OS_POSIX) 40 // Unixes don't use slash as a switch. 41 const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; 42 #endif 43 size_t switch_prefix_count = arraysize(kSwitchPrefixes); 44 45 size_t GetSwitchPrefixLength(const CommandLine::StringType& string) { 46 for (size_t i = 0; i < switch_prefix_count; ++i) { 47 CommandLine::StringType prefix(kSwitchPrefixes[i]); 48 if (string.compare(0, prefix.length(), prefix) == 0) 49 return prefix.length(); 50 } 51 return 0; 52 } 53 54 // Fills in |switch_string| and |switch_value| if |string| is a switch. 55 // This will preserve the input switch prefix in the output |switch_string|. 56 bool IsSwitch(const CommandLine::StringType& string, 57 CommandLine::StringType* switch_string, 58 CommandLine::StringType* switch_value) { 59 switch_string->clear(); 60 switch_value->clear(); 61 size_t prefix_length = GetSwitchPrefixLength(string); 62 if (prefix_length == 0 || prefix_length == string.length()) 63 return false; 64 65 const size_t equals_position = string.find(kSwitchValueSeparator); 66 *switch_string = string.substr(0, equals_position); 67 if (equals_position != CommandLine::StringType::npos) 68 *switch_value = string.substr(equals_position + 1); 69 return true; 70 } 71 72 // Append switches and arguments, keeping switches before arguments. 73 void AppendSwitchesAndArguments(CommandLine* command_line, 74 const CommandLine::StringVector& argv) { 75 bool parse_switches = true; 76 for (size_t i = 1; i < argv.size(); ++i) { 77 CommandLine::StringType arg = argv[i]; 78 #if defined(OS_WIN) 79 TrimWhitespace(arg, TRIM_ALL, &arg); 80 #else 81 TrimWhitespaceASCII(arg, TRIM_ALL, &arg); 82 #endif 83 84 CommandLine::StringType switch_string; 85 CommandLine::StringType switch_value; 86 parse_switches &= (arg != kSwitchTerminator); 87 if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { 88 #if defined(OS_WIN) 89 command_line->AppendSwitchNative(UTF16ToASCII(switch_string), 90 switch_value); 91 #elif defined(OS_POSIX) 92 command_line->AppendSwitchNative(switch_string, switch_value); 93 #endif 94 } else { 95 command_line->AppendArgNative(arg); 96 } 97 } 98 } 99 100 #if defined(OS_WIN) 101 // Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. 102 string16 QuoteForCommandLineToArgvW(const string16& arg, 103 bool quote_placeholders) { 104 // We follow the quoting rules of CommandLineToArgvW. 105 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx 106 string16 quotable_chars(L" \\\""); 107 // We may also be required to quote '%', which is commonly used in a command 108 // line as a placeholder. (It may be substituted for a string with spaces.) 109 if (quote_placeholders) 110 quotable_chars.push_back(L'%'); 111 if (arg.find_first_of(quotable_chars) == string16::npos) { 112 // No quoting necessary. 113 return arg; 114 } 115 116 string16 out; 117 out.push_back(L'"'); 118 for (size_t i = 0; i < arg.size(); ++i) { 119 if (arg[i] == '\\') { 120 // Find the extent of this run of backslashes. 121 size_t start = i, end = start + 1; 122 for (; end < arg.size() && arg[end] == '\\'; ++end) {} 123 size_t backslash_count = end - start; 124 125 // Backslashes are escapes only if the run is followed by a double quote. 126 // Since we also will end the string with a double quote, we escape for 127 // either a double quote or the end of the string. 128 if (end == arg.size() || arg[end] == '"') { 129 // To quote, we need to output 2x as many backslashes. 130 backslash_count *= 2; 131 } 132 for (size_t j = 0; j < backslash_count; ++j) 133 out.push_back('\\'); 134 135 // Advance i to one before the end to balance i++ in loop. 136 i = end - 1; 137 } else if (arg[i] == '"') { 138 out.push_back('\\'); 139 out.push_back('"'); 140 } else { 141 out.push_back(arg[i]); 142 } 143 } 144 out.push_back('"'); 145 146 return out; 147 } 148 #endif 149 150 } // namespace 151 152 CommandLine::CommandLine(NoProgram) : argv_(1), begin_args_(1) {} 153 154 CommandLine::CommandLine(const FilePath& program) 155 : argv_(1), 156 begin_args_(1) { 157 SetProgram(program); 158 } 159 160 CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) 161 : argv_(1), 162 begin_args_(1) { 163 InitFromArgv(argc, argv); 164 } 165 166 CommandLine::CommandLine(const StringVector& argv) 167 : argv_(1), 168 begin_args_(1) { 169 InitFromArgv(argv); 170 } 171 172 CommandLine::CommandLine(const CommandLine& other) 173 : argv_(other.argv_), 174 switches_(other.switches_), 175 begin_args_(other.begin_args_) { 176 ResetStringPieces(); 177 } 178 179 CommandLine& CommandLine::operator=(const CommandLine& other) { 180 argv_ = other.argv_; 181 switches_ = other.switches_; 182 begin_args_ = other.begin_args_; 183 ResetStringPieces(); 184 return *this; 185 } 186 187 CommandLine::~CommandLine() { 188 } 189 190 #if defined(OS_WIN) 191 // static 192 void CommandLine::set_slash_is_not_a_switch() { 193 // The last switch prefix should be slash, so adjust the size to skip it. 194 DCHECK_EQ(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/"), 0); 195 switch_prefix_count = arraysize(kSwitchPrefixes) - 1; 196 } 197 198 // static 199 void CommandLine::InitUsingArgvForTesting(int argc, const char* const* argv) { 200 DCHECK(!current_process_commandline_); 201 current_process_commandline_ = new CommandLine(NO_PROGRAM); 202 // On Windows we need to convert the command line arguments to string16. 203 base::CommandLine::StringVector argv_vector; 204 for (int i = 0; i < argc; ++i) 205 argv_vector.push_back(UTF8ToUTF16(argv[i])); 206 current_process_commandline_->InitFromArgv(argv_vector); 207 } 208 #endif 209 210 // static 211 bool CommandLine::Init(int argc, const char* const* argv) { 212 if (current_process_commandline_) { 213 // If this is intentional, Reset() must be called first. If we are using 214 // the shared build mode, we have to share a single object across multiple 215 // shared libraries. 216 return false; 217 } 218 219 current_process_commandline_ = new CommandLine(NO_PROGRAM); 220 #if defined(OS_WIN) 221 current_process_commandline_->ParseFromString(::GetCommandLineW()); 222 #elif defined(OS_POSIX) 223 current_process_commandline_->InitFromArgv(argc, argv); 224 #endif 225 226 return true; 227 } 228 229 // static 230 void CommandLine::Reset() { 231 DCHECK(current_process_commandline_); 232 delete current_process_commandline_; 233 current_process_commandline_ = NULL; 234 } 235 236 // static 237 CommandLine* CommandLine::ForCurrentProcess() { 238 DCHECK(current_process_commandline_); 239 return current_process_commandline_; 240 } 241 242 // static 243 bool CommandLine::InitializedForCurrentProcess() { 244 return !!current_process_commandline_; 245 } 246 247 #if defined(OS_WIN) 248 // static 249 CommandLine CommandLine::FromString(const string16& command_line) { 250 CommandLine cmd(NO_PROGRAM); 251 cmd.ParseFromString(command_line); 252 return cmd; 253 } 254 #endif 255 256 void CommandLine::InitFromArgv(int argc, 257 const CommandLine::CharType* const* argv) { 258 StringVector new_argv; 259 for (int i = 0; i < argc; ++i) 260 new_argv.push_back(argv[i]); 261 InitFromArgv(new_argv); 262 } 263 264 void CommandLine::InitFromArgv(const StringVector& argv) { 265 argv_ = StringVector(1); 266 switches_.clear(); 267 switches_by_stringpiece_.clear(); 268 begin_args_ = 1; 269 SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); 270 AppendSwitchesAndArguments(this, argv); 271 } 272 273 FilePath CommandLine::GetProgram() const { 274 return FilePath(argv_[0]); 275 } 276 277 void CommandLine::SetProgram(const FilePath& program) { 278 #if defined(OS_WIN) 279 TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]); 280 #else 281 TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]); 282 #endif 283 } 284 285 bool CommandLine::HasSwitch(const base::StringPiece& switch_string) const { 286 DCHECK_EQ(ToLowerASCII(switch_string), switch_string); 287 return switches_by_stringpiece_.find(switch_string) != 288 switches_by_stringpiece_.end(); 289 } 290 291 bool CommandLine::HasSwitch(const char switch_constant[]) const { 292 return HasSwitch(base::StringPiece(switch_constant)); 293 } 294 295 std::string CommandLine::GetSwitchValueASCII( 296 const base::StringPiece& switch_string) const { 297 StringType value = GetSwitchValueNative(switch_string); 298 if (!IsStringASCII(value)) { 299 DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; 300 return std::string(); 301 } 302 #if defined(OS_WIN) 303 return UTF16ToASCII(value); 304 #else 305 return value; 306 #endif 307 } 308 309 FilePath CommandLine::GetSwitchValuePath( 310 const base::StringPiece& switch_string) const { 311 return FilePath(GetSwitchValueNative(switch_string)); 312 } 313 314 CommandLine::StringType CommandLine::GetSwitchValueNative( 315 const base::StringPiece& switch_string) const { 316 DCHECK_EQ(ToLowerASCII(switch_string), switch_string); 317 auto result = switches_by_stringpiece_.find(switch_string); 318 return result == switches_by_stringpiece_.end() ? StringType() 319 : *(result->second); 320 } 321 322 void CommandLine::AppendSwitch(const std::string& switch_string) { 323 AppendSwitchNative(switch_string, StringType()); 324 } 325 326 void CommandLine::AppendSwitchPath(const std::string& switch_string, 327 const FilePath& path) { 328 AppendSwitchNative(switch_string, path.value()); 329 } 330 331 void CommandLine::AppendSwitchNative(const std::string& switch_string, 332 const CommandLine::StringType& value) { 333 #if defined(OS_WIN) 334 const std::string switch_key = ToLowerASCII(switch_string); 335 StringType combined_switch_string(ASCIIToUTF16(switch_key)); 336 #elif defined(OS_POSIX) 337 const std::string& switch_key = switch_string; 338 StringType combined_switch_string(switch_key); 339 #endif 340 size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); 341 auto insertion = 342 switches_.insert(make_pair(switch_key.substr(prefix_length), value)); 343 if (!insertion.second) 344 insertion.first->second = value; 345 switches_by_stringpiece_[insertion.first->first] = &(insertion.first->second); 346 // Preserve existing switch prefixes in |argv_|; only append one if necessary. 347 if (prefix_length == 0) 348 combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; 349 if (!value.empty()) 350 combined_switch_string += kSwitchValueSeparator + value; 351 // Append the switch and update the switches/arguments divider |begin_args_|. 352 argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); 353 } 354 355 void CommandLine::AppendSwitchASCII(const std::string& switch_string, 356 const std::string& value_string) { 357 #if defined(OS_WIN) 358 AppendSwitchNative(switch_string, ASCIIToUTF16(value_string)); 359 #elif defined(OS_POSIX) 360 AppendSwitchNative(switch_string, value_string); 361 #endif 362 } 363 364 void CommandLine::CopySwitchesFrom(const CommandLine& source, 365 const char* const switches[], 366 size_t count) { 367 for (size_t i = 0; i < count; ++i) { 368 if (source.HasSwitch(switches[i])) 369 AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i])); 370 } 371 } 372 373 CommandLine::StringVector CommandLine::GetArgs() const { 374 // Gather all arguments after the last switch (may include kSwitchTerminator). 375 StringVector args(argv_.begin() + begin_args_, argv_.end()); 376 // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) 377 StringVector::iterator switch_terminator = 378 std::find(args.begin(), args.end(), kSwitchTerminator); 379 if (switch_terminator != args.end()) 380 args.erase(switch_terminator); 381 return args; 382 } 383 384 void CommandLine::AppendArg(const std::string& value) { 385 #if defined(OS_WIN) 386 DCHECK(IsStringUTF8(value)); 387 AppendArgNative(UTF8ToWide(value)); 388 #elif defined(OS_POSIX) 389 AppendArgNative(value); 390 #endif 391 } 392 393 void CommandLine::AppendArgPath(const FilePath& path) { 394 AppendArgNative(path.value()); 395 } 396 397 void CommandLine::AppendArgNative(const CommandLine::StringType& value) { 398 argv_.push_back(value); 399 } 400 401 void CommandLine::AppendArguments(const CommandLine& other, 402 bool include_program) { 403 if (include_program) 404 SetProgram(other.GetProgram()); 405 AppendSwitchesAndArguments(this, other.argv()); 406 } 407 408 void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { 409 if (wrapper.empty()) 410 return; 411 // The wrapper may have embedded arguments (like "gdb --args"). In this case, 412 // we don't pretend to do anything fancy, we just split on spaces. 413 StringVector wrapper_argv = SplitString( 414 wrapper, FilePath::StringType(1, ' '), base::TRIM_WHITESPACE, 415 base::SPLIT_WANT_ALL); 416 // Prepend the wrapper and update the switches/arguments |begin_args_|. 417 argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end()); 418 begin_args_ += wrapper_argv.size(); 419 } 420 421 #if defined(OS_WIN) 422 void CommandLine::ParseFromString(const string16& command_line) { 423 string16 command_line_string; 424 TrimWhitespace(command_line, TRIM_ALL, &command_line_string); 425 if (command_line_string.empty()) 426 return; 427 428 int num_args = 0; 429 wchar_t** args = NULL; 430 args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); 431 432 DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " 433 << UTF16ToUTF8(command_line); 434 InitFromArgv(num_args, args); 435 LocalFree(args); 436 } 437 #endif 438 439 CommandLine::StringType CommandLine::GetCommandLineStringInternal( 440 bool quote_placeholders) const { 441 StringType string(argv_[0]); 442 #if defined(OS_WIN) 443 string = QuoteForCommandLineToArgvW(string, quote_placeholders); 444 #endif 445 StringType params(GetArgumentsStringInternal(quote_placeholders)); 446 if (!params.empty()) { 447 string.append(StringType(FILE_PATH_LITERAL(" "))); 448 string.append(params); 449 } 450 return string; 451 } 452 453 CommandLine::StringType CommandLine::GetArgumentsStringInternal( 454 bool quote_placeholders) const { 455 #if !defined(OS_WIN) 456 (void)quote_placeholders; // Avoid an unused warning. 457 #endif 458 StringType params; 459 // Append switches and arguments. 460 bool parse_switches = true; 461 for (size_t i = 1; i < argv_.size(); ++i) { 462 StringType arg = argv_[i]; 463 StringType switch_string; 464 StringType switch_value; 465 parse_switches &= arg != kSwitchTerminator; 466 if (i > 1) 467 params.append(StringType(FILE_PATH_LITERAL(" "))); 468 if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { 469 params.append(switch_string); 470 if (!switch_value.empty()) { 471 #if defined(OS_WIN) 472 switch_value = 473 QuoteForCommandLineToArgvW(switch_value, quote_placeholders); 474 #endif 475 params.append(kSwitchValueSeparator + switch_value); 476 } 477 } else { 478 #if defined(OS_WIN) 479 arg = QuoteForCommandLineToArgvW(arg, quote_placeholders); 480 #endif 481 params.append(arg); 482 } 483 } 484 return params; 485 } 486 487 void CommandLine::ResetStringPieces() { 488 switches_by_stringpiece_.clear(); 489 for (const auto& entry : switches_) 490 switches_by_stringpiece_[entry.first] = &(entry.second); 491 } 492 493 } // namespace base 494