diff options
Diffstat (limited to 'base')
-rw-r--r-- | base/command_line.cc | 417 | ||||
-rw-r--r-- | base/command_line.h | 70 | ||||
-rw-r--r-- | base/command_line_unittest.cc | 202 |
3 files changed, 340 insertions, 349 deletions
diff --git a/base/command_line.cc b/base/command_line.cc index b027d2a..b3a79eb 100644 --- a/base/command_line.cc +++ b/base/command_line.cc @@ -23,29 +23,79 @@ CommandLine* CommandLine::current_process_commandline_ = NULL; namespace { -typedef CommandLine::StringType::value_type CharType; - -const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); -const CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); +const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); +const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); // Since we use a lazy match, make sure that longer versions (like "--") are // listed before shorter versions (like "-") of similar prefixes. #if defined(OS_WIN) -const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; +const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; #elif defined(OS_POSIX) // Unixes don't use slash as a switch. -const CharType* const kSwitchPrefixes[] = {"--", "-"}; +const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; #endif +size_t GetSwitchPrefixLength(const CommandLine::StringType& string) { + for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { + CommandLine::StringType prefix(kSwitchPrefixes[i]); + if (string.find(prefix) == 0) + return prefix.length(); + } + return 0; +} + +// Fills in |switch_string| and |switch_value| if |string| is a switch. +// This will preserve the input switch prefix in the output |switch_string|. +bool IsSwitch(const CommandLine::StringType& string, + CommandLine::StringType* switch_string, + CommandLine::StringType* switch_value) { + switch_string->clear(); + switch_value->clear(); + if (GetSwitchPrefixLength(string) == 0) + return false; + + const size_t equals_position = string.find(kSwitchValueSeparator); + *switch_string = string.substr(0, equals_position); + if (equals_position != CommandLine::StringType::npos) + *switch_value = string.substr(equals_position + 1); + return true; +} + +// Append switches and arguments, keeping switches before arguments. +void AppendSwitchesAndArguments(CommandLine& command_line, + const CommandLine::StringVector& argv) { + bool parse_switches = true; + for (size_t i = 1; i < argv.size(); ++i) { + CommandLine::StringType arg = argv[i]; + TrimWhitespace(arg, TRIM_ALL, &arg); + + CommandLine::StringType switch_string; + CommandLine::StringType switch_value; + parse_switches &= (arg != kSwitchTerminator); + if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { #if defined(OS_WIN) -// Lowercase a string for case-insensitivity of switches. -// Is this desirable? It exists for backwards compatibility on Windows. -void Lowercase(std::string* arg) { - transform(arg->begin(), arg->end(), arg->begin(), tolower); + command_line.AppendSwitchNative(WideToASCII(switch_string), switch_value); +#elif defined(OS_POSIX) + command_line.AppendSwitchNative(switch_string, switch_value); +#endif + } else { + command_line.AppendArgNative(arg); + } + } +} + +// Lowercase switches for backwards compatiblity *on Windows*. +std::string LowerASCIIOnWindows(const std::string& string) { +#if defined(OS_WIN) + return StringToLowerASCII(string); +#elif defined(OS_POSIX) + return string; +#endif } -// Quote a string if necessary, such that CommandLineToArgvW() will always -// process it as a single argument. -std::wstring WindowsStyleQuote(const std::wstring& arg) { + +#if defined(OS_WIN) +// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. +std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg) { // We follow the quoting rules of CommandLineToArgvW. // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx if (arg.find_first_of(L" \\\"") == std::wstring::npos) { @@ -88,73 +138,30 @@ std::wstring WindowsStyleQuote(const std::wstring& arg) { } #endif -// Returns true and fills in |switch_string| and |switch_value| if -// |parameter_string| represents a switch. -bool IsSwitch(const CommandLine::StringType& parameter_string, - std::string* switch_string, - CommandLine::StringType* switch_value) { - switch_string->clear(); - switch_value->clear(); - - for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { - CommandLine::StringType prefix(kSwitchPrefixes[i]); - if (parameter_string.find(prefix) != 0) - continue; - - const size_t switch_start = prefix.length(); - const size_t equals_position = parameter_string.find( - kSwitchValueSeparator, switch_start); - CommandLine::StringType switch_native; - if (equals_position == CommandLine::StringType::npos) { - switch_native = parameter_string.substr(switch_start); - } else { - switch_native = parameter_string.substr( - switch_start, equals_position - switch_start); - *switch_value = parameter_string.substr(equals_position + 1); - } -#if defined(OS_WIN) - *switch_string = WideToASCII(switch_native); - Lowercase(switch_string); -#else - *switch_string = switch_native; -#endif - - return true; - } - - return false; -} - } // namespace -CommandLine::CommandLine(NoProgram no_program) { -#if defined(OS_POSIX) - // Push an empty argument, because we always assume argv_[0] is a program. - argv_.push_back(""); -#endif +CommandLine::CommandLine(NoProgram no_program) + : argv_(1), + begin_args_(1) { } -CommandLine::CommandLine(const FilePath& program) { -#if defined(OS_WIN) - if (!program.empty()) { - program_ = program.value(); - // TODO(evanm): proper quoting here. - command_line_string_ = L'"' + program.value() + L'"'; - } -#elif defined(OS_POSIX) - argv_.push_back(program.value()); -#endif +CommandLine::CommandLine(const FilePath& program) + : argv_(1), + begin_args_(1) { + SetProgram(program); } -#if defined(OS_POSIX) -CommandLine::CommandLine(int argc, const char* const* argv) { +CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) + : argv_(1), + begin_args_(1) { InitFromArgv(argc, argv); } -CommandLine::CommandLine(const StringVector& argv) { +CommandLine::CommandLine(const StringVector& argv) + : argv_(1), + begin_args_(1) { InitFromArgv(argv); } -#endif // OS_POSIX CommandLine::~CommandLine() { } @@ -162,7 +169,7 @@ CommandLine::~CommandLine() { // static void CommandLine::Init(int argc, const char* const* argv) { delete current_process_commandline_; - current_process_commandline_ = new CommandLine; + current_process_commandline_ = new CommandLine(NO_PROGRAM); #if defined(OS_WIN) current_process_commandline_->ParseFromString(::GetCommandLineW()); #elif defined(OS_POSIX) @@ -186,76 +193,76 @@ CommandLine* CommandLine::ForCurrentProcess() { #if defined(OS_WIN) // static CommandLine CommandLine::FromString(const std::wstring& command_line) { - CommandLine cmd; + CommandLine cmd(NO_PROGRAM); cmd.ParseFromString(command_line); return cmd; } -#endif // OS_WIN +#endif -#if defined(OS_POSIX) -void CommandLine::InitFromArgv(int argc, const char* const* argv) { +void CommandLine::InitFromArgv(int argc, + const CommandLine::CharType* const* argv) { + StringVector new_argv; for (int i = 0; i < argc; ++i) - argv_.push_back(argv[i]); - InitFromArgv(argv_); + new_argv.push_back(argv[i]); + InitFromArgv(new_argv); } void CommandLine::InitFromArgv(const StringVector& argv) { - argv_ = argv; - bool parse_switches = true; - for (size_t i = 1; i < argv_.size(); ++i) { - const std::string& arg = argv_[i]; - - if (!parse_switches) { - args_.push_back(arg); - continue; - } - - if (arg == kSwitchTerminator) { - parse_switches = false; - continue; - } - - std::string switch_string; - StringType switch_value; - if (IsSwitch(arg, &switch_string, &switch_value)) { - switches_[switch_string] = switch_value; - } else { - args_.push_back(arg); - } - } + argv_ = StringVector(1); + begin_args_ = 1; + SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); + AppendSwitchesAndArguments(*this, argv); } -#endif // OS_POSIX CommandLine::StringType CommandLine::command_line_string() const { + StringType string(argv_[0]); #if defined(OS_WIN) - return command_line_string_; -#elif defined(OS_POSIX) - return JoinString(argv_, ' '); + string = QuoteForCommandLineToArgvW(string); +#endif + // Append switches and arguments. + bool parse_switches = true; + for (size_t i = 1; i < argv_.size(); ++i) { + CommandLine::StringType arg = argv_[i]; + CommandLine::StringType switch_string; + CommandLine::StringType switch_value; + parse_switches &= arg != kSwitchTerminator; + string.append(StringType(FILE_PATH_LITERAL(" "))); + if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { + string.append(switch_string); + if (!switch_value.empty()) { +#if defined(OS_WIN) + switch_value = QuoteForCommandLineToArgvW(switch_value); +#endif + string.append(kSwitchValueSeparator + switch_value); + } + } + else { +#if defined(OS_WIN) + arg = QuoteForCommandLineToArgvW(arg); #endif + string.append(arg); + } + } + return string; } FilePath CommandLine::GetProgram() const { -#if defined(OS_WIN) - return FilePath(program_); -#else - DCHECK_GT(argv_.size(), 0U); return FilePath(argv_[0]); -#endif +} + +void CommandLine::SetProgram(const FilePath& program) { + TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]); } bool CommandLine::HasSwitch(const std::string& switch_string) const { - std::string lowercased_switch(switch_string); -#if defined(OS_WIN) - Lowercase(&lowercased_switch); -#endif - return switches_.find(lowercased_switch) != switches_.end(); + return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end(); } std::string CommandLine::GetSwitchValueASCII( const std::string& switch_string) const { - CommandLine::StringType value = GetSwitchValueNative(switch_string); + StringType value = GetSwitchValueNative(switch_string); if (!IsStringASCII(value)) { - LOG(WARNING) << "Value of --" << switch_string << " must be ASCII."; + LOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; return ""; } #if defined(OS_WIN) @@ -272,18 +279,9 @@ FilePath CommandLine::GetSwitchValuePath( CommandLine::StringType CommandLine::GetSwitchValueNative( const std::string& switch_string) const { - std::string lowercased_switch(switch_string); -#if defined(OS_WIN) - Lowercase(&lowercased_switch); -#endif - - SwitchMap::const_iterator result = switches_.find(lowercased_switch); - - if (result == switches_.end()) { - return CommandLine::StringType(); - } else { - return result->second; - } + SwitchMap::const_iterator result = switches_.end(); + result = switches_.find(LowerASCIIOnWindows(switch_string)); + return result == switches_.end() ? StringType() : result->second; } size_t CommandLine::GetSwitchCount() const { @@ -291,14 +289,7 @@ size_t CommandLine::GetSwitchCount() const { } void CommandLine::AppendSwitch(const std::string& switch_string) { -#if defined(OS_WIN) - command_line_string_.append(L" "); - command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string)); - switches_[switch_string] = L""; -#elif defined(OS_POSIX) - argv_.push_back(kSwitchPrefixes[0] + switch_string); - switches_[switch_string] = ""; -#endif + AppendSwitchNative(switch_string, StringType()); } void CommandLine::AppendSwitchPath(const std::string& switch_string, @@ -308,23 +299,21 @@ void CommandLine::AppendSwitchPath(const std::string& switch_string, void CommandLine::AppendSwitchNative(const std::string& switch_string, const CommandLine::StringType& value) { + std::string switch_key(LowerASCIIOnWindows(switch_string)); #if defined(OS_WIN) - StringType combined_switch_string = - kSwitchPrefixes[0] + ASCIIToWide(switch_string); - if (!value.empty()) - combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value); - - command_line_string_.append(L" "); - command_line_string_.append(combined_switch_string); - - switches_[switch_string] = value; + StringType combined_switch_string(ASCIIToWide(switch_key)); #elif defined(OS_POSIX) - StringType combined_switch_string = kSwitchPrefixes[0] + switch_string; + StringType combined_switch_string(switch_string); +#endif + size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); + switches_[switch_key.substr(prefix_length)] = value; + // Preserve existing switch prefixes in |argv_|; only append one if necessary. + if (prefix_length == 0) + combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; if (!value.empty()) combined_switch_string += kSwitchValueSeparator + value; - argv_.push_back(combined_switch_string); - switches_[switch_string] = value; -#endif + // Append the switch and update the switches/arguments divider |begin_args_|. + argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); } void CommandLine::AppendSwitchASCII(const std::string& switch_string, @@ -336,23 +325,26 @@ void CommandLine::AppendSwitchASCII(const std::string& switch_string, #endif } -void CommandLine::AppendSwitches(const CommandLine& other) { - SwitchMap::const_iterator i; - for (i = other.switches_.begin(); i != other.switches_.end(); ++i) - AppendSwitchNative(i->first, i->second); -} - void CommandLine::CopySwitchesFrom(const CommandLine& source, const char* const switches[], size_t count) { for (size_t i = 0; i < count; ++i) { - if (source.HasSwitch(switches[i])) { - StringType value = source.GetSwitchValueNative(switches[i]); - AppendSwitchNative(switches[i], value); - } + if (source.HasSwitch(switches[i])) + AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i])); } } +CommandLine::StringVector CommandLine::args() const { + // Gather all arguments after the last switch (may include kSwitchTerminator). + StringVector args(argv_.begin() + begin_args_, argv_.end()); + // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) + StringVector::iterator switch_terminator = + std::find(args.begin(), args.end(), kSwitchTerminator); + if (switch_terminator != args.end()) + args.erase(switch_terminator); + return args; +} + void CommandLine::AppendArg(const std::string& value) { #if defined(OS_WIN) DCHECK(IsStringUTF8(value)); @@ -367,117 +359,42 @@ void CommandLine::AppendArgPath(const FilePath& path) { } void CommandLine::AppendArgNative(const CommandLine::StringType& value) { -#if defined(OS_WIN) - command_line_string_.append(L" "); - command_line_string_.append(WindowsStyleQuote(value)); - args_.push_back(value); -#elif defined(OS_POSIX) - DCHECK(IsStringUTF8(value)); argv_.push_back(value); -#endif -} - -void CommandLine::AppendArgs(const CommandLine& other) { - if(other.args_.size() <= 0) - return; -#if defined(OS_WIN) - command_line_string_.append(L" --"); -#endif // OS_WIN - StringVector::const_iterator i; - for (i = other.args_.begin(); i != other.args_.end(); ++i) - AppendArgNative(*i); } void CommandLine::AppendArguments(const CommandLine& other, bool include_program) { -#if defined(OS_WIN) - // Verify include_program is used correctly. - DCHECK(!include_program || !other.GetProgram().empty()); if (include_program) - program_ = other.program_; - - if (!command_line_string_.empty()) - command_line_string_ += L' '; - - command_line_string_ += other.command_line_string_; -#elif defined(OS_POSIX) - // Verify include_program is used correctly. - // Logic could be shorter but this is clearer. - DCHECK_EQ(include_program, !other.GetProgram().empty()); - - if (include_program) - argv_[0] = other.argv_[0]; - - // Skip the first arg when copying since it's the program but push all - // arguments to our arg vector. - for (size_t i = 1; i < other.argv_.size(); ++i) - argv_.push_back(other.argv_[i]); -#endif - - SwitchMap::const_iterator i; - for (i = other.switches_.begin(); i != other.switches_.end(); ++i) - switches_[i->first] = i->second; + SetProgram(other.GetProgram()); + AppendSwitchesAndArguments(*this, other.argv()); } void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { - // The wrapper may have embedded arguments (like "gdb --args"). In this case, - // we don't pretend to do anything fancy, we just split on spaces. if (wrapper.empty()) return; - StringVector wrapper_and_args; -#if defined(OS_WIN) - base::SplitString(wrapper, ' ', &wrapper_and_args); - program_ = wrapper_and_args[0]; - command_line_string_ = wrapper + L" " + command_line_string_; -#elif defined(OS_POSIX) - base::SplitString(wrapper, ' ', &wrapper_and_args); - argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); -#endif + // The wrapper may have embedded arguments (like "gdb --args"). In this case, + // we don't pretend to do anything fancy, we just split on spaces. + StringVector wrapper_argv; + base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv); + // Prepend the wrapper and update the switches/arguments |begin_args_|. + argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end()); + begin_args_ += wrapper_argv.size(); } #if defined(OS_WIN) void CommandLine::ParseFromString(const std::wstring& command_line) { - TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); - - if (command_line_string_.empty()) + std::wstring command_line_string; + TrimWhitespace(command_line, TRIM_ALL, &command_line_string); + if (command_line_string.empty()) return; int num_args = 0; wchar_t** args = NULL; + args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args); - args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); - - // Populate program_ with the trimmed version of the first arg. - TrimWhitespace(args[0], TRIM_ALL, &program_); - - bool parse_switches = true; - for (int i = 1; i < num_args; ++i) { - std::wstring arg; - TrimWhitespace(args[i], TRIM_ALL, &arg); - - if (!parse_switches) { - args_.push_back(arg); - continue; - } - - if (arg == kSwitchTerminator) { - parse_switches = false; - continue; - } - - std::string switch_string; - std::wstring switch_value; - if (IsSwitch(arg, &switch_string, &switch_value)) { - switches_[switch_string] = switch_value; - } else { - args_.push_back(arg); - } - } - - if (args) - LocalFree(args); + PLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " << + command_line; + InitFromArgv(num_args, args); + LocalFree(args); } #endif - -CommandLine::CommandLine() { -} diff --git a/base/command_line.h b/base/command_line.h index 970e4a7..d9f3138 100644 --- a/base/command_line.h +++ b/base/command_line.h @@ -3,10 +3,11 @@ // found in the LICENSE file. // This class works with command lines: building and parsing. -// Switches can optionally have a value attached using an equals sign, as in -// "-switch=value". Arguments that aren't prefixed with a switch prefix are -// saved as extra arguments. An argument of "--" will terminate switch parsing, -// causing everything after to be considered as extra arguments. +// Arguments with prefixes ('--', '-', and on Windows, '/') are switches. +// Switches will precede all other arguments without switch prefixes. +// Switches can optionally have values, delimited by '=', e.g., "-switch=value". +// An argument of "--" will terminate switch parsing during initialization, +// interpreting subsequent tokens as non-switch arguments, regardless of prefix. // There is a singleton read-only CommandLine that represents the command line // that the current process was started with. It must be initialized in main(). @@ -34,8 +35,8 @@ class BASE_API CommandLine { typedef std::string StringType; #endif + typedef StringType::value_type CharType; typedef std::vector<StringType> StringVector; - // The type of map for parsed-out switch key and values. typedef std::map<std::string, StringType> SwitchMap; // A constructor for CommandLines that only carry switches and arguments. @@ -45,10 +46,9 @@ class BASE_API CommandLine { // Construct a new command line with |program| as argv[0]. explicit CommandLine(const FilePath& program); -#if defined(OS_POSIX) - CommandLine(int argc, const char* const* argv); + // Construct a new command line from an argument list. + CommandLine(int argc, const CharType* const* argv); explicit CommandLine(const StringVector& argv); -#endif ~CommandLine(); @@ -73,23 +73,21 @@ class BASE_API CommandLine { static CommandLine FromString(const std::wstring& command_line); #endif -#if defined(OS_POSIX) // Initialize from an argv vector. - void InitFromArgv(int argc, const char* const* argv); + void InitFromArgv(int argc, const CharType* const* argv); void InitFromArgv(const StringVector& argv); -#endif - // Returns the represented command line string. + // Constructs and returns the represented command line string. // CAUTION! This should be avoided because quoting behavior is unclear. + // TODO(msw): Rename GetCommandLineString. StringType command_line_string() const; -#if defined(OS_POSIX) // Returns the original command line string as a vector of strings. const StringVector& argv() const { return argv_; } -#endif - // Returns the program part of the command line string (the first item). + // Get and Set the program part of the command line string (the first item). FilePath GetProgram() const; + void SetProgram(const FilePath& program); // Returns true if this command line contains the given switch. // (Switch names are case-insensitive). @@ -109,35 +107,35 @@ class BASE_API CommandLine { const SwitchMap& GetSwitches() const { return switches_; } // Append a switch [with optional value] to the command line. - // CAUTION! Appending a switch after the "--" switch terminator is futile! + // Note: Switches will precede arguments regardless of appending order. void AppendSwitch(const std::string& switch_string); void AppendSwitchPath(const std::string& switch_string, const FilePath& path); void AppendSwitchNative(const std::string& switch_string, const StringType& value); void AppendSwitchASCII(const std::string& switch_string, const std::string& value); - void AppendSwitches(const CommandLine& other); // Copy a set of switches (and any values) from another command line. // Commonly used when launching a subprocess. - void CopySwitchesFrom(const CommandLine& source, const char* const switches[], + void CopySwitchesFrom(const CommandLine& source, + const char* const switches[], size_t count); // Get the remaining arguments to the command. - const StringVector& args() const { return args_; } + // TODO(msw): Rename GetArgs. + StringVector args() const; // Append an argument to the command line. Note that the argument is quoted // properly such that it is interpreted as one argument to the target command. // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8. + // Note: Switches will precede arguments regardless of appending order. void AppendArg(const std::string& value); void AppendArgPath(const FilePath& value); void AppendArgNative(const StringType& value); - void AppendArgs(const CommandLine& other); - // Append the arguments from another command line to this one. + // Append the switches and arguments from another command line to this one. // If |include_program| is true, include |other|'s program as well. - void AppendArguments(const CommandLine& other, - bool include_program); + void AppendArguments(const CommandLine& other, bool include_program); // Insert a command before the current command. // Common for debuggers, like "valgrind" or "gdb --args". @@ -150,34 +148,24 @@ class BASE_API CommandLine { #endif private: - // Disallow public default construction; a program name must be specified. + // Disallow default constructor; a program name must be explicitly specified. CommandLine(); + // Allow the copy constructor. A common pattern is to copy of the current + // process's command line and then add some flags to it. For example: + // CommandLine cl(*CommandLine::ForCurrentProcess()); + // cl.AppendSwitch(...); // The singleton CommandLine representing the current process's command line. static CommandLine* current_process_commandline_; - // We store a platform-native version of the command line, used when building - // up a new command line to be executed. This ifdef delimits that code. -#if defined(OS_WIN) - // The quoted, space-separated command line string. - StringType command_line_string_; - // The name of the program. - StringType program_; -#elif defined(OS_POSIX) - // The argv array, with the program name in argv_[0]. + // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* } StringVector argv_; -#endif // Parsed-out switch keys and values. SwitchMap switches_; - // Non-switch command line arguments. - StringVector args_; - - // Allow the copy constructor. A common pattern is to copy the current - // process's command line and then add some flags to it. For example: - // CommandLine cl(*CommandLine::ForCurrentProcess()); - // cl.AppendSwitch(...); + // The index after the program and switches, any arguments start here. + size_t begin_args_; }; #endif // BASE_COMMAND_LINE_H_ diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc index 5ce6911..a8d1eed 100644 --- a/base/command_line_unittest.cc +++ b/base/command_line_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -15,32 +15,34 @@ // and quotes. // Consider the command-line argument: q\"bs1\bs2\\bs3q\\\" // Here it is with C-style escapes. -#define TRICKY_QUOTED L"q\\\"bs1\\bs2\\\\bs3q\\\\\\\"" +static const CommandLine::StringType kTrickyQuoted = + FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\""); // It should be parsed by Windows as: q"bs1\bs2\\bs3q\" // Here that is with C-style escapes. -#define TRICKY L"q\"bs1\\bs2\\\\bs3q\\\"" +static const CommandLine::StringType kTricky = + FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\""); TEST(CommandLineTest, CommandLineConstructor) { -#if defined(OS_WIN) - CommandLine cl = CommandLine::FromString( - L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " - L"--other-switches=\"--dog=canine --cat=feline\" " - L"-spaetzle=Crepe -=loosevalue flan " - L"--input-translation=\"45\"--output-rotation " - L"--quotes=" TRICKY_QUOTED L" " - L"-- -- --not-a-switch " - L"\"in the time of submarines...\""); - EXPECT_FALSE(cl.command_line_string().empty()); -#elif defined(OS_POSIX) - const char* argv[] = {"program", "--foo=", "-bar", - "-spaetzel=pierogi", "-baz", "flim", - "--other-switches=--dog=canine --cat=feline", - "-spaetzle=Crepe", "-=loosevalue", "flan", - "--input-translation=45--output-rotation", - "--", "--", "--not-a-switch", - "in the time of submarines..."}; + const CommandLine::CharType* argv[] = { + FILE_PATH_LITERAL("program"), + FILE_PATH_LITERAL("--foo="), + FILE_PATH_LITERAL("-bAr"), + FILE_PATH_LITERAL("-spaetzel=pierogi"), + FILE_PATH_LITERAL("-baz"), + FILE_PATH_LITERAL("flim"), + FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"), + FILE_PATH_LITERAL("-spaetzle=Crepe"), + FILE_PATH_LITERAL("-=loosevalue"), + FILE_PATH_LITERAL("FLAN"), + FILE_PATH_LITERAL("--input-translation=45--output-rotation"), + FILE_PATH_LITERAL("--"), + FILE_PATH_LITERAL("--"), + FILE_PATH_LITERAL("--not-a-switch"), + FILE_PATH_LITERAL("\"in the time of submarines...\""), + FILE_PATH_LITERAL("unquoted arg-with-space")}; CommandLine cl(arraysize(argv), argv); -#endif + + EXPECT_FALSE(cl.command_line_string().empty()); EXPECT_FALSE(cl.HasSwitch("cruller")); EXPECT_FALSE(cl.HasSwitch("flim")); EXPECT_FALSE(cl.HasSwitch("program")); @@ -54,7 +56,7 @@ TEST(CommandLineTest, CommandLineConstructor) { cl.GetProgram().value()); EXPECT_TRUE(cl.HasSwitch("foo")); - EXPECT_TRUE(cl.HasSwitch("bar")); + EXPECT_TRUE(cl.HasSwitch("bAr")); EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_TRUE(cl.HasSwitch("spaetzle")); #if defined(OS_WIN) @@ -62,9 +64,66 @@ TEST(CommandLineTest, CommandLineConstructor) { #endif EXPECT_TRUE(cl.HasSwitch("other-switches")); EXPECT_TRUE(cl.HasSwitch("input-translation")); + + EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); + EXPECT_EQ("", cl.GetSwitchValueASCII("Foo")); + EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); + EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); + EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( + "other-switches")); + EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); + + const std::vector<CommandLine::StringType>& args = cl.args(); + ASSERT_EQ(6U, args.size()); + + std::vector<CommandLine::StringType>::const_iterator iter = args.begin(); + EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); + ++iter; + EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); + ++iter; + EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); + ++iter; + EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); + ++iter; + EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter); + ++iter; + EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter); + ++iter; + EXPECT_TRUE(iter == args.end()); +} + +TEST(CommandLineTest, CommandLineFromString) { #if defined(OS_WIN) + CommandLine cl = CommandLine::FromString( + L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " + L"--other-switches=\"--dog=canine --cat=feline\" " + L"-spaetzle=Crepe -=loosevalue FLAN " + L"--input-translation=\"45\"--output-rotation " + L"--quotes=" + kTrickyQuoted + L" " + L"-- -- --not-a-switch " + L"\"in the time of submarines...\""); + + EXPECT_FALSE(cl.command_line_string().empty()); + EXPECT_FALSE(cl.HasSwitch("cruller")); + EXPECT_FALSE(cl.HasSwitch("flim")); + EXPECT_FALSE(cl.HasSwitch("program")); + EXPECT_FALSE(cl.HasSwitch("dog")); + EXPECT_FALSE(cl.HasSwitch("cat")); + EXPECT_FALSE(cl.HasSwitch("output-rotation")); + EXPECT_FALSE(cl.HasSwitch("not-a-switch")); + EXPECT_FALSE(cl.HasSwitch("--")); + + EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), + cl.GetProgram().value()); + + EXPECT_TRUE(cl.HasSwitch("foo")); + EXPECT_TRUE(cl.HasSwitch("bar")); + EXPECT_TRUE(cl.HasSwitch("baz")); + EXPECT_TRUE(cl.HasSwitch("spaetzle")); + EXPECT_TRUE(cl.HasSwitch("SPAETZLE")); + EXPECT_TRUE(cl.HasSwitch("other-switches")); + EXPECT_TRUE(cl.HasSwitch("input-translation")); EXPECT_TRUE(cl.HasSwitch("quotes")); -#endif EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); EXPECT_EQ("", cl.GetSwitchValueASCII("Foo")); @@ -73,9 +132,7 @@ TEST(CommandLineTest, CommandLineConstructor) { EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( "other-switches")); EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); -#if defined(OS_WIN) - EXPECT_EQ(TRICKY, cl.GetSwitchValueNative("quotes")); -#endif + EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes")); const std::vector<CommandLine::StringType>& args = cl.args(); ASSERT_EQ(5U, args.size()); @@ -83,7 +140,7 @@ TEST(CommandLineTest, CommandLineConstructor) { std::vector<CommandLine::StringType>::const_iterator iter = args.begin(); EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("flan"), *iter); + EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); ++iter; @@ -92,69 +149,92 @@ TEST(CommandLineTest, CommandLineConstructor) { EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter); ++iter; EXPECT_TRUE(iter == args.end()); -#if defined(OS_POSIX) - const std::vector<std::string>& argvec = cl.argv(); - for (size_t i = 0; i < argvec.size(); i++) { - EXPECT_EQ(0, argvec[i].compare(argv[i])); - } + // Check that a generated string produces an equivalent command line. + CommandLine cl_duplicate = CommandLine::FromString(cl.command_line_string()); + EXPECT_EQ(cl.command_line_string(), cl_duplicate.command_line_string()); #endif } // Tests behavior with an empty input string. TEST(CommandLineTest, EmptyString) { #if defined(OS_WIN) - CommandLine cl = CommandLine::FromString(L""); - EXPECT_TRUE(cl.command_line_string().empty()); - EXPECT_TRUE(cl.GetProgram().empty()); -#elif defined(OS_POSIX) - CommandLine cl(0, NULL); - EXPECT_TRUE(cl.argv().empty()); + CommandLine cl_from_string = CommandLine::FromString(L""); + EXPECT_TRUE(cl_from_string.command_line_string().empty()); + EXPECT_TRUE(cl_from_string.GetProgram().empty()); + EXPECT_EQ(1U, cl_from_string.argv().size()); + EXPECT_TRUE(cl_from_string.args().empty()); #endif - EXPECT_TRUE(cl.args().empty()); + CommandLine cl_from_argv(0, NULL); + EXPECT_TRUE(cl_from_argv.command_line_string().empty()); + EXPECT_TRUE(cl_from_argv.GetProgram().empty()); + EXPECT_EQ(1U, cl_from_argv.argv().size()); + EXPECT_TRUE(cl_from_argv.args().empty()); } // Test methods for appending switches to a command line. TEST(CommandLineTest, AppendSwitches) { std::string switch1 = "switch1"; std::string switch2 = "switch2"; - std::string value = "value"; + std::string value2 = "value"; std::string switch3 = "switch3"; std::string value3 = "a value with spaces"; std::string switch4 = "switch4"; std::string value4 = "\"a value with quotes\""; std::string switch5 = "quotes"; - std::string value5 = WideToUTF8(TRICKY); + CommandLine::StringType value5 = kTricky; CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch(switch1); - cl.AppendSwitchASCII(switch2, value); + cl.AppendSwitchASCII(switch2, value2); cl.AppendSwitchASCII(switch3, value3); cl.AppendSwitchASCII(switch4, value4); - cl.AppendSwitchASCII(switch5, value5); + cl.AppendSwitchNative(switch5, value5); EXPECT_TRUE(cl.HasSwitch(switch1)); EXPECT_TRUE(cl.HasSwitch(switch2)); - EXPECT_EQ(value, cl.GetSwitchValueASCII(switch2)); + EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); EXPECT_TRUE(cl.HasSwitch(switch3)); EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3)); EXPECT_TRUE(cl.HasSwitch(switch4)); EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4)); EXPECT_TRUE(cl.HasSwitch(switch5)); - EXPECT_EQ(value5, cl.GetSwitchValueASCII(switch5)); + EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5)); #if defined(OS_WIN) - EXPECT_EQ(L"\"Program\" " + EXPECT_EQ(L"Program " L"--switch1 " L"--switch2=value " L"--switch3=\"a value with spaces\" " L"--switch4=\"\\\"a value with quotes\\\"\" " - L"--quotes=\"" TRICKY_QUOTED L"\"", + L"--quotes=\"" + kTrickyQuoted + L"\"", cl.command_line_string()); #endif } +TEST(CommandLineTest, AppendSwitchesDashDash) { + const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"), + FILE_PATH_LITERAL("--"), + FILE_PATH_LITERAL("--arg1") }; + CommandLine cl(arraysize(raw_argv), raw_argv); + + cl.AppendSwitch("switch1"); + cl.AppendSwitchASCII("switch2", "foo"); + + cl.AppendArg("--arg2"); + + EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"), + cl.command_line_string()); + CommandLine::StringVector cl_argv = cl.argv(); + EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]); + EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]); + EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]); + EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]); + EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]); + EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]); +} + // Tests that when AppendArguments is called that the program is set correctly // on the target CommandLine object and the switches from the source // CommandLine are added to the target. @@ -180,21 +260,27 @@ TEST(CommandLineTest, AppendArguments) { } #if defined(OS_WIN) -// Make sure that the program part of a command line is always quoted. +// Make sure that program paths of command_line_string are quoted as necessary. // This only makes sense on Windows and the test is basically here to guard // against regressions. TEST(CommandLineTest, ProgramQuotes) { + // Check that quotes are not added for paths without spaces. const FilePath kProgram(L"Program"); + CommandLine cl_program(kProgram); + EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value()); + EXPECT_EQ(kProgram.value(), cl_program.command_line_string()); + + const FilePath kProgramPath(L"Program Path"); // Check that quotes are not returned from GetProgram(). - CommandLine cl(kProgram); - EXPECT_EQ(kProgram.value(), cl.GetProgram().value()); - - // Verify that in the command line string, the program part is always quoted. - CommandLine::StringType cmd(cl.command_line_string()); - CommandLine::StringType program(cl.GetProgram().value()); - EXPECT_EQ('"', cmd[0]); - EXPECT_EQ(program, cmd.substr(1, program.length())); - EXPECT_EQ('"', cmd[program.length() + 1]); + CommandLine cl_program_path(kProgramPath); + EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value()); + + // Check that quotes are added to command line string paths containing spaces. + CommandLine::StringType cmd_string(cl_program_path.command_line_string()); + CommandLine::StringType program_string(cl_program_path.GetProgram().value()); + EXPECT_EQ('"', cmd_string[0]); + EXPECT_EQ(program_string, cmd_string.substr(1, program_string.length())); + EXPECT_EQ('"', cmd_string[program_string.length() + 1]); } #endif |