summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rw-r--r--base/command_line.cc417
-rw-r--r--base/command_line.h70
-rw-r--r--base/command_line_unittest.cc202
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