security/sandbox/chromium/base/command_line.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/sandbox/chromium/base/command_line.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,424 @@
     1.4 +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "base/command_line.h"
     1.9 +
    1.10 +#include <algorithm>
    1.11 +#include <ostream>
    1.12 +
    1.13 +#include "base/basictypes.h"
    1.14 +#include "base/files/file_path.h"
    1.15 +#include "base/logging.h"
    1.16 +#include "base/strings/string_split.h"
    1.17 +#include "base/strings/string_util.h"
    1.18 +#include "base/strings/utf_string_conversions.h"
    1.19 +#include "build/build_config.h"
    1.20 +
    1.21 +#if defined(OS_WIN)
    1.22 +#include <windows.h>
    1.23 +#include <shellapi.h>
    1.24 +#endif
    1.25 +
    1.26 +using base::FilePath;
    1.27 +
    1.28 +CommandLine* CommandLine::current_process_commandline_ = NULL;
    1.29 +
    1.30 +namespace {
    1.31 +const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
    1.32 +const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
    1.33 +// Since we use a lazy match, make sure that longer versions (like "--") are
    1.34 +// listed before shorter versions (like "-") of similar prefixes.
    1.35 +#if defined(OS_WIN)
    1.36 +const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
    1.37 +#elif defined(OS_POSIX)
    1.38 +// Unixes don't use slash as a switch.
    1.39 +const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
    1.40 +#endif
    1.41 +
    1.42 +size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
    1.43 +  for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
    1.44 +    CommandLine::StringType prefix(kSwitchPrefixes[i]);
    1.45 +    if (string.compare(0, prefix.length(), prefix) == 0)
    1.46 +      return prefix.length();
    1.47 +  }
    1.48 +  return 0;
    1.49 +}
    1.50 +
    1.51 +// Fills in |switch_string| and |switch_value| if |string| is a switch.
    1.52 +// This will preserve the input switch prefix in the output |switch_string|.
    1.53 +bool IsSwitch(const CommandLine::StringType& string,
    1.54 +              CommandLine::StringType* switch_string,
    1.55 +              CommandLine::StringType* switch_value) {
    1.56 +  switch_string->clear();
    1.57 +  switch_value->clear();
    1.58 +  size_t prefix_length = GetSwitchPrefixLength(string);
    1.59 +  if (prefix_length == 0 || prefix_length == string.length())
    1.60 +    return false;
    1.61 +
    1.62 +  const size_t equals_position = string.find(kSwitchValueSeparator);
    1.63 +  *switch_string = string.substr(0, equals_position);
    1.64 +  if (equals_position != CommandLine::StringType::npos)
    1.65 +    *switch_value = string.substr(equals_position + 1);
    1.66 +  return true;
    1.67 +}
    1.68 +
    1.69 +// Append switches and arguments, keeping switches before arguments.
    1.70 +void AppendSwitchesAndArguments(CommandLine& command_line,
    1.71 +                                const CommandLine::StringVector& argv) {
    1.72 +  bool parse_switches = true;
    1.73 +  for (size_t i = 1; i < argv.size(); ++i) {
    1.74 +    CommandLine::StringType arg = argv[i];
    1.75 +    TrimWhitespace(arg, TRIM_ALL, &arg);
    1.76 +
    1.77 +    CommandLine::StringType switch_string;
    1.78 +    CommandLine::StringType switch_value;
    1.79 +    parse_switches &= (arg != kSwitchTerminator);
    1.80 +    if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
    1.81 +#if defined(OS_WIN)
    1.82 +      command_line.AppendSwitchNative(WideToASCII(switch_string), switch_value);
    1.83 +#elif defined(OS_POSIX)
    1.84 +      command_line.AppendSwitchNative(switch_string, switch_value);
    1.85 +#endif
    1.86 +    } else {
    1.87 +      command_line.AppendArgNative(arg);
    1.88 +    }
    1.89 +  }
    1.90 +}
    1.91 +
    1.92 +// Lowercase switches for backwards compatiblity *on Windows*.
    1.93 +std::string LowerASCIIOnWindows(const std::string& string) {
    1.94 +#if defined(OS_WIN)
    1.95 +  return StringToLowerASCII(string);
    1.96 +#elif defined(OS_POSIX)
    1.97 +  return string;
    1.98 +#endif
    1.99 +}
   1.100 +
   1.101 +
   1.102 +#if defined(OS_WIN)
   1.103 +// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*.
   1.104 +std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg) {
   1.105 +  // We follow the quoting rules of CommandLineToArgvW.
   1.106 +  // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
   1.107 +  if (arg.find_first_of(L" \\\"") == std::wstring::npos) {
   1.108 +    // No quoting necessary.
   1.109 +    return arg;
   1.110 +  }
   1.111 +
   1.112 +  std::wstring out;
   1.113 +  out.push_back(L'"');
   1.114 +  for (size_t i = 0; i < arg.size(); ++i) {
   1.115 +    if (arg[i] == '\\') {
   1.116 +      // Find the extent of this run of backslashes.
   1.117 +      size_t start = i, end = start + 1;
   1.118 +      for (; end < arg.size() && arg[end] == '\\'; ++end)
   1.119 +        /* empty */;
   1.120 +      size_t backslash_count = end - start;
   1.121 +
   1.122 +      // Backslashes are escapes only if the run is followed by a double quote.
   1.123 +      // Since we also will end the string with a double quote, we escape for
   1.124 +      // either a double quote or the end of the string.
   1.125 +      if (end == arg.size() || arg[end] == '"') {
   1.126 +        // To quote, we need to output 2x as many backslashes.
   1.127 +        backslash_count *= 2;
   1.128 +      }
   1.129 +      for (size_t j = 0; j < backslash_count; ++j)
   1.130 +        out.push_back('\\');
   1.131 +
   1.132 +      // Advance i to one before the end to balance i++ in loop.
   1.133 +      i = end - 1;
   1.134 +    } else if (arg[i] == '"') {
   1.135 +      out.push_back('\\');
   1.136 +      out.push_back('"');
   1.137 +    } else {
   1.138 +      out.push_back(arg[i]);
   1.139 +    }
   1.140 +  }
   1.141 +  out.push_back('"');
   1.142 +
   1.143 +  return out;
   1.144 +}
   1.145 +#endif
   1.146 +
   1.147 +}  // namespace
   1.148 +
   1.149 +CommandLine::CommandLine(NoProgram no_program)
   1.150 +    : argv_(1),
   1.151 +      begin_args_(1) {
   1.152 +}
   1.153 +
   1.154 +CommandLine::CommandLine(const FilePath& program)
   1.155 +    : argv_(1),
   1.156 +      begin_args_(1) {
   1.157 +  SetProgram(program);
   1.158 +}
   1.159 +
   1.160 +CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv)
   1.161 +    : argv_(1),
   1.162 +      begin_args_(1) {
   1.163 +  InitFromArgv(argc, argv);
   1.164 +}
   1.165 +
   1.166 +CommandLine::CommandLine(const StringVector& argv)
   1.167 +    : argv_(1),
   1.168 +      begin_args_(1) {
   1.169 +  InitFromArgv(argv);
   1.170 +}
   1.171 +
   1.172 +CommandLine::~CommandLine() {
   1.173 +}
   1.174 +
   1.175 +// static
   1.176 +bool CommandLine::Init(int argc, const char* const* argv) {
   1.177 +  if (current_process_commandline_) {
   1.178 +    // If this is intentional, Reset() must be called first. If we are using
   1.179 +    // the shared build mode, we have to share a single object across multiple
   1.180 +    // shared libraries.
   1.181 +    return false;
   1.182 +  }
   1.183 +
   1.184 +  current_process_commandline_ = new CommandLine(NO_PROGRAM);
   1.185 +#if defined(OS_WIN)
   1.186 +  current_process_commandline_->ParseFromString(::GetCommandLineW());
   1.187 +#elif defined(OS_POSIX)
   1.188 +  current_process_commandline_->InitFromArgv(argc, argv);
   1.189 +#endif
   1.190 +
   1.191 +  return true;
   1.192 +}
   1.193 +
   1.194 +// static
   1.195 +void CommandLine::Reset() {
   1.196 +  DCHECK(current_process_commandline_);
   1.197 +  delete current_process_commandline_;
   1.198 +  current_process_commandline_ = NULL;
   1.199 +}
   1.200 +
   1.201 +// static
   1.202 +CommandLine* CommandLine::ForCurrentProcess() {
   1.203 +  DCHECK(current_process_commandline_);
   1.204 +  return current_process_commandline_;
   1.205 +}
   1.206 +
   1.207 +// static
   1.208 +bool CommandLine::InitializedForCurrentProcess() {
   1.209 +  return !!current_process_commandline_;
   1.210 +}
   1.211 +
   1.212 +#if defined(OS_WIN)
   1.213 +// static
   1.214 +CommandLine CommandLine::FromString(const std::wstring& command_line) {
   1.215 +  CommandLine cmd(NO_PROGRAM);
   1.216 +  cmd.ParseFromString(command_line);
   1.217 +  return cmd;
   1.218 +}
   1.219 +#endif
   1.220 +
   1.221 +void CommandLine::InitFromArgv(int argc,
   1.222 +                               const CommandLine::CharType* const* argv) {
   1.223 +  StringVector new_argv;
   1.224 +  for (int i = 0; i < argc; ++i)
   1.225 +    new_argv.push_back(argv[i]);
   1.226 +  InitFromArgv(new_argv);
   1.227 +}
   1.228 +
   1.229 +void CommandLine::InitFromArgv(const StringVector& argv) {
   1.230 +  argv_ = StringVector(1);
   1.231 +  switches_.clear();
   1.232 +  begin_args_ = 1;
   1.233 +  SetProgram(argv.empty() ? FilePath() : FilePath(argv[0]));
   1.234 +  AppendSwitchesAndArguments(*this, argv);
   1.235 +}
   1.236 +
   1.237 +CommandLine::StringType CommandLine::GetCommandLineString() const {
   1.238 +  StringType string(argv_[0]);
   1.239 +#if defined(OS_WIN)
   1.240 +  string = QuoteForCommandLineToArgvW(string);
   1.241 +#endif
   1.242 +  StringType params(GetArgumentsString());
   1.243 +  if (!params.empty()) {
   1.244 +    string.append(StringType(FILE_PATH_LITERAL(" ")));
   1.245 +    string.append(params);
   1.246 +  }
   1.247 +  return string;
   1.248 +}
   1.249 +
   1.250 +CommandLine::StringType CommandLine::GetArgumentsString() const {
   1.251 +  StringType params;
   1.252 +  // Append switches and arguments.
   1.253 +  bool parse_switches = true;
   1.254 +  for (size_t i = 1; i < argv_.size(); ++i) {
   1.255 +    StringType arg = argv_[i];
   1.256 +    StringType switch_string;
   1.257 +    StringType switch_value;
   1.258 +    parse_switches &= arg != kSwitchTerminator;
   1.259 +    if (i > 1)
   1.260 +      params.append(StringType(FILE_PATH_LITERAL(" ")));
   1.261 +    if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) {
   1.262 +      params.append(switch_string);
   1.263 +      if (!switch_value.empty()) {
   1.264 +#if defined(OS_WIN)
   1.265 +        switch_value = QuoteForCommandLineToArgvW(switch_value);
   1.266 +#endif
   1.267 +        params.append(kSwitchValueSeparator + switch_value);
   1.268 +      }
   1.269 +    }
   1.270 +    else {
   1.271 +#if defined(OS_WIN)
   1.272 +      arg = QuoteForCommandLineToArgvW(arg);
   1.273 +#endif
   1.274 +      params.append(arg);
   1.275 +    }
   1.276 +  }
   1.277 +  return params;
   1.278 +}
   1.279 +
   1.280 +FilePath CommandLine::GetProgram() const {
   1.281 +  return FilePath(argv_[0]);
   1.282 +}
   1.283 +
   1.284 +void CommandLine::SetProgram(const FilePath& program) {
   1.285 +  TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]);
   1.286 +}
   1.287 +
   1.288 +bool CommandLine::HasSwitch(const std::string& switch_string) const {
   1.289 +  return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end();
   1.290 +}
   1.291 +
   1.292 +std::string CommandLine::GetSwitchValueASCII(
   1.293 +    const std::string& switch_string) const {
   1.294 +  StringType value = GetSwitchValueNative(switch_string);
   1.295 +  if (!IsStringASCII(value)) {
   1.296 +    DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII.";
   1.297 +    return std::string();
   1.298 +  }
   1.299 +#if defined(OS_WIN)
   1.300 +  return WideToASCII(value);
   1.301 +#else
   1.302 +  return value;
   1.303 +#endif
   1.304 +}
   1.305 +
   1.306 +FilePath CommandLine::GetSwitchValuePath(
   1.307 +    const std::string& switch_string) const {
   1.308 +  return FilePath(GetSwitchValueNative(switch_string));
   1.309 +}
   1.310 +
   1.311 +CommandLine::StringType CommandLine::GetSwitchValueNative(
   1.312 +    const std::string& switch_string) const {
   1.313 +  SwitchMap::const_iterator result = switches_.end();
   1.314 +  result = switches_.find(LowerASCIIOnWindows(switch_string));
   1.315 +  return result == switches_.end() ? StringType() : result->second;
   1.316 +}
   1.317 +
   1.318 +void CommandLine::AppendSwitch(const std::string& switch_string) {
   1.319 +  AppendSwitchNative(switch_string, StringType());
   1.320 +}
   1.321 +
   1.322 +void CommandLine::AppendSwitchPath(const std::string& switch_string,
   1.323 +                                   const FilePath& path) {
   1.324 +  AppendSwitchNative(switch_string, path.value());
   1.325 +}
   1.326 +
   1.327 +void CommandLine::AppendSwitchNative(const std::string& switch_string,
   1.328 +                                     const CommandLine::StringType& value) {
   1.329 +  std::string switch_key(LowerASCIIOnWindows(switch_string));
   1.330 +#if defined(OS_WIN)
   1.331 +  StringType combined_switch_string(ASCIIToWide(switch_key));
   1.332 +#elif defined(OS_POSIX)
   1.333 +  StringType combined_switch_string(switch_string);
   1.334 +#endif
   1.335 +  size_t prefix_length = GetSwitchPrefixLength(combined_switch_string);
   1.336 +  switches_[switch_key.substr(prefix_length)] = value;
   1.337 +  // Preserve existing switch prefixes in |argv_|; only append one if necessary.
   1.338 +  if (prefix_length == 0)
   1.339 +    combined_switch_string = kSwitchPrefixes[0] + combined_switch_string;
   1.340 +  if (!value.empty())
   1.341 +    combined_switch_string += kSwitchValueSeparator + value;
   1.342 +  // Append the switch and update the switches/arguments divider |begin_args_|.
   1.343 +  argv_.insert(argv_.begin() + begin_args_++, combined_switch_string);
   1.344 +}
   1.345 +
   1.346 +void CommandLine::AppendSwitchASCII(const std::string& switch_string,
   1.347 +                                    const std::string& value_string) {
   1.348 +#if defined(OS_WIN)
   1.349 +  AppendSwitchNative(switch_string, ASCIIToWide(value_string));
   1.350 +#elif defined(OS_POSIX)
   1.351 +  AppendSwitchNative(switch_string, value_string);
   1.352 +#endif
   1.353 +}
   1.354 +
   1.355 +void CommandLine::CopySwitchesFrom(const CommandLine& source,
   1.356 +                                   const char* const switches[],
   1.357 +                                   size_t count) {
   1.358 +  for (size_t i = 0; i < count; ++i) {
   1.359 +    if (source.HasSwitch(switches[i]))
   1.360 +      AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i]));
   1.361 +  }
   1.362 +}
   1.363 +
   1.364 +CommandLine::StringVector CommandLine::GetArgs() const {
   1.365 +  // Gather all arguments after the last switch (may include kSwitchTerminator).
   1.366 +  StringVector args(argv_.begin() + begin_args_, argv_.end());
   1.367 +  // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?)
   1.368 +  StringVector::iterator switch_terminator =
   1.369 +      std::find(args.begin(), args.end(), kSwitchTerminator);
   1.370 +  if (switch_terminator != args.end())
   1.371 +    args.erase(switch_terminator);
   1.372 +  return args;
   1.373 +}
   1.374 +
   1.375 +void CommandLine::AppendArg(const std::string& value) {
   1.376 +#if defined(OS_WIN)
   1.377 +  DCHECK(IsStringUTF8(value));
   1.378 +  AppendArgNative(UTF8ToWide(value));
   1.379 +#elif defined(OS_POSIX)
   1.380 +  AppendArgNative(value);
   1.381 +#endif
   1.382 +}
   1.383 +
   1.384 +void CommandLine::AppendArgPath(const FilePath& path) {
   1.385 +  AppendArgNative(path.value());
   1.386 +}
   1.387 +
   1.388 +void CommandLine::AppendArgNative(const CommandLine::StringType& value) {
   1.389 +  argv_.push_back(value);
   1.390 +}
   1.391 +
   1.392 +void CommandLine::AppendArguments(const CommandLine& other,
   1.393 +                                  bool include_program) {
   1.394 +  if (include_program)
   1.395 +    SetProgram(other.GetProgram());
   1.396 +  AppendSwitchesAndArguments(*this, other.argv());
   1.397 +}
   1.398 +
   1.399 +void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) {
   1.400 +  if (wrapper.empty())
   1.401 +    return;
   1.402 +  // The wrapper may have embedded arguments (like "gdb --args"). In this case,
   1.403 +  // we don't pretend to do anything fancy, we just split on spaces.
   1.404 +  StringVector wrapper_argv;
   1.405 +  base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv);
   1.406 +  // Prepend the wrapper and update the switches/arguments |begin_args_|.
   1.407 +  argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end());
   1.408 +  begin_args_ += wrapper_argv.size();
   1.409 +}
   1.410 +
   1.411 +#if defined(OS_WIN)
   1.412 +void CommandLine::ParseFromString(const std::wstring& command_line) {
   1.413 +  std::wstring command_line_string;
   1.414 +  TrimWhitespace(command_line, TRIM_ALL, &command_line_string);
   1.415 +  if (command_line_string.empty())
   1.416 +    return;
   1.417 +
   1.418 +  int num_args = 0;
   1.419 +  wchar_t** args = NULL;
   1.420 +  args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args);
   1.421 +
   1.422 +  DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
   1.423 +                         << command_line;
   1.424 +  InitFromArgv(num_args, args);
   1.425 +  LocalFree(args);
   1.426 +}
   1.427 +#endif

mercurial