summaryrefslogtreecommitdiffstats
path: root/base/command_line.cc
diff options
context:
space:
mode:
authormsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-14 01:10:24 +0000
committermsw@chromium.org <msw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-14 01:10:24 +0000
commita40ca43033ac86cc3224d843c233a71e94e72dbf (patch)
tree3172d0bd0d9430a8f155df047c6b14d188557abc /base/command_line.cc
parentdc09efb7dd87597a7e4b5fe790d0ce3a2511380e (diff)
downloadchromium_src-a40ca43033ac86cc3224d843c233a71e94e72dbf.zip
chromium_src-a40ca43033ac86cc3224d843c233a71e94e72dbf.tar.gz
chromium_src-a40ca43033ac86cc3224d843c233a71e94e72dbf.tar.bz2
Consolidate most CommandLine code across platforms.
Significant refactoring with some notable behavior changes: 1. Switches are appended preceding existing arguments (after other swtiches). 2. (Windows) command_line_string() is generated and properly quoted/escaped. 3. Appended switches will retain their (optional) included prefixes (--,-,/). Notable internal changes (shouldn't affect behavior): 1. (Windows) Generate the cl string, instead of storing&updating the original. 2. Explicitly retain switch prefixes (--,-,/) (was automatic in init*/ctor). Update (obvious) code expecting switches to be appended antecedent to args. Add Nico's test from: codereview.chromium.org/6728016/. An intermediary CL landed between patch set 3 and 4, see: http://codereview.chromium.org/6596020 BUG=73195,67764 TEST=Commandline usage. Review URL: http://codereview.chromium.org/6526040 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@85360 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/command_line.cc')
-rw-r--r--base/command_line.cc417
1 files changed, 167 insertions, 250 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() {
-}