1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/command_line.h" 6 7 #if defined(OS_WIN) 8 #include <windows.h> 9 #include <shellapi.h> 10 #elif defined(OS_POSIX) 11 #include <limits.h> 12 #include <stdlib.h> 13 #include <unistd.h> 14 #endif 15 #if defined(OS_LINUX) 16 #include <sys/prctl.h> 17 #endif 18 19 #include <algorithm> 20 21 #include "base/file_path.h" 22 #include "base/logging.h" 23 #include "base/singleton.h" 24 #include "base/string_piece.h" 25 #include "base/string_util.h" 26 #include "base/sys_string_conversions.h" 27 28 #if defined(OS_LINUX) 29 // Linux/glibc doesn't natively have setproctitle(). 30 #include "base/setproctitle_linux.h" 31 #endif 32 33 CommandLine* CommandLine::current_process_commandline_ = NULL; 34 35 // Since we use a lazy match, make sure that longer versions (like L"--") 36 // are listed before shorter versions (like L"-") of similar prefixes. 37 #if defined(OS_WIN) 38 const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; 39 const wchar_t kSwitchTerminator[] = L"--"; 40 const wchar_t kSwitchValueSeparator[] = L"="; 41 #elif defined(OS_POSIX) 42 // Unixes don't use slash as a switch. 43 const char* const kSwitchPrefixes[] = {"--", "-"}; 44 const char kSwitchTerminator[] = "--"; 45 const char kSwitchValueSeparator[] = "="; 46 #endif 47 48 #if defined(OS_WIN) 49 // Lowercase a string. This is used to lowercase switch names. 50 // Is this what we really want? It seems crazy to me. I've left it in 51 // for backwards compatibility on Windows. 52 static void Lowercase(std::string* parameter) { 53 transform(parameter->begin(), parameter->end(), parameter->begin(), 54 tolower); 55 } 56 #endif 57 58 #if defined(OS_WIN) 59 CommandLine::CommandLine(ArgumentsOnly args_only) { 60 } 61 62 void CommandLine::ParseFromString(const std::wstring& command_line) { 63 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); 64 65 if (command_line_string_.empty()) 66 return; 67 68 int num_args = 0; 69 wchar_t** args = NULL; 70 71 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); 72 73 // Populate program_ with the trimmed version of the first arg. 74 TrimWhitespace(args[0], TRIM_ALL, &program_); 75 76 bool parse_switches = true; 77 for (int i = 1; i < num_args; ++i) { 78 std::wstring arg; 79 TrimWhitespace(args[i], TRIM_ALL, &arg); 80 81 if (!parse_switches) { 82 loose_values_.push_back(arg); 83 continue; 84 } 85 86 if (arg == kSwitchTerminator) { 87 parse_switches = false; 88 continue; 89 } 90 91 std::string switch_string; 92 std::wstring switch_value; 93 if (IsSwitch(arg, &switch_string, &switch_value)) { 94 switches_[switch_string] = switch_value; 95 } else { 96 loose_values_.push_back(arg); 97 } 98 } 99 100 if (args) 101 LocalFree(args); 102 } 103 104 CommandLine::CommandLine(const FilePath& program) { 105 if (!program.empty()) { 106 program_ = program.value(); 107 command_line_string_ = L'"' + program.value() + L'"'; 108 } 109 } 110 111 #elif defined(OS_POSIX) 112 CommandLine::CommandLine(ArgumentsOnly args_only) { 113 // Push an empty argument, because we always assume argv_[0] is a program. 114 argv_.push_back(""); 115 } 116 117 void CommandLine::InitFromArgv(int argc, const char* const* argv) { 118 for (int i = 0; i < argc; ++i) 119 argv_.push_back(argv[i]); 120 InitFromArgv(argv_); 121 } 122 123 void CommandLine::InitFromArgv(const std::vector<std::string>& argv) { 124 argv_ = argv; 125 bool parse_switches = true; 126 for (size_t i = 1; i < argv_.size(); ++i) { 127 const std::string& arg = argv_[i]; 128 129 if (!parse_switches) { 130 loose_values_.push_back(arg); 131 continue; 132 } 133 134 if (arg == kSwitchTerminator) { 135 parse_switches = false; 136 continue; 137 } 138 139 std::string switch_string; 140 std::string switch_value; 141 if (IsSwitch(arg, &switch_string, &switch_value)) { 142 switches_[switch_string] = switch_value; 143 } else { 144 loose_values_.push_back(arg); 145 } 146 } 147 } 148 149 CommandLine::CommandLine(const FilePath& program) { 150 argv_.push_back(program.value()); 151 } 152 153 #endif 154 155 // static 156 bool CommandLine::IsSwitch(const StringType& parameter_string, 157 std::string* switch_string, 158 StringType* switch_value) { 159 switch_string->clear(); 160 switch_value->clear(); 161 162 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { 163 StringType prefix(kSwitchPrefixes[i]); 164 if (parameter_string.find(prefix) != 0) 165 continue; 166 167 const size_t switch_start = prefix.length(); 168 const size_t equals_position = parameter_string.find( 169 kSwitchValueSeparator, switch_start); 170 StringType switch_native; 171 if (equals_position == StringType::npos) { 172 switch_native = parameter_string.substr(switch_start); 173 } else { 174 switch_native = parameter_string.substr( 175 switch_start, equals_position - switch_start); 176 *switch_value = parameter_string.substr(equals_position + 1); 177 } 178 #if defined(OS_WIN) 179 *switch_string = WideToASCII(switch_native); 180 Lowercase(switch_string); 181 #else 182 *switch_string = switch_native; 183 #endif 184 185 return true; 186 } 187 188 return false; 189 } 190 191 // static 192 void CommandLine::Init(int argc, const char* const* argv) { 193 delete current_process_commandline_; 194 current_process_commandline_ = new CommandLine; 195 #if defined(OS_WIN) 196 current_process_commandline_->ParseFromString(::GetCommandLineW()); 197 #elif defined(OS_POSIX) 198 current_process_commandline_->InitFromArgv(argc, argv); 199 #endif 200 201 #if defined(OS_LINUX) 202 if (argv) 203 setproctitle_init(const_cast<char**>(argv)); 204 #endif 205 } 206 207 #if defined(OS_POSIX) && !defined(OS_MACOSX) 208 // static 209 void CommandLine::SetProcTitle() { 210 // Build a single string which consists of all the arguments separated 211 // by spaces. We can't actually keep them separate due to the way the 212 // setproctitle() function works. 213 std::string title; 214 bool have_argv0 = false; 215 #if defined(OS_LINUX) 216 // In Linux we sometimes exec ourselves from /proc/self/exe, but this makes us 217 // show up as "exe" in process listings. Read the symlink /proc/self/exe and 218 // use the path it points at for our process title. Note that this is only for 219 // display purposes and has no TOCTTOU security implications. 220 char buffer[PATH_MAX]; 221 // Note: readlink() does not append a null byte to terminate the string. 222 ssize_t length = readlink("/proc/self/exe", buffer, sizeof(buffer)); 223 DCHECK(length <= static_cast<ssize_t>(sizeof(buffer))); 224 if (length > 0) { 225 have_argv0 = true; 226 title.assign(buffer, length); 227 // If the binary has since been deleted, Linux appends " (deleted)" to the 228 // symlink target. Remove it, since this is not really part of our name. 229 const std::string kDeletedSuffix = " (deleted)"; 230 if (EndsWith(title, kDeletedSuffix, true)) 231 title.resize(title.size() - kDeletedSuffix.size()); 232 #if defined(PR_SET_NAME) 233 // If PR_SET_NAME is available at compile time, we try using it. We ignore 234 // any errors if the kernel does not support it at runtime though. When 235 // available, this lets us set the short process name that shows when the 236 // full command line is not being displayed in most process listings. 237 prctl(PR_SET_NAME, FilePath(title).BaseName().value().c_str()); 238 #endif 239 } 240 #endif 241 for (size_t i = 1; i < current_process_commandline_->argv_.size(); ++i) { 242 if (!title.empty()) 243 title += " "; 244 title += current_process_commandline_->argv_[i]; 245 } 246 // Disable prepending argv[0] with '-' if we prepended it ourselves above. 247 setproctitle(have_argv0 ? "-%s" : "%s", title.c_str()); 248 } 249 #endif 250 251 void CommandLine::Reset() { 252 DCHECK(current_process_commandline_ != NULL); 253 delete current_process_commandline_; 254 current_process_commandline_ = NULL; 255 } 256 257 bool CommandLine::HasSwitch(const std::string& switch_string) const { 258 std::string lowercased_switch(switch_string); 259 #if defined(OS_WIN) 260 Lowercase(&lowercased_switch); 261 #endif 262 return switches_.find(lowercased_switch) != switches_.end(); 263 } 264 265 std::wstring CommandLine::GetSwitchValue( 266 const std::string& switch_string) const { 267 std::string lowercased_switch(switch_string); 268 #if defined(OS_WIN) 269 Lowercase(&lowercased_switch); 270 #endif 271 272 std::map<std::string, StringType>::const_iterator result = 273 switches_.find(lowercased_switch); 274 275 if (result == switches_.end()) { 276 return L""; 277 } else { 278 #if defined(OS_WIN) 279 return result->second; 280 #else 281 return base::SysNativeMBToWide(result->second); 282 #endif 283 } 284 } 285 286 #if defined(OS_WIN) 287 std::vector<std::wstring> CommandLine::GetLooseValues() const { 288 return loose_values_; 289 } 290 std::wstring CommandLine::program() const { 291 return program_; 292 } 293 #else 294 std::vector<std::wstring> CommandLine::GetLooseValues() const { 295 std::vector<std::wstring> values; 296 for (size_t i = 0; i < loose_values_.size(); ++i) 297 values.push_back(base::SysNativeMBToWide(loose_values_[i])); 298 return values; 299 } 300 std::wstring CommandLine::program() const { 301 DCHECK_GT(argv_.size(), 0U); 302 return base::SysNativeMBToWide(argv_[0]); 303 } 304 #endif 305 306 307 // static 308 std::wstring CommandLine::PrefixedSwitchString( 309 const std::string& switch_string) { 310 #if defined(OS_WIN) 311 return kSwitchPrefixes[0] + ASCIIToWide(switch_string); 312 #else 313 return ASCIIToWide(kSwitchPrefixes[0] + switch_string); 314 #endif 315 } 316 317 // static 318 std::wstring CommandLine::PrefixedSwitchStringWithValue( 319 const std::string& switch_string, const std::wstring& value_string) { 320 if (value_string.empty()) { 321 return PrefixedSwitchString(switch_string); 322 } 323 324 return PrefixedSwitchString(switch_string + 325 #if defined(OS_WIN) 326 WideToASCII(kSwitchValueSeparator) 327 #else 328 kSwitchValueSeparator 329 #endif 330 ) + value_string; 331 } 332 333 #if defined(OS_WIN) 334 void CommandLine::AppendSwitch(const std::string& switch_string) { 335 std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string); 336 command_line_string_.append(L" "); 337 command_line_string_.append(prefixed_switch_string); 338 switches_[switch_string] = L""; 339 } 340 341 void CommandLine::AppendSwitchWithValue(const std::string& switch_string, 342 const std::wstring& value_string) { 343 std::wstring value_string_edit; 344 345 // NOTE(jhughes): If the value contains a quotation mark at one 346 // end but not both, you may get unusable output. 347 if (!value_string.empty() && 348 (value_string.find(L" ") != std::wstring::npos) && 349 (value_string[0] != L'"') && 350 (value_string[value_string.length() - 1] != L'"')) { 351 // need to provide quotes 352 value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str()); 353 } else { 354 value_string_edit = value_string; 355 } 356 357 std::wstring combined_switch_string = 358 PrefixedSwitchStringWithValue(switch_string, value_string_edit); 359 360 command_line_string_.append(L" "); 361 command_line_string_.append(combined_switch_string); 362 363 switches_[switch_string] = value_string; 364 } 365 366 void CommandLine::AppendLooseValue(const std::wstring& value) { 367 // TODO(evan): quoting? 368 command_line_string_.append(L" "); 369 command_line_string_.append(value); 370 loose_values_.push_back(value); 371 } 372 373 void CommandLine::AppendArguments(const CommandLine& other, 374 bool include_program) { 375 // Verify include_program is used correctly. 376 // Logic could be shorter but this is clearer. 377 DCHECK(include_program ? !other.program().empty() : other.program().empty()); 378 command_line_string_ += L" " + other.command_line_string_; 379 std::map<std::string, StringType>::const_iterator i; 380 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) 381 switches_[i->first] = i->second; 382 } 383 384 void CommandLine::PrependWrapper(const std::wstring& wrapper) { 385 // The wrapper may have embedded arguments (like "gdb --args"). In this case, 386 // we don't pretend to do anything fancy, we just split on spaces. 387 std::vector<std::wstring> wrapper_and_args; 388 SplitString(wrapper, ' ', &wrapper_and_args); 389 program_ = wrapper_and_args[0]; 390 command_line_string_ = wrapper + L" " + command_line_string_; 391 } 392 393 #elif defined(OS_POSIX) 394 void CommandLine::AppendSwitch(const std::string& switch_string) { 395 argv_.push_back(kSwitchPrefixes[0] + switch_string); 396 switches_[switch_string] = ""; 397 } 398 399 void CommandLine::AppendSwitchWithValue(const std::string& switch_string, 400 const std::wstring& value_string) { 401 std::string mb_value = base::SysWideToNativeMB(value_string); 402 403 argv_.push_back(kSwitchPrefixes[0] + switch_string + 404 kSwitchValueSeparator + mb_value); 405 switches_[switch_string] = mb_value; 406 } 407 408 void CommandLine::AppendLooseValue(const std::wstring& value) { 409 argv_.push_back(base::SysWideToNativeMB(value)); 410 } 411 412 void CommandLine::AppendArguments(const CommandLine& other, 413 bool include_program) { 414 // Verify include_program is used correctly. 415 // Logic could be shorter but this is clearer. 416 DCHECK(include_program ? !other.program().empty() : other.program().empty()); 417 418 size_t first_arg = include_program ? 0 : 1; 419 for (size_t i = first_arg; i < other.argv_.size(); ++i) 420 argv_.push_back(other.argv_[i]); 421 std::map<std::string, StringType>::const_iterator i; 422 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) 423 switches_[i->first] = i->second; 424 } 425 426 void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) { 427 // The wrapper may have embedded arguments (like "gdb --args"). In this case, 428 // we don't pretend to do anything fancy, we just split on spaces. 429 const std::string wrapper = base::SysWideToNativeMB(wrapper_wide); 430 std::vector<std::string> wrapper_and_args; 431 SplitString(wrapper, ' ', &wrapper_and_args); 432 argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); 433 } 434 435 #endif 436