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