tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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