| // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/command_line.h" |
| |
| #if defined(OS_WIN) |
| #include <windows.h> |
| #include <shellapi.h> |
| #elif defined(OS_POSIX) |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #endif |
| #if defined(OS_LINUX) |
| #include <sys/prctl.h> |
| #endif |
| |
| #include <algorithm> |
| |
| #include "base/file_path.h" |
| #include "base/logging.h" |
| #include "base/singleton.h" |
| #include "base/string_piece.h" |
| #include "base/string_util.h" |
| #include "base/sys_string_conversions.h" |
| |
| #if defined(OS_LINUX) |
| // Linux/glibc doesn't natively have setproctitle(). |
| #include "base/setproctitle_linux.h" |
| #endif |
| |
| CommandLine* CommandLine::current_process_commandline_ = NULL; |
| |
| // Since we use a lazy match, make sure that longer versions (like L"--") |
| // are listed before shorter versions (like L"-") of similar prefixes. |
| #if defined(OS_WIN) |
| const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; |
| const wchar_t kSwitchTerminator[] = L"--"; |
| const wchar_t kSwitchValueSeparator[] = L"="; |
| #elif defined(OS_POSIX) |
| // Unixes don't use slash as a switch. |
| const char* const kSwitchPrefixes[] = {"--", "-"}; |
| const char kSwitchTerminator[] = "--"; |
| const char kSwitchValueSeparator[] = "="; |
| #endif |
| |
| #if defined(OS_WIN) |
| // Lowercase a string. This is used to lowercase switch names. |
| // Is this what we really want? It seems crazy to me. I've left it in |
| // for backwards compatibility on Windows. |
| static void Lowercase(std::string* parameter) { |
| transform(parameter->begin(), parameter->end(), parameter->begin(), |
| tolower); |
| } |
| #endif |
| |
| #if defined(OS_WIN) |
| CommandLine::CommandLine(ArgumentsOnly args_only) { |
| } |
| |
| void CommandLine::ParseFromString(const std::wstring& command_line) { |
| 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); |
| |
| // 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) { |
| loose_values_.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 { |
| loose_values_.push_back(arg); |
| } |
| } |
| |
| if (args) |
| LocalFree(args); |
| } |
| |
| CommandLine::CommandLine(const FilePath& program) { |
| if (!program.empty()) { |
| program_ = program.value(); |
| command_line_string_ = L'"' + program.value() + L'"'; |
| } |
| } |
| |
| #elif defined(OS_POSIX) |
| CommandLine::CommandLine(ArgumentsOnly args_only) { |
| // Push an empty argument, because we always assume argv_[0] is a program. |
| argv_.push_back(""); |
| } |
| |
| void CommandLine::InitFromArgv(int argc, const char* const* argv) { |
| for (int i = 0; i < argc; ++i) |
| argv_.push_back(argv[i]); |
| InitFromArgv(argv_); |
| } |
| |
| void CommandLine::InitFromArgv(const std::vector<std::string>& 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) { |
| loose_values_.push_back(arg); |
| continue; |
| } |
| |
| if (arg == kSwitchTerminator) { |
| parse_switches = false; |
| continue; |
| } |
| |
| std::string switch_string; |
| std::string switch_value; |
| if (IsSwitch(arg, &switch_string, &switch_value)) { |
| switches_[switch_string] = switch_value; |
| } else { |
| loose_values_.push_back(arg); |
| } |
| } |
| } |
| |
| CommandLine::CommandLine(const FilePath& program) { |
| argv_.push_back(program.value()); |
| } |
| |
| #endif |
| |
| // static |
| bool CommandLine::IsSwitch(const StringType& parameter_string, |
| std::string* switch_string, |
| StringType* switch_value) { |
| switch_string->clear(); |
| switch_value->clear(); |
| |
| for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { |
| 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); |
| StringType switch_native; |
| if (equals_position == 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; |
| } |
| |
| // static |
| void CommandLine::Init(int argc, const char* const* argv) { |
| delete current_process_commandline_; |
| current_process_commandline_ = new CommandLine; |
| #if defined(OS_WIN) |
| current_process_commandline_->ParseFromString(::GetCommandLineW()); |
| #elif defined(OS_POSIX) |
| current_process_commandline_->InitFromArgv(argc, argv); |
| #endif |
| |
| #if defined(OS_LINUX) |
| if (argv) |
| setproctitle_init(const_cast<char**>(argv)); |
| #endif |
| } |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| // static |
| void CommandLine::SetProcTitle() { |
| // Build a single string which consists of all the arguments separated |
| // by spaces. We can't actually keep them separate due to the way the |
| // setproctitle() function works. |
| std::string title; |
| bool have_argv0 = false; |
| #if defined(OS_LINUX) |
| // In Linux we sometimes exec ourselves from /proc/self/exe, but this makes us |
| // show up as "exe" in process listings. Read the symlink /proc/self/exe and |
| // use the path it points at for our process title. Note that this is only for |
| // display purposes and has no TOCTTOU security implications. |
| char buffer[PATH_MAX]; |
| // Note: readlink() does not append a null byte to terminate the string. |
| ssize_t length = readlink("/proc/self/exe", buffer, sizeof(buffer)); |
| DCHECK(length <= static_cast<ssize_t>(sizeof(buffer))); |
| if (length > 0) { |
| have_argv0 = true; |
| title.assign(buffer, length); |
| // If the binary has since been deleted, Linux appends " (deleted)" to the |
| // symlink target. Remove it, since this is not really part of our name. |
| const std::string kDeletedSuffix = " (deleted)"; |
| if (EndsWith(title, kDeletedSuffix, true)) |
| title.resize(title.size() - kDeletedSuffix.size()); |
| #if defined(PR_SET_NAME) |
| // If PR_SET_NAME is available at compile time, we try using it. We ignore |
| // any errors if the kernel does not support it at runtime though. When |
| // available, this lets us set the short process name that shows when the |
| // full command line is not being displayed in most process listings. |
| prctl(PR_SET_NAME, FilePath(title).BaseName().value().c_str()); |
| #endif |
| } |
| #endif |
| for (size_t i = 1; i < current_process_commandline_->argv_.size(); ++i) { |
| if (!title.empty()) |
| title += " "; |
| title += current_process_commandline_->argv_[i]; |
| } |
| // Disable prepending argv[0] with '-' if we prepended it ourselves above. |
| setproctitle(have_argv0 ? "-%s" : "%s", title.c_str()); |
| } |
| #endif |
| |
| void CommandLine::Reset() { |
| DCHECK(current_process_commandline_ != NULL); |
| delete current_process_commandline_; |
| current_process_commandline_ = NULL; |
| } |
| |
| 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(); |
| } |
| |
| std::wstring CommandLine::GetSwitchValue( |
| const std::string& switch_string) const { |
| std::string lowercased_switch(switch_string); |
| #if defined(OS_WIN) |
| Lowercase(&lowercased_switch); |
| #endif |
| |
| std::map<std::string, StringType>::const_iterator result = |
| switches_.find(lowercased_switch); |
| |
| if (result == switches_.end()) { |
| return L""; |
| } else { |
| #if defined(OS_WIN) |
| return result->second; |
| #else |
| return base::SysNativeMBToWide(result->second); |
| #endif |
| } |
| } |
| |
| #if defined(OS_WIN) |
| std::vector<std::wstring> CommandLine::GetLooseValues() const { |
| return loose_values_; |
| } |
| std::wstring CommandLine::program() const { |
| return program_; |
| } |
| #else |
| std::vector<std::wstring> CommandLine::GetLooseValues() const { |
| std::vector<std::wstring> values; |
| for (size_t i = 0; i < loose_values_.size(); ++i) |
| values.push_back(base::SysNativeMBToWide(loose_values_[i])); |
| return values; |
| } |
| std::wstring CommandLine::program() const { |
| DCHECK_GT(argv_.size(), 0U); |
| return base::SysNativeMBToWide(argv_[0]); |
| } |
| #endif |
| |
| |
| // static |
| std::wstring CommandLine::PrefixedSwitchString( |
| const std::string& switch_string) { |
| #if defined(OS_WIN) |
| return kSwitchPrefixes[0] + ASCIIToWide(switch_string); |
| #else |
| return ASCIIToWide(kSwitchPrefixes[0] + switch_string); |
| #endif |
| } |
| |
| // static |
| std::wstring CommandLine::PrefixedSwitchStringWithValue( |
| const std::string& switch_string, const std::wstring& value_string) { |
| if (value_string.empty()) { |
| return PrefixedSwitchString(switch_string); |
| } |
| |
| return PrefixedSwitchString(switch_string + |
| #if defined(OS_WIN) |
| WideToASCII(kSwitchValueSeparator) |
| #else |
| kSwitchValueSeparator |
| #endif |
| ) + value_string; |
| } |
| |
| #if defined(OS_WIN) |
| void CommandLine::AppendSwitch(const std::string& switch_string) { |
| std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string); |
| command_line_string_.append(L" "); |
| command_line_string_.append(prefixed_switch_string); |
| switches_[switch_string] = L""; |
| } |
| |
| void CommandLine::AppendSwitchWithValue(const std::string& switch_string, |
| const std::wstring& value_string) { |
| std::wstring value_string_edit; |
| |
| // NOTE(jhughes): If the value contains a quotation mark at one |
| // end but not both, you may get unusable output. |
| if (!value_string.empty() && |
| (value_string.find(L" ") != std::wstring::npos) && |
| (value_string[0] != L'"') && |
| (value_string[value_string.length() - 1] != L'"')) { |
| // need to provide quotes |
| value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str()); |
| } else { |
| value_string_edit = value_string; |
| } |
| |
| std::wstring combined_switch_string = |
| PrefixedSwitchStringWithValue(switch_string, value_string_edit); |
| |
| command_line_string_.append(L" "); |
| command_line_string_.append(combined_switch_string); |
| |
| switches_[switch_string] = value_string; |
| } |
| |
| void CommandLine::AppendLooseValue(const std::wstring& value) { |
| // TODO(evan): quoting? |
| command_line_string_.append(L" "); |
| command_line_string_.append(value); |
| loose_values_.push_back(value); |
| } |
| |
| void CommandLine::AppendArguments(const CommandLine& other, |
| bool include_program) { |
| // Verify include_program is used correctly. |
| // Logic could be shorter but this is clearer. |
| DCHECK(include_program ? !other.program().empty() : other.program().empty()); |
| command_line_string_ += L" " + other.command_line_string_; |
| std::map<std::string, StringType>::const_iterator i; |
| for (i = other.switches_.begin(); i != other.switches_.end(); ++i) |
| switches_[i->first] = i->second; |
| } |
| |
| void CommandLine::PrependWrapper(const std::wstring& 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. |
| std::vector<std::wstring> wrapper_and_args; |
| SplitString(wrapper, ' ', &wrapper_and_args); |
| program_ = wrapper_and_args[0]; |
| command_line_string_ = wrapper + L" " + command_line_string_; |
| } |
| |
| #elif defined(OS_POSIX) |
| void CommandLine::AppendSwitch(const std::string& switch_string) { |
| argv_.push_back(kSwitchPrefixes[0] + switch_string); |
| switches_[switch_string] = ""; |
| } |
| |
| void CommandLine::AppendSwitchWithValue(const std::string& switch_string, |
| const std::wstring& value_string) { |
| std::string mb_value = base::SysWideToNativeMB(value_string); |
| |
| argv_.push_back(kSwitchPrefixes[0] + switch_string + |
| kSwitchValueSeparator + mb_value); |
| switches_[switch_string] = mb_value; |
| } |
| |
| void CommandLine::AppendLooseValue(const std::wstring& value) { |
| argv_.push_back(base::SysWideToNativeMB(value)); |
| } |
| |
| void CommandLine::AppendArguments(const CommandLine& other, |
| bool include_program) { |
| // Verify include_program is used correctly. |
| // Logic could be shorter but this is clearer. |
| DCHECK(include_program ? !other.program().empty() : other.program().empty()); |
| |
| size_t first_arg = include_program ? 0 : 1; |
| for (size_t i = first_arg; i < other.argv_.size(); ++i) |
| argv_.push_back(other.argv_[i]); |
| std::map<std::string, StringType>::const_iterator i; |
| for (i = other.switches_.begin(); i != other.switches_.end(); ++i) |
| switches_[i->first] = i->second; |
| } |
| |
| void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) { |
| // 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. |
| const std::string wrapper = base::SysWideToNativeMB(wrapper_wide); |
| std::vector<std::string> wrapper_and_args; |
| SplitString(wrapper, ' ', &wrapper_and_args); |
| argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); |
| } |
| |
| #endif |