Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/command_line.h" | 5 #include "base/command_line.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
| 10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/singleton.h" | 12 #include "base/singleton.h" |
| 13 #include "base/string_split.h" | 13 #include "base/string_split.h" |
| 14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 15 #include "base/sys_string_conversions.h" | 15 #include "base/sys_string_conversions.h" |
| 16 #include "base/utf_string_conversions.h" | 16 #include "base/utf_string_conversions.h" |
| 17 #include "build/build_config.h" | 17 #include "build/build_config.h" |
| 18 | 18 |
| 19 #if defined(OS_WIN) | 19 #if defined(OS_WIN) |
| 20 #include <windows.h> | 20 #include <windows.h> |
| 21 #include <shellapi.h> | 21 #include <shellapi.h> |
| 22 #elif defined(OS_POSIX) | 22 #elif defined(OS_POSIX) |
| 23 #include <limits.h> | 23 #include <limits.h> |
| 24 #include <stdlib.h> | 24 #include <stdlib.h> |
| 25 #include <unistd.h> | 25 #include <unistd.h> |
| 26 #endif | 26 #endif |
| 27 | 27 |
| 28 CommandLine* CommandLine::current_process_commandline_ = NULL; | 28 CommandLine* CommandLine::current_process_commandline_ = NULL; |
| 29 | 29 |
| 30 // Since we use a lazy match, make sure that longer versions (like L"--") | 30 namespace { |
| 31 // are listed before shorter versions (like L"-") of similar prefixes. | 31 const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); |
| 32 const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); | |
| 33 // Since we use a lazy match, make sure that longer versions (like "--") | |
| 34 // are listed before shorter versions (like "-") of similar prefixes. | |
| 32 #if defined(OS_WIN) | 35 #if defined(OS_WIN) |
| 33 const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; | 36 const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; |
| 34 const wchar_t kSwitchTerminator[] = L"--"; | |
| 35 const wchar_t kSwitchValueSeparator[] = L"="; | |
| 36 #elif defined(OS_POSIX) | 37 #elif defined(OS_POSIX) |
| 37 // Unixes don't use slash as a switch. | 38 // Unixes don't use slash as a switch. |
| 38 const char* const kSwitchPrefixes[] = {"--", "-"}; | 39 const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; |
| 39 const char kSwitchTerminator[] = "--"; | |
| 40 const char kSwitchValueSeparator[] = "="; | |
| 41 #endif | 40 #endif |
| 42 | 41 |
| 43 #if defined(OS_WIN) | 42 void Lowercase(string* arg); |
| 44 // Lowercase a string. This is used to lowercase switch names. | |
| 45 // Is this what we really want? It seems crazy to me. I've left it in | |
| 46 // for backwards compatibility on Windows. | |
| 47 static void Lowercase(std::string* parameter) { | |
| 48 transform(parameter->begin(), parameter->end(), parameter->begin(), | |
| 49 tolower); | |
| 50 } | |
| 51 #endif | |
| 52 | 43 |
| 53 CommandLine::~CommandLine() { | 44 // Returns true and fills in |switch_string| and |switch_value| if |
| 54 } | 45 // |parameter_string| represents a switch. |
| 55 | 46 bool IsSwitch(const CommandLine::StringType& parameter_string, |
| 56 #if defined(OS_WIN) | 47 string* switch_string, |
| 57 CommandLine::CommandLine(NoProgram no_program) { | 48 CommandLine::StringType* switch_value) { |
| 58 } | |
| 59 | |
| 60 void CommandLine::ParseFromString(const std::wstring& command_line) { | |
| 61 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); | |
| 62 | |
| 63 if (command_line_string_.empty()) | |
| 64 return; | |
| 65 | |
| 66 int num_args = 0; | |
| 67 wchar_t** args = NULL; | |
| 68 | |
| 69 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); | |
| 70 | |
| 71 // Populate program_ with the trimmed version of the first arg. | |
| 72 TrimWhitespace(args[0], TRIM_ALL, &program_); | |
| 73 | |
| 74 bool parse_switches = true; | |
| 75 for (int i = 1; i < num_args; ++i) { | |
| 76 std::wstring arg; | |
| 77 TrimWhitespace(args[i], TRIM_ALL, &arg); | |
| 78 | |
| 79 if (!parse_switches) { | |
| 80 args_.push_back(arg); | |
| 81 continue; | |
| 82 } | |
| 83 | |
|
evanm
2011/02/24 01:01:05
It's hard to follow what's gone on in this file.
msw
2011/05/10 23:18:43
Done (move was r76339, this change is cleaner).
| |
| 84 if (arg == kSwitchTerminator) { | |
| 85 parse_switches = false; | |
| 86 continue; | |
| 87 } | |
| 88 | |
| 89 std::string switch_string; | |
| 90 std::wstring switch_value; | |
| 91 if (IsSwitch(arg, &switch_string, &switch_value)) { | |
| 92 switches_[switch_string] = switch_value; | |
| 93 } else { | |
| 94 args_.push_back(arg); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 if (args) | |
| 99 LocalFree(args); | |
| 100 } | |
| 101 | |
| 102 // static | |
| 103 CommandLine CommandLine::FromString(const std::wstring& command_line) { | |
| 104 CommandLine cmd; | |
| 105 cmd.ParseFromString(command_line); | |
| 106 return cmd; | |
| 107 } | |
| 108 | |
| 109 CommandLine::CommandLine(const FilePath& program) { | |
| 110 if (!program.empty()) { | |
| 111 program_ = program.value(); | |
| 112 // TODO(evanm): proper quoting here. | |
| 113 command_line_string_ = L'"' + program.value() + L'"'; | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 #elif defined(OS_POSIX) | |
| 118 CommandLine::CommandLine(NoProgram no_program) { | |
| 119 // Push an empty argument, because we always assume argv_[0] is a program. | |
| 120 argv_.push_back(""); | |
| 121 } | |
| 122 | |
| 123 CommandLine::CommandLine(int argc, const char* const* argv) { | |
| 124 InitFromArgv(argc, argv); | |
| 125 } | |
| 126 | |
| 127 CommandLine::CommandLine(const std::vector<std::string>& argv) { | |
| 128 InitFromArgv(argv); | |
| 129 } | |
| 130 | |
| 131 void CommandLine::InitFromArgv(int argc, const char* const* argv) { | |
| 132 for (int i = 0; i < argc; ++i) | |
| 133 argv_.push_back(argv[i]); | |
| 134 InitFromArgv(argv_); | |
| 135 } | |
| 136 | |
| 137 void CommandLine::InitFromArgv(const std::vector<std::string>& argv) { | |
| 138 argv_ = argv; | |
| 139 bool parse_switches = true; | |
| 140 for (size_t i = 1; i < argv_.size(); ++i) { | |
| 141 const std::string& arg = argv_[i]; | |
| 142 | |
| 143 if (!parse_switches) { | |
| 144 args_.push_back(arg); | |
| 145 continue; | |
| 146 } | |
| 147 | |
| 148 if (arg == kSwitchTerminator) { | |
| 149 parse_switches = false; | |
| 150 continue; | |
| 151 } | |
| 152 | |
| 153 std::string switch_string; | |
| 154 std::string switch_value; | |
| 155 if (IsSwitch(arg, &switch_string, &switch_value)) { | |
| 156 switches_[switch_string] = switch_value; | |
| 157 } else { | |
| 158 args_.push_back(arg); | |
| 159 } | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 CommandLine::CommandLine(const FilePath& program) { | |
| 164 argv_.push_back(program.value()); | |
| 165 } | |
| 166 | |
| 167 #endif | |
| 168 | |
| 169 // static | |
| 170 bool CommandLine::IsSwitch(const StringType& parameter_string, | |
| 171 std::string* switch_string, | |
| 172 StringType* switch_value) { | |
| 173 switch_string->clear(); | 49 switch_string->clear(); |
| 174 switch_value->clear(); | 50 switch_value->clear(); |
| 175 | 51 |
| 176 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { | 52 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { |
| 177 StringType prefix(kSwitchPrefixes[i]); | 53 CommandLine::StringType prefix(kSwitchPrefixes[i]); |
| 178 if (parameter_string.find(prefix) != 0) | 54 if (parameter_string.find(prefix) != 0) |
| 179 continue; | 55 continue; |
| 180 | 56 |
| 181 const size_t switch_start = prefix.length(); | 57 const size_t switch_start = prefix.length(); |
| 182 const size_t equals_position = parameter_string.find( | 58 const size_t equals_position = parameter_string.find( |
| 183 kSwitchValueSeparator, switch_start); | 59 kSwitchValueSeparator, switch_start); |
| 184 StringType switch_native; | 60 CommandLine::StringType switch_native; |
| 185 if (equals_position == StringType::npos) { | 61 if (equals_position == CommandLine::StringType::npos) { |
| 186 switch_native = parameter_string.substr(switch_start); | 62 switch_native = parameter_string.substr(switch_start); |
| 187 } else { | 63 } else { |
| 188 switch_native = parameter_string.substr( | 64 switch_native = parameter_string.substr( |
| 189 switch_start, equals_position - switch_start); | 65 switch_start, equals_position - switch_start); |
| 190 *switch_value = parameter_string.substr(equals_position + 1); | 66 *switch_value = parameter_string.substr(equals_position + 1); |
| 191 } | 67 } |
| 192 #if defined(OS_WIN) | 68 #if defined(OS_WIN) |
| 193 *switch_string = WideToASCII(switch_native); | 69 *switch_string = WideToASCII(switch_native); |
| 194 Lowercase(switch_string); | |
| 195 #else | 70 #else |
| 196 *switch_string = switch_native; | 71 *switch_string = switch_native; |
| 197 #endif | 72 #endif |
| 73 Lowercase(switch_string); | |
| 198 | 74 |
| 199 return true; | 75 return true; |
| 200 } | 76 } |
| 201 | 77 |
| 202 return false; | 78 return false; |
| 203 } | 79 } |
| 204 | 80 |
| 81 #if defined(OS_WIN) | |
| 82 // Lowercase a string for case-insensitivity of switches *on Windows*. | |
| 83 // Is this desirable? It exists for backwards compatibility on Windows. | |
| 84 void Lowercase(string* arg) { | |
| 85 transform(arg->begin(), arg->end(), arg->begin(), tolower); | |
| 86 } | |
| 87 | |
| 88 CommandLine::StringType Native(const string& string) { | |
| 89 return ASCIIToWide(string); | |
| 90 } | |
| 91 | |
| 92 CommandLine::StringType Native(const std::wstring& string) { return string; } | |
| 93 | |
| 94 // Quote a string if necessary *on Windows*, such that CommandLineToArgvW() will | |
| 95 // always process it as a single argument. | |
| 96 CommandLine::StringType Quote(const CommandLine::StringType& arg) { | |
| 97 // We follow the quoting rules of CommandLineToArgvW. | |
| 98 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx | |
| 99 if (arg.find_first_of(L" \\\"") == CommandLine::StringType::npos) { | |
| 100 // No quoting necessary. | |
| 101 return arg; | |
| 102 } | |
| 103 | |
| 104 std::wstring out; | |
| 105 out.push_back(L'"'); | |
| 106 for (size_t i = 0; i < arg.size(); ++i) { | |
| 107 if (arg[i] == '\\') { | |
| 108 // Find the extent of this run of backslashes. | |
| 109 size_t start = i, end = start + 1; | |
| 110 for (; end < arg.size() && arg[end] == '\\'; ++end) | |
| 111 /* empty */; | |
| 112 size_t backslash_count = end - start; | |
| 113 | |
| 114 // Backslashes are escapes only if the run is followed by a double quote. | |
| 115 // Since we also will end the string with a double quote, we escape for | |
| 116 // either a double quote or the end of the string. | |
| 117 if (end == arg.size() || arg[end] == '"') { | |
| 118 // To quote, we need to output 2x as many backslashes. | |
| 119 backslash_count *= 2; | |
| 120 } | |
| 121 for (size_t j = 0; j < backslash_count; ++j) | |
| 122 out.push_back('\\'); | |
| 123 | |
| 124 // Advance i to one before the end to balance i++ in loop. | |
| 125 i = end - 1; | |
| 126 } else if (arg[i] == '"') { | |
| 127 out.push_back('\\'); | |
| 128 out.push_back('"'); | |
| 129 } else { | |
| 130 out.push_back(arg[i]); | |
| 131 } | |
| 132 } | |
| 133 out.push_back('"'); | |
| 134 | |
| 135 return out; | |
| 136 } | |
| 137 #elif defined(OS_POSIX) | |
| 138 void Lowercase(string* arg) {} | |
| 139 | |
| 140 CommandLine::StringType Native(const string& string) { return string; } | |
| 141 | |
| 142 CommandLine::StringType Native(const std::wstring& string) { | |
| 143 return WideToASCII(string); | |
| 144 } | |
| 145 | |
| 146 CommandLine::StringType Quote(const CommandLine::StringType& string) { | |
| 147 return string; | |
| 148 } | |
| 149 #endif | |
| 150 } // namespace | |
| 151 | |
| 152 CommandLine::CommandLine(NoProgram no_program) { | |
| 153 SetProgram(StringType()); | |
| 154 } | |
| 155 | |
| 156 CommandLine::CommandLine(const FilePath& program) { | |
| 157 SetProgram(program.value()); | |
| 158 } | |
| 159 | |
| 160 CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) { | |
| 161 InitFromArgv(argc, argv); | |
| 162 } | |
| 163 | |
| 164 CommandLine::CommandLine(const StringVector& argv) { | |
| 165 InitFromArgv(argv); | |
| 166 } | |
| 167 | |
| 205 // static | 168 // static |
| 206 void CommandLine::Init(int argc, const char* const* argv) { | 169 void CommandLine::Init(int argc, const char* const* argv) { |
| 207 delete current_process_commandline_; | 170 delete current_process_commandline_; |
| 208 current_process_commandline_ = new CommandLine; | 171 current_process_commandline_ = new CommandLine; |
| 209 #if defined(OS_WIN) | 172 #if defined(OS_WIN) |
| 210 current_process_commandline_->ParseFromString(::GetCommandLineW()); | 173 current_process_commandline_->ParseFromString(::GetCommandLineW()); |
| 211 #elif defined(OS_POSIX) | 174 #elif defined(OS_POSIX) |
| 212 current_process_commandline_->InitFromArgv(argc, argv); | 175 current_process_commandline_->InitFromArgv(argc, argv); |
| 213 #endif | 176 #endif |
| 214 } | 177 } |
| 215 | 178 |
| 179 // static | |
| 216 void CommandLine::Reset() { | 180 void CommandLine::Reset() { |
| 217 DCHECK(current_process_commandline_ != NULL); | 181 DCHECK(current_process_commandline_); |
| 218 delete current_process_commandline_; | 182 delete current_process_commandline_; |
| 219 current_process_commandline_ = NULL; | 183 current_process_commandline_ = NULL; |
| 220 } | 184 } |
| 221 | 185 |
| 222 // static | 186 // static |
| 223 CommandLine* CommandLine::ForCurrentProcess() { | 187 CommandLine* CommandLine::ForCurrentProcess() { |
| 224 DCHECK(current_process_commandline_); | 188 DCHECK(current_process_commandline_); |
| 225 return current_process_commandline_; | 189 return current_process_commandline_; |
| 226 } | 190 } |
| 227 | 191 |
| 228 bool CommandLine::HasSwitch(const std::string& switch_string) const { | 192 #if defined(OS_WIN) |
| 193 // static | |
| 194 CommandLine CommandLine::FromString( | |
| 195 const CommandLine::StringType& command_line) { | |
| 196 CommandLine cmd; | |
| 197 cmd.ParseFromString(command_line); | |
| 198 return cmd; | |
| 199 } | |
| 200 #endif | |
| 201 | |
| 202 void CommandLine::InitFromArgv(int argc, | |
| 203 const CommandLine::CharType* const* argv) { | |
| 204 StringVector new_argv; | |
| 205 for (int i = 0; i < argc; ++i) | |
| 206 new_argv.push_back(Native(argv[i])); | |
| 207 InitFromArgv(new_argv); | |
| 208 } | |
| 209 | |
| 210 void CommandLine::InitFromArgv(const StringVector argv) { | |
| 211 argv_.clear(); | |
| 212 divider_ = 0; | |
| 213 if (argv.size() <= 0) | |
| 214 return; | |
| 215 | |
| 216 // Initialize the first program argument. | |
| 217 SetProgram(argv[0]); | |
| 218 | |
| 219 // Copy switches/arguments to our vector, keeping switches before arguments. | |
| 220 bool seen_kSwitchTerminator = false; | |
| 221 for (size_t i = 1; i < argv.size(); ++i) { | |
| 222 StringType arg = argv[i]; | |
| 223 TrimWhitespace(arg, TRIM_ALL, &arg); | |
| 224 | |
| 225 string switch_string; | |
| 226 StringType switch_value; | |
| 227 seen_kSwitchTerminator |= arg == kSwitchTerminator; | |
| 228 if (!seen_kSwitchTerminator && IsSwitch(arg, &switch_string, &switch_value)) | |
| 229 AppendSwitchNative(switch_string, switch_value); | |
| 230 else | |
| 231 AppendArgNative(arg); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 bool CommandLine::HasSwitch(const string& switch_string) const { | |
| 229 std::string lowercased_switch(switch_string); | 236 std::string lowercased_switch(switch_string); |
| 230 #if defined(OS_WIN) | |
| 231 Lowercase(&lowercased_switch); | 237 Lowercase(&lowercased_switch); |
| 232 #endif | |
| 233 return switches_.find(lowercased_switch) != switches_.end(); | 238 return switches_.find(lowercased_switch) != switches_.end(); |
| 234 } | 239 } |
| 235 | 240 |
| 236 std::string CommandLine::GetSwitchValueASCII( | 241 string CommandLine::GetSwitchValueASCII(const string& switch_string) const { |
| 237 const std::string& switch_string) const { | 242 StringType value = GetSwitchValueNative(switch_string); |
| 238 CommandLine::StringType value = GetSwitchValueNative(switch_string); | |
| 239 if (!IsStringASCII(value)) { | 243 if (!IsStringASCII(value)) { |
| 240 LOG(WARNING) << "Value of --" << switch_string << " must be ASCII."; | 244 LOG(WARNING) << "Value of --" << switch_string << " must be ASCII."; |
| 241 return ""; | 245 return ""; |
| 242 } | 246 } |
| 243 #if defined(OS_WIN) | 247 #if defined(OS_WIN) |
| 244 return WideToASCII(value); | 248 return WideToASCII(value); |
| 245 #else | 249 #else |
| 246 return value; | 250 return value; |
| 247 #endif | 251 #endif |
| 248 } | 252 } |
| 249 | 253 |
| 250 FilePath CommandLine::GetSwitchValuePath( | 254 FilePath CommandLine::GetSwitchValuePath(const string& switch_string) const { |
| 251 const std::string& switch_string) const { | |
| 252 return FilePath(GetSwitchValueNative(switch_string)); | 255 return FilePath(GetSwitchValueNative(switch_string)); |
| 253 } | 256 } |
| 254 | 257 |
| 255 CommandLine::StringType CommandLine::GetSwitchValueNative( | 258 CommandLine::StringType CommandLine::GetSwitchValueNative( |
| 256 const std::string& switch_string) const { | 259 const string& switch_string) const { |
| 257 std::string lowercased_switch(switch_string); | 260 std::string lowercased_switch(switch_string); |
| 258 #if defined(OS_WIN) | |
| 259 Lowercase(&lowercased_switch); | 261 Lowercase(&lowercased_switch); |
| 260 #endif | 262 SwitchMap::const_iterator result = switches_.find(lowercased_switch); |
| 263 return result == switches_.end() ? StringType() : result->second; | |
| 264 } | |
| 261 | 265 |
| 262 std::map<std::string, StringType>::const_iterator result = | 266 size_t CommandLine::GetSwitchCount() const { |
| 263 switches_.find(lowercased_switch); | 267 return switches_.size(); |
| 268 } | |
| 264 | 269 |
| 265 if (result == switches_.end()) { | 270 CommandLine::StringVector CommandLine::args() const { |
| 266 return CommandLine::StringType(); | 271 // Gather all arguments after the last switch (may include kSwitchTerminator). |
| 267 } else { | 272 StringVector args(argv_.begin() + divider_, argv_.end()); |
| 268 return result->second; | 273 |
| 269 } | 274 // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) |
| 275 StringVector::iterator it = args.begin(); | |
| 276 for (; it < args.end() && *it != kSwitchTerminator; ++it) | |
| 277 /* empty */; | |
| 278 if (it != args.end()) | |
| 279 args.erase(it); | |
| 280 | |
| 281 return args; | |
| 282 } | |
| 283 | |
| 284 CommandLine::StringType CommandLine::command_line_string() const { | |
| 285 return JoinString(argv_, FILE_PATH_LITERAL(' ')); | |
| 270 } | 286 } |
| 271 | 287 |
| 272 FilePath CommandLine::GetProgram() const { | 288 FilePath CommandLine::GetProgram() const { |
| 273 #if defined(OS_WIN) | 289 #if defined(OS_WIN) |
| 274 return FilePath(program_); | 290 // Ensure that quotes are not returned from GetProgram(). |
| 275 #else | 291 // TODO(evanm): proper quoting/handling here. |
| 276 DCHECK_GT(argv_.size(), 0U); | 292 return FilePath(argv_.size() > 0 ? argv_[0].substr(1, argv_[0].size() - 2) : |
| 277 return FilePath(argv_[0]); | 293 StringType()); |
| 294 #endif | |
| 295 return FilePath(argv_.size() > 0 ? argv_[0] : StringType()); | |
| 296 } | |
| 297 | |
| 298 void CommandLine::SetProgram(const CommandLine::StringType& program) { | |
| 299 CommandLine::StringType program_string(program); | |
| 300 TrimWhitespace(program_string, TRIM_ALL, &program_string); | |
| 301 | |
| 302 #if defined(OS_WIN) | |
| 303 // TODO(evanm): proper quoting here. | |
| 304 if (!program_string.empty()) | |
| 305 program_string = L'"' + program + L'"'; | |
| 306 #endif | |
| 307 | |
| 308 if (argv_.size() > 0){ | |
| 309 argv_[0] = program_string; | |
| 310 } else { | |
| 311 argv_.push_back(program_string); | |
| 312 divider_ = 1; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 void CommandLine::AppendSwitch(const string& switch_string) { | |
| 317 AppendSwitchNative(switch_string, CommandLine::StringType()); | |
| 318 } | |
| 319 | |
| 320 void CommandLine::AppendSwitchPath(const string& switch_string, | |
| 321 const FilePath& path) { | |
| 322 AppendSwitchNative(switch_string, path.value()); | |
| 323 } | |
| 324 | |
| 325 void CommandLine::AppendSwitchNative(const string& switch_string, | |
| 326 const CommandLine::StringType& value) { | |
| 327 CommandLine::StringType combined = kSwitchPrefixes[0] + Native(switch_string); | |
| 328 if (!value.empty()) | |
| 329 combined += kSwitchValueSeparator + Quote(value); | |
| 330 // Append the switch and update the switches/arguments |divider_|. | |
| 331 argv_.insert(argv_.begin() + divider_++, combined); | |
| 332 switches_[switch_string] = value; | |
| 333 } | |
| 334 | |
| 335 void CommandLine::AppendSwitchASCII(const string& switch_string, | |
| 336 const string& value_string) { | |
| 337 AppendSwitchNative(switch_string, Native(value_string)); | |
| 338 } | |
| 339 | |
| 340 void CommandLine::AppendSwitches(const CommandLine& other) { | |
| 341 SwitchMap::const_iterator i; | |
| 342 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) | |
| 343 AppendSwitchNative(i->first, i->second); | |
| 344 } | |
| 345 | |
| 346 void CommandLine::AppendArg(const string& value) { | |
| 347 #if defined(OS_WIN) | |
| 348 DCHECK(IsStringUTF8(value)); | |
| 349 AppendArgNative(UTF8ToWide(value)); | |
| 350 #elif defined(OS_POSIX) | |
| 351 AppendArgNative(value); | |
| 278 #endif | 352 #endif |
| 279 } | 353 } |
| 280 | 354 |
| 281 #if defined(OS_POSIX) | 355 void CommandLine::AppendArgPath(const FilePath& path) { |
| 282 std::string CommandLine::command_line_string() const { | 356 AppendArgNative(path.value()); |
| 283 return JoinString(argv_, ' '); | |
| 284 } | |
| 285 #endif | |
| 286 | |
| 287 #if defined(OS_WIN) | |
| 288 void CommandLine::AppendSwitch(const std::string& switch_string) { | |
| 289 command_line_string_.append(L" "); | |
| 290 command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string)); | |
| 291 switches_[switch_string] = L""; | |
| 292 } | 357 } |
| 293 | 358 |
| 294 void CommandLine::AppendSwitchASCII(const std::string& switch_string, | 359 void CommandLine::AppendArgNative(const CommandLine::StringType& value) { |
| 295 const std::string& value_string) { | 360 #if defined(OS_POSIX) |
| 296 AppendSwitchNative(switch_string, ASCIIToWide(value_string)); | |
| 297 } | |
| 298 | |
| 299 // Quote a string if necessary, such that CommandLineToArgvW() will | |
| 300 // always process it as a single argument. | |
| 301 static std::wstring WindowsStyleQuote(const std::wstring& arg) { | |
| 302 // We follow the quoting rules of CommandLineToArgvW. | |
| 303 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx | |
| 304 if (arg.find_first_of(L" \\\"") == std::wstring::npos) { | |
| 305 // No quoting necessary. | |
| 306 return arg; | |
| 307 } | |
| 308 | |
| 309 std::wstring out; | |
| 310 out.push_back(L'"'); | |
| 311 for (size_t i = 0; i < arg.size(); ++i) { | |
| 312 if (arg[i] == '\\') { | |
| 313 // Find the extent of this run of backslashes. | |
| 314 size_t start = i, end = start + 1; | |
| 315 for (; end < arg.size() && arg[end] == '\\'; ++end) | |
| 316 /* empty */; | |
| 317 size_t backslash_count = end - start; | |
| 318 | |
| 319 // Backslashes are escapes only if the run is followed by a double quote. | |
| 320 // Since we also will end the string with a double quote, we escape for | |
| 321 // either a double quote or the end of the string. | |
| 322 if (end == arg.size() || arg[end] == '"') { | |
| 323 // To quote, we need to output 2x as many backslashes. | |
| 324 backslash_count *= 2; | |
| 325 } | |
| 326 for (size_t j = 0; j < backslash_count; ++j) | |
| 327 out.push_back('\\'); | |
| 328 | |
| 329 // Advance i to one before the end to balance i++ in loop. | |
| 330 i = end - 1; | |
| 331 } else if (arg[i] == '"') { | |
| 332 out.push_back('\\'); | |
| 333 out.push_back('"'); | |
| 334 } else { | |
| 335 out.push_back(arg[i]); | |
| 336 } | |
| 337 } | |
| 338 out.push_back('"'); | |
| 339 | |
| 340 return out; | |
| 341 } | |
| 342 | |
| 343 void CommandLine::AppendSwitchNative(const std::string& switch_string, | |
| 344 const std::wstring& value) { | |
| 345 std::wstring combined_switch_string = | |
| 346 kSwitchPrefixes[0] + ASCIIToWide(switch_string); | |
| 347 if (!value.empty()) | |
| 348 combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value); | |
| 349 | |
| 350 command_line_string_.append(L" "); | |
| 351 command_line_string_.append(combined_switch_string); | |
| 352 | |
| 353 switches_[switch_string] = value; | |
| 354 } | |
| 355 | |
| 356 void CommandLine::AppendArg(const std::string& value) { | |
| 357 DCHECK(IsStringUTF8(value)); | 361 DCHECK(IsStringUTF8(value)); |
| 358 AppendArgNative(UTF8ToWide(value)); | 362 #endif |
| 359 } | 363 argv_.push_back(Quote(value)); |
| 360 | |
| 361 void CommandLine::AppendArgNative(const std::wstring& value) { | |
| 362 command_line_string_.append(L" "); | |
| 363 command_line_string_.append(WindowsStyleQuote(value)); | |
| 364 args_.push_back(value); | |
| 365 } | 364 } |
| 366 | 365 |
| 367 void CommandLine::AppendArguments(const CommandLine& other, | 366 void CommandLine::AppendArguments(const CommandLine& other, |
| 368 bool include_program) { | 367 bool include_program) { |
| 369 // Verify include_program is used correctly. | 368 // Verify include_program is used correctly. |
| 370 // Logic could be shorter but this is clearer. | 369 DCHECK(!include_program || other.argv_.size() > 0); |
| 371 DCHECK_EQ(include_program, !other.GetProgram().empty()); | |
| 372 if (include_program) | 370 if (include_program) |
| 373 program_ = other.program_; | 371 SetProgram(other.GetProgram().value()); |
| 374 | 372 |
| 375 if (!command_line_string_.empty()) | 373 // Copy switches/arguments to our vector, keeping switches before arguments. |
| 376 command_line_string_ += L' '; | 374 bool seen_kSwitchTerminator = false; |
| 375 for (size_t i = 1; i < other.argv_.size(); ++i) { | |
| 376 StringType arg = other.argv_[i]; | |
| 377 TrimWhitespace(arg, TRIM_ALL, &arg); | |
| 377 | 378 |
| 378 command_line_string_ += other.command_line_string_; | 379 string switch_string; |
| 379 | 380 StringType switch_value; |
| 380 std::map<std::string, StringType>::const_iterator i; | 381 seen_kSwitchTerminator |= arg == kSwitchTerminator; |
| 381 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) | 382 if (!seen_kSwitchTerminator && IsSwitch(arg, &switch_string, &switch_value)) |
| 382 switches_[i->first] = i->second; | 383 AppendSwitchNative(switch_string, switch_value); |
| 384 else | |
| 385 AppendArgNative(arg); | |
| 386 } | |
| 383 } | 387 } |
| 384 | 388 |
| 385 void CommandLine::PrependWrapper(const std::wstring& wrapper) { | 389 void CommandLine::PrependWrapper(const StringType& wrapper) { |
| 386 if (wrapper.empty()) | 390 if (wrapper.empty()) |
| 387 return; | 391 return; |
| 388 // The wrapper may have embedded arguments (like "gdb --args"). In this case, | 392 // The wrapper may have embedded arguments (like "gdb --args"). In this case, |
| 389 // we don't pretend to do anything fancy, we just split on spaces. | 393 // we don't pretend to do anything fancy, we just split on spaces. |
| 390 std::vector<std::wstring> wrapper_and_args; | 394 StringVector prefix; |
| 391 base::SplitString(wrapper, ' ', &wrapper_and_args); | 395 base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &prefix); |
| 392 program_ = wrapper_and_args[0]; | 396 // Prepend the wrapper and update the switches/arguments |divider_|. |
| 393 command_line_string_ = wrapper + L" " + command_line_string_; | 397 argv_.insert(argv_.begin(), prefix.begin(), prefix.end()); |
| 394 } | 398 divider_ += prefix.size(); |
| 395 | |
| 396 #elif defined(OS_POSIX) | |
| 397 void CommandLine::AppendSwitch(const std::string& switch_string) { | |
| 398 argv_.push_back(kSwitchPrefixes[0] + switch_string); | |
| 399 switches_[switch_string] = ""; | |
| 400 } | |
| 401 | |
| 402 void CommandLine::AppendSwitchNative(const std::string& switch_string, | |
| 403 const std::string& value) { | |
| 404 std::string combined_switch_string = kSwitchPrefixes[0] + switch_string; | |
| 405 if (!value.empty()) | |
| 406 combined_switch_string += kSwitchValueSeparator + value; | |
| 407 argv_.push_back(combined_switch_string); | |
| 408 switches_[switch_string] = value; | |
| 409 } | |
| 410 | |
| 411 void CommandLine::AppendSwitchASCII(const std::string& switch_string, | |
| 412 const std::string& value_string) { | |
| 413 AppendSwitchNative(switch_string, value_string); | |
| 414 } | |
| 415 | |
| 416 void CommandLine::AppendArg(const std::string& value) { | |
| 417 AppendArgNative(value); | |
| 418 } | |
| 419 | |
| 420 void CommandLine::AppendArgNative(const std::string& value) { | |
| 421 DCHECK(IsStringUTF8(value)); | |
| 422 argv_.push_back(value); | |
| 423 } | |
| 424 | |
| 425 void CommandLine::AppendArguments(const CommandLine& other, | |
| 426 bool include_program) { | |
| 427 // Verify include_program is used correctly. | |
| 428 // Logic could be shorter but this is clearer. | |
| 429 DCHECK_EQ(include_program, !other.GetProgram().empty()); | |
| 430 | |
| 431 if (include_program) | |
| 432 argv_[0] = other.argv_[0]; | |
| 433 | |
| 434 // Skip the first arg when copying since it's the program but push all | |
| 435 // arguments to our arg vector. | |
| 436 for (size_t i = 1; i < other.argv_.size(); ++i) | |
| 437 argv_.push_back(other.argv_[i]); | |
| 438 | |
| 439 std::map<std::string, StringType>::const_iterator i; | |
| 440 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) | |
| 441 switches_[i->first] = i->second; | |
| 442 } | |
| 443 | |
| 444 void CommandLine::PrependWrapper(const std::string& wrapper) { | |
| 445 // The wrapper may have embedded arguments (like "gdb --args"). In this case, | |
| 446 // we don't pretend to do anything fancy, we just split on spaces. | |
| 447 std::vector<std::string> wrapper_and_args; | |
| 448 base::SplitString(wrapper, ' ', &wrapper_and_args); | |
| 449 argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); | |
| 450 } | |
| 451 | |
| 452 #endif | |
| 453 | |
| 454 void CommandLine::AppendArgPath(const FilePath& path) { | |
| 455 AppendArgNative(path.value()); | |
| 456 } | |
| 457 | |
| 458 void CommandLine::AppendSwitchPath(const std::string& switch_string, | |
| 459 const FilePath& path) { | |
| 460 AppendSwitchNative(switch_string, path.value()); | |
| 461 } | 399 } |
| 462 | 400 |
| 463 void CommandLine::CopySwitchesFrom(const CommandLine& source, | 401 void CommandLine::CopySwitchesFrom(const CommandLine& source, |
| 464 const char* const switches[], | 402 const char* const switches[], |
| 465 size_t count) { | 403 size_t count) { |
| 466 for (size_t i = 0; i < count; ++i) { | 404 for (size_t i = 0; i < count; ++i) { |
| 467 if (source.HasSwitch(switches[i])) { | 405 if (source.HasSwitch(switches[i])) { |
| 468 StringType value = source.GetSwitchValueNative(switches[i]); | 406 StringType value = source.GetSwitchValueNative(switches[i]); |
| 469 AppendSwitchNative(switches[i], value); | 407 AppendSwitchNative(switches[i], value); |
| 470 } | 408 } |
| 471 } | 409 } |
| 472 } | 410 } |
| 473 | 411 |
| 474 // private | 412 #if defined(OS_WIN) |
| 475 CommandLine::CommandLine() { | 413 void CommandLine::ParseFromString(const CommandLine::StringType& command_line) { |
| 414 StringType command_line_string; | |
| 415 TrimWhitespace(command_line, TRIM_ALL, &command_line_string); | |
| 416 if (command_line_string.empty()) | |
| 417 return; | |
| 418 | |
| 419 int argc = 0; | |
| 420 wchar_t** argv = NULL; | |
| 421 argv = ::CommandLineToArgvW(command_line_string.c_str(), &argc); | |
| 422 | |
| 423 CHECK(argv); | |
| 424 InitFromArgv(argc, argv); | |
| 425 LocalFree(argv); | |
| 476 } | 426 } |
| 477 | 427 #endif |
| 478 // static | |
| 479 CommandLine* CommandLine::ForCurrentProcessMutable() { | |
| 480 DCHECK(current_process_commandline_); | |
| 481 return current_process_commandline_; | |
| 482 } | |
| OLD | NEW |