command_line.cc (12511B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 #include "base/command_line.h" 8 9 #if defined(XP_WIN) 10 # include <windows.h> 11 # include <shellapi.h> 12 # include "mozilla/DynamicallyLinkedFunctionPtr.h" 13 #endif 14 15 #include "base/logging.h" 16 #include "base/string_piece.h" 17 #include "base/string_util.h" 18 #include "base/sys_string_conversions.h" 19 20 CommandLine* CommandLine::current_process_commandline_ = NULL; 21 22 // Since we use a lazy match, make sure that longer versions (like L"--") 23 // are listed before shorter versions (like L"-") of similar prefixes. 24 #if defined(XP_WIN) 25 const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; 26 const wchar_t kSwitchTerminator[] = L"--"; 27 const wchar_t kSwitchValueSeparator[] = L"="; 28 #else 29 // Unixes don't use slash as a switch. 30 const char* const kSwitchPrefixes[] = {"--", "-"}; 31 const char kSwitchTerminator[] = "--"; 32 const char kSwitchValueSeparator[] = "="; 33 #endif 34 35 #if defined(XP_WIN) 36 // Lowercase a string. This is used to lowercase switch names. 37 // Is this what we really want? It seems crazy to me. I've left it in 38 // for backwards compatibility on Windows. 39 static void Lowercase(std::wstring* parameter) { 40 transform(parameter->begin(), parameter->end(), parameter->begin(), tolower); 41 } 42 #endif 43 44 #if defined(XP_WIN) 45 void CommandLine::ParseFromString(const std::wstring& command_line) { 46 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); 47 48 if (command_line_string_.empty()) return; 49 50 int num_args = 0; 51 wchar_t** args = NULL; 52 // When calling CommandLineToArgvW, use the API set if available. 53 // Doing so will bypass loading shell32.dll on Win8+. 54 mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CommandLineToArgvW)> 55 pCommandLineToArgvW(L"api-ms-win-downlevel-shell32-l1-1-0.dll", 56 "CommandLineToArgvW"); 57 if (pCommandLineToArgvW) { 58 args = pCommandLineToArgvW(command_line_string_.c_str(), &num_args); 59 } else { 60 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); 61 } 62 63 // Populate program_ with the trimmed version of the first arg. 64 TrimWhitespace(args[0], TRIM_ALL, &program_); 65 66 bool parse_switches = true; 67 for (int i = 1; i < num_args; ++i) { 68 std::wstring arg; 69 TrimWhitespace(args[i], TRIM_ALL, &arg); 70 71 if (!parse_switches) { 72 loose_values_.push_back(arg); 73 continue; 74 } 75 76 if (arg == kSwitchTerminator) { 77 parse_switches = false; 78 continue; 79 } 80 81 std::string switch_string; 82 std::wstring switch_value; 83 if (IsSwitch(arg, &switch_string, &switch_value)) { 84 switches_[switch_string] = switch_value; 85 } else { 86 loose_values_.push_back(arg); 87 } 88 } 89 90 if (args) LocalFree(args); 91 } 92 CommandLine::CommandLine(const std::wstring& program) { 93 if (!program.empty()) { 94 program_ = program; 95 command_line_string_ = L'"' + program + L'"'; 96 } 97 } 98 #else 99 CommandLine::CommandLine(int argc, const char* const* argv) { 100 for (int i = 0; i < argc; ++i) argv_.push_back(argv[i]); 101 InitFromArgv(); 102 } 103 CommandLine::CommandLine(const std::vector<std::string>& argv) { 104 argv_ = argv; 105 InitFromArgv(); 106 } 107 108 void CommandLine::InitFromArgv() { 109 bool parse_switches = true; 110 for (size_t i = 1; i < argv_.size(); ++i) { 111 const std::string& arg = argv_[i]; 112 113 if (!parse_switches) { 114 loose_values_.push_back(arg); 115 continue; 116 } 117 118 if (arg == kSwitchTerminator) { 119 parse_switches = false; 120 continue; 121 } 122 123 std::string switch_string; 124 std::string switch_value; 125 if (IsSwitch(arg, &switch_string, &switch_value)) { 126 switches_[switch_string] = switch_value; 127 } else { 128 loose_values_.push_back(arg); 129 } 130 } 131 } 132 133 CommandLine::CommandLine(const std::wstring& program) { 134 argv_.push_back(WideToASCII(program)); 135 } 136 #endif 137 138 // static 139 bool CommandLine::IsSwitch(const StringType& parameter_string, 140 std::string* switch_string, 141 StringType* switch_value) { 142 switch_string->clear(); 143 switch_value->clear(); 144 145 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { 146 StringType prefix(kSwitchPrefixes[i]); 147 if (parameter_string.find(prefix) != 0) continue; 148 149 const size_t switch_start = prefix.length(); 150 const size_t equals_position = 151 parameter_string.find(kSwitchValueSeparator, switch_start); 152 StringType switch_native; 153 if (equals_position == StringType::npos) { 154 switch_native = parameter_string.substr(switch_start); 155 } else { 156 switch_native = 157 parameter_string.substr(switch_start, equals_position - switch_start); 158 *switch_value = parameter_string.substr(equals_position + 1); 159 } 160 #if defined(XP_WIN) 161 Lowercase(&switch_native); 162 *switch_string = WideToASCII(switch_native); 163 #else 164 *switch_string = switch_native; 165 #endif 166 167 return true; 168 } 169 170 return false; 171 } 172 173 // static 174 void CommandLine::Init(int argc, const char* const* argv) { 175 DCHECK(current_process_commandline_ == NULL); 176 #if defined(XP_WIN) 177 current_process_commandline_ = new CommandLine; 178 current_process_commandline_->ParseFromString(::GetCommandLineW()); 179 #else 180 current_process_commandline_ = new CommandLine(argc, argv); 181 #endif 182 } 183 184 void CommandLine::Terminate() { 185 DCHECK(current_process_commandline_ != NULL); 186 delete current_process_commandline_; 187 current_process_commandline_ = NULL; 188 } 189 190 bool CommandLine::HasSwitch(const std::wstring& switch_string) const { 191 std::wstring lowercased_switch(switch_string); 192 #if defined(XP_WIN) 193 Lowercase(&lowercased_switch); 194 #endif 195 return switches_.find(WideToASCII(lowercased_switch)) != switches_.end(); 196 } 197 198 std::wstring CommandLine::GetSwitchValue( 199 const std::wstring& switch_string) const { 200 std::wstring lowercased_switch(switch_string); 201 #if defined(XP_WIN) 202 Lowercase(&lowercased_switch); 203 #endif 204 205 std::map<std::string, StringType>::const_iterator result = 206 switches_.find(WideToASCII(lowercased_switch)); 207 208 if (result == switches_.end()) { 209 return L""; 210 } else { 211 #if defined(XP_WIN) 212 return result->second; 213 #else 214 return ASCIIToWide(result->second); 215 #endif 216 } 217 } 218 219 #if defined(XP_WIN) 220 std::vector<std::wstring> CommandLine::GetLooseValues() const { 221 return loose_values_; 222 } 223 std::wstring CommandLine::program() const { return program_; } 224 #else 225 std::vector<std::wstring> CommandLine::GetLooseValues() const { 226 std::vector<std::wstring> values; 227 for (size_t i = 0; i < loose_values_.size(); ++i) 228 values.push_back(ASCIIToWide(loose_values_[i])); 229 return values; 230 } 231 std::wstring CommandLine::program() const { 232 DCHECK(argv_.size() > 0); 233 return ASCIIToWide(argv_[0]); 234 } 235 #endif 236 237 // static 238 std::wstring CommandLine::PrefixedSwitchString( 239 const std::wstring& switch_string) { 240 return StringPrintf(L"%ls%ls", kSwitchPrefixes[0], switch_string.c_str()); 241 } 242 243 // static 244 std::wstring CommandLine::PrefixedSwitchStringWithValue( 245 const std::wstring& switch_string, const std::wstring& value_string) { 246 if (value_string.empty()) { 247 return PrefixedSwitchString(switch_string); 248 } 249 250 return StringPrintf(L"%ls%ls%ls%ls", kSwitchPrefixes[0], 251 switch_string.c_str(), kSwitchValueSeparator, 252 value_string.c_str()); 253 } 254 255 #if defined(XP_WIN) 256 void CommandLine::AppendSwitch(const std::wstring& switch_string) { 257 std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string); 258 command_line_string_.append(L" "); 259 command_line_string_.append(prefixed_switch_string); 260 switches_[WideToASCII(switch_string)] = L""; 261 } 262 263 // Quote a string if necessary, such that CommandLineToArgvW() will 264 // always process it as a single argument. 265 static std::wstring WindowsStyleQuote(const std::wstring& arg) { 266 // We follow the quoting rules of CommandLineToArgvW. 267 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx 268 if (arg.find_first_of(L" \\\"\t") == std::wstring::npos) { 269 // No quoting necessary. 270 return arg; 271 } 272 273 std::wstring out; 274 out.push_back(L'"'); 275 for (size_t i = 0; i < arg.size(); ++i) { 276 if (arg[i] == '\\') { 277 // Find the extent of this run of backslashes. 278 size_t start = i, end = start + 1; 279 for (; end < arg.size() && arg[end] == '\\'; ++end) /* empty */ 280 ; 281 size_t backslash_count = end - start; 282 283 // Backslashes are escapes only if the run is followed by a double quote. 284 // Since we also will end the string with a double quote, we escape for 285 // either a double quote or the end of the string. 286 if (end == arg.size() || arg[end] == '"') { 287 // To quote, we need to output 2x as many backslashes. 288 backslash_count *= 2; 289 } 290 for (size_t j = 0; j < backslash_count; ++j) out.push_back('\\'); 291 292 // Advance i to one before the end to balance i++ in loop. 293 i = end - 1; 294 } else if (arg[i] == '"') { 295 out.push_back('\\'); 296 out.push_back('"'); 297 } else { 298 out.push_back(arg[i]); 299 } 300 } 301 out.push_back('"'); 302 303 return out; 304 } 305 306 void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, 307 const std::wstring& value_string) { 308 std::wstring quoted_value_string = WindowsStyleQuote(value_string); 309 std::wstring combined_switch_string = 310 PrefixedSwitchStringWithValue(switch_string, quoted_value_string); 311 312 command_line_string_.append(L" "); 313 command_line_string_.append(combined_switch_string); 314 315 switches_[WideToASCII(switch_string)] = value_string; 316 } 317 318 void CommandLine::AppendLooseValue(const std::wstring& value) { 319 command_line_string_.append(L" "); 320 command_line_string_.append(WindowsStyleQuote(value)); 321 } 322 323 void CommandLine::AppendArguments(const CommandLine& other, 324 bool include_program) { 325 // Verify include_program is used correctly. 326 // Logic could be shorter but this is clearer. 327 DCHECK(include_program ? !other.program().empty() : other.program().empty()); 328 command_line_string_ += L" " + other.command_line_string_; 329 std::map<std::string, StringType>::const_iterator i; 330 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) 331 switches_[i->first] = i->second; 332 } 333 334 void CommandLine::PrependWrapper(const std::wstring& wrapper) { 335 // The wrapper may have embedded arguments (like "gdb --args"). In this case, 336 // we don't pretend to do anything fancy, we just split on spaces. 337 std::vector<std::wstring> wrapper_and_args; 338 SplitString(wrapper, ' ', &wrapper_and_args); 339 program_ = wrapper_and_args[0]; 340 command_line_string_ = wrapper + L" " + command_line_string_; 341 } 342 343 #else 344 void CommandLine::AppendSwitch(const std::wstring& switch_string) { 345 std::string ascii_switch = WideToASCII(switch_string); 346 argv_.push_back(kSwitchPrefixes[0] + ascii_switch); 347 switches_[ascii_switch] = ""; 348 } 349 350 void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, 351 const std::wstring& value_string) { 352 std::string ascii_switch = WideToASCII(switch_string); 353 std::string ascii_value = WideToASCII(value_string); 354 355 argv_.push_back(kSwitchPrefixes[0] + ascii_switch + kSwitchValueSeparator + 356 ascii_value); 357 switches_[ascii_switch] = ascii_value; 358 } 359 360 void CommandLine::AppendLooseValue(const std::wstring& value) { 361 argv_.push_back(WideToASCII(value)); 362 } 363 364 void CommandLine::AppendArguments(const CommandLine& other, 365 bool include_program) { 366 // Verify include_program is used correctly. 367 // Logic could be shorter but this is clearer. 368 DCHECK(include_program ? !other.program().empty() : other.program().empty()); 369 370 size_t first_arg = include_program ? 0 : 1; 371 for (size_t i = first_arg; i < other.argv_.size(); ++i) 372 argv_.push_back(other.argv_[i]); 373 std::map<std::string, StringType>::const_iterator i; 374 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) 375 switches_[i->first] = i->second; 376 } 377 378 void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) { 379 // The wrapper may have embedded arguments (like "gdb --args"). In this case, 380 // we don't pretend to do anything fancy, we just split on spaces. 381 const std::string wrapper = WideToASCII(wrapper_wide); 382 std::vector<std::string> wrapper_and_args; 383 SplitString(wrapper, ' ', &wrapper_and_args); 384 argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); 385 } 386 387 #endif