Home | History | Annotate | Download | only in base
      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