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 /* no_program */) 153 : argv_(1), 154 begin_args_(1) { 155 } 156 157 CommandLine::CommandLine(const FilePath& program) 158 : argv_(1), 159 begin_args_(1) { 160 SetProgram(program); 161 } 162 163 CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) 164 : argv_(1), 165 begin_args_(1) { 166 InitFromArgv(argc, argv); 167 } 168 169 CommandLine::CommandLine(const StringVector& argv) 170 : argv_(1), 171 begin_args_(1) { 172 InitFromArgv(argv); 173 } 174 175 CommandLine::CommandLine(const CommandLine& other) 176 : argv_(other.argv_), 177 switches_(other.switches_), 178 begin_args_(other.begin_args_) { 179 ResetStringPieces(); 180 } 181 182 CommandLine& CommandLine::operator=(const CommandLine& other) { 183 argv_ = other.argv_; 184 switches_ = other.switches_; 185 begin_args_ = other.begin_args_; 186 ResetStringPieces(); 187 return *this; 188 } 189 190 CommandLine::~CommandLine() { 191 } 192 193 #if defined(OS_WIN) 194 // static 195 void CommandLine::set_slash_is_not_a_switch() { 196 // The last switch prefix should be slash, so adjust the size to skip it. 197 DCHECK_EQ(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/"), 0); 198 switch_prefix_count = arraysize(kSwitchPrefixes) - 1; 199 } 200 #endif 201 202 // static 203 bool CommandLine::Init(int argc, const char* const* argv) { 204 if (current_process_commandline_) { 205 // If this is intentional, Reset() must be called first. If we are using 206 // the shared build mode, we have to share a single object across multiple 207 // shared libraries. 208 return false; 209 } 210 211 current_process_commandline_ = new CommandLine(NO_PROGRAM); 212 #if defined(OS_WIN) 213 current_process_commandline_->ParseFromString(::GetCommandLineW()); 214 #elif defined(OS_POSIX) 215 current_process_commandline_->InitFromArgv(argc, argv); 216 #endif 217 218 return true; 219 } 220 221 // static 222 void CommandLine::Reset() { 223 DCHECK(current_process_commandline_); 224 delete current_process_commandline_; 225 current_process_commandline_ = NULL; 226 } 227 228 // static 229 CommandLine* CommandLine::ForCurrentProcess() { 230 DCHECK(current_process_commandline_); 231 return current_process_commandline_; 232 } 233 234 // static 235 bool CommandLine::InitializedForCurrentProcess() { 236 return !!current_process_commandline_; 237 } 238 239 #if defined(OS_WIN) 240 // static 241 CommandLine CommandLine::FromString(const string16& command_line) { 242 CommandLine cmd(NO_PROGRAM); 243 cmd.ParseFromString(command_line); 244 return cmd; 245 } 246 #endif 247 248 void CommandLine::InitFromArgv(int argc, 249 const CommandLine::CharType* const* argv) { 250 StringVector new_argv; 251 for (int i = 0; i < argc; ++i) 252 new_argv.push_back(argv[i]); 253 InitFromArgv(new_argv); 254 } 255 256 void CommandLine::InitFromArgv(const StringVector& argv) { 257 argv_ = StringVector(1); 258 switches_.clear(); 259 switches_by_stringpiece_.clear(); 260 begin_args_ = 1; 261 SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); 262 AppendSwitchesAndArguments(this, argv); 263 } 264 265 FilePath CommandLine::GetProgram() const { 266 return FilePath(argv_[0]); 267 } 268 269 void CommandLine::SetProgram(const FilePath& program) { 270 #if defined(OS_WIN) 271 TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]); 272 #else 273 TrimWhitespaceASCII(program.value(), TRIM_ALL, &argv_[0]); 274 #endif 275 } 276 277 bool CommandLine::HasSwitch(const base::StringPiece& switch_string) const { 278 DCHECK_EQ(ToLowerASCII(switch_string), switch_string); 279 return switches_by_stringpiece_.find(switch_string) != 280 switches_by_stringpiece_.end(); 281 } 282 283 bool CommandLine::HasSwitch(const char switch_constant[]) const { 284 return HasSwitch(base::StringPiece(switch_constant)); 285 } 286 287 std::string CommandLine::GetSwitchValueASCII( 288 const base::StringPiece& switch_string) const { 289 StringType value = GetSwitchValueNative(switch_string); 290 if (!IsStringASCII(value)) { 291 DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; 292 return std::string(); 293 } 294 #if defined(OS_WIN) 295 return UTF16ToASCII(value); 296 #else 297 return value; 298 #endif 299 } 300 301 FilePath CommandLine::GetSwitchValuePath( 302 const base::StringPiece& switch_string) const { 303 return FilePath(GetSwitchValueNative(switch_string)); 304 } 305 306 CommandLine::StringType CommandLine::GetSwitchValueNative( 307 const base::StringPiece& switch_string) const { 308 DCHECK_EQ(ToLowerASCII(switch_string), switch_string); 309 auto result = switches_by_stringpiece_.find(switch_string); 310 return result == switches_by_stringpiece_.end() ? StringType() 311 : *(result->second); 312 } 313 314 void CommandLine::AppendSwitch(const std::string& switch_string) { 315 AppendSwitchNative(switch_string, StringType()); 316 } 317 318 void CommandLine::AppendSwitchPath(const std::string& switch_string, 319 const FilePath& path) { 320 AppendSwitchNative(switch_string, path.value()); 321 } 322 323 void CommandLine::AppendSwitchNative(const std::string& switch_string, 324 const CommandLine::StringType& value) { 325 #if defined(OS_WIN) 326 const std::string switch_key = ToLowerASCII(switch_string); 327 StringType combined_switch_string(ASCIIToUTF16(switch_key)); 328 #elif defined(OS_POSIX) 329 const std::string& switch_key = switch_string; 330 StringType combined_switch_string(switch_key); 331 #endif 332 size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); 333 auto insertion = 334 switches_.insert(make_pair(switch_key.substr(prefix_length), value)); 335 if (!insertion.second) 336 insertion.first->second = value; 337 switches_by_stringpiece_[insertion.first->first] = &(insertion.first->second); 338 // Preserve existing switch prefixes in |argv_|; only append one if necessary. 339 if (prefix_length == 0) 340 combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; 341 if (!value.empty()) 342 combined_switch_string += kSwitchValueSeparator + value; 343 // Append the switch and update the switches/arguments divider |begin_args_|. 344 argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); 345 } 346 347 void CommandLine::AppendSwitchASCII(const std::string& switch_string, 348 const std::string& value_string) { 349 #if defined(OS_WIN) 350 AppendSwitchNative(switch_string, ASCIIToUTF16(value_string)); 351 #elif defined(OS_POSIX) 352 AppendSwitchNative(switch_string, value_string); 353 #endif 354 } 355 356 void CommandLine::CopySwitchesFrom(const CommandLine& source, 357 const char* const switches[], 358 size_t count) { 359 for (size_t i = 0; i < count; ++i) { 360 if (source.HasSwitch(switches[i])) 361 AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i])); 362 } 363 } 364 365 CommandLine::StringVector CommandLine::GetArgs() const { 366 // Gather all arguments after the last switch (may include kSwitchTerminator). 367 StringVector args(argv_.begin() + begin_args_, argv_.end()); 368 // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) 369 StringVector::iterator switch_terminator = 370 std::find(args.begin(), args.end(), kSwitchTerminator); 371 if (switch_terminator != args.end()) 372 args.erase(switch_terminator); 373 return args; 374 } 375 376 void CommandLine::AppendArg(const std::string& value) { 377 #if defined(OS_WIN) 378 DCHECK(IsStringUTF8(value)); 379 AppendArgNative(UTF8ToWide(value)); 380 #elif defined(OS_POSIX) 381 AppendArgNative(value); 382 #endif 383 } 384 385 void CommandLine::AppendArgPath(const FilePath& path) { 386 AppendArgNative(path.value()); 387 } 388 389 void CommandLine::AppendArgNative(const CommandLine::StringType& value) { 390 argv_.push_back(value); 391 } 392 393 void CommandLine::AppendArguments(const CommandLine& other, 394 bool include_program) { 395 if (include_program) 396 SetProgram(other.GetProgram()); 397 AppendSwitchesAndArguments(this, other.argv()); 398 } 399 400 void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { 401 if (wrapper.empty()) 402 return; 403 // The wrapper may have embedded arguments (like "gdb --args"). In this case, 404 // we don't pretend to do anything fancy, we just split on spaces. 405 StringVector wrapper_argv = SplitString( 406 wrapper, FilePath::StringType(1, ' '), base::TRIM_WHITESPACE, 407 base::SPLIT_WANT_ALL); 408 // Prepend the wrapper and update the switches/arguments |begin_args_|. 409 argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end()); 410 begin_args_ += wrapper_argv.size(); 411 } 412 413 #if defined(OS_WIN) 414 void CommandLine::ParseFromString(const string16& command_line) { 415 string16 command_line_string; 416 TrimWhitespace(command_line, TRIM_ALL, &command_line_string); 417 if (command_line_string.empty()) 418 return; 419 420 int num_args = 0; 421 wchar_t** args = NULL; 422 args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); 423 424 DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " 425 << UTF16ToUTF8(command_line); 426 InitFromArgv(num_args, args); 427 LocalFree(args); 428 } 429 #endif 430 431 CommandLine::StringType CommandLine::GetCommandLineStringInternal( 432 bool quote_placeholders) const { 433 StringType string(argv_[0]); 434 #if defined(OS_WIN) 435 string = QuoteForCommandLineToArgvW(string, quote_placeholders); 436 #endif 437 StringType params(GetArgumentsStringInternal(quote_placeholders)); 438 if (!params.empty()) { 439 string.append(StringType(FILE_PATH_LITERAL(" "))); 440 string.append(params); 441 } 442 return string; 443 } 444 445 CommandLine::StringType CommandLine::GetArgumentsStringInternal( 446 bool /* quote_placeholders */) const { 447 StringType params; 448 // Append switches and arguments. 449 bool parse_switches = true; 450 for (size_t i = 1; i < argv_.size(); ++i) { 451 StringType arg = argv_[i]; 452 StringType switch_string; 453 StringType switch_value; 454 parse_switches &= arg != kSwitchTerminator; 455 if (i > 1) 456 params.append(StringType(FILE_PATH_LITERAL(" "))); 457 if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { 458 params.append(switch_string); 459 if (!switch_value.empty()) { 460 #if defined(OS_WIN) 461 switch_value = 462 QuoteForCommandLineToArgvW(switch_value, quote_placeholders); 463 #endif 464 params.append(kSwitchValueSeparator + switch_value); 465 } 466 } else { 467 #if defined(OS_WIN) 468 arg = QuoteForCommandLineToArgvW(arg, quote_placeholders); 469 #endif 470 params.append(arg); 471 } 472 } 473 return params; 474 } 475 476 void CommandLine::ResetStringPieces() { 477 switches_by_stringpiece_.clear(); 478 for (const auto& entry : switches_) 479 switches_by_stringpiece_[entry.first] = &(entry.second); 480 } 481 482 } // namespace base 483