Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011 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 #include <algorithm>
      8 #include <ostream>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/file_path.h"
     12 #include "base/logging.h"
     13 #include "base/string_split.h"
     14 #include "base/string_util.h"
     15 #include "base/utf_string_conversions.h"
     16 #include "build/build_config.h"
     17 
     18 #if defined(OS_WIN)
     19 #include <windows.h>
     20 #include <shellapi.h>
     21 #endif
     22 
     23 CommandLine* CommandLine::current_process_commandline_ = NULL;
     24 
     25 namespace {
     26 typedef CommandLine::StringType::value_type CharType;
     27 
     28 const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
     29 const CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
     30 // Since we use a lazy match, make sure that longer versions (like "--") are
     31 // listed before shorter versions (like "-") of similar prefixes.
     32 #if defined(OS_WIN)
     33 const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
     34 #elif defined(OS_POSIX)
     35 // Unixes don't use slash as a switch.
     36 const CharType* const kSwitchPrefixes[] = {"--", "-"};
     37 #endif
     38 
     39 #if defined(OS_WIN)
     40 // Lowercase a string for case-insensitivity of switches.
     41 // Is this desirable? It exists for backwards compatibility on Windows.
     42 void Lowercase(std::string* arg) {
     43   transform(arg->begin(), arg->end(), arg->begin(), tolower);
     44 }
     45 
     46 // Quote a string if necessary, such that CommandLineToArgvW() will always
     47 // process it as a single argument.
     48 std::wstring WindowsStyleQuote(const std::wstring& arg) {
     49   // We follow the quoting rules of CommandLineToArgvW.
     50   // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
     51   if (arg.find_first_of(L" \\\"") == std::wstring::npos) {
     52     // No quoting necessary.
     53     return arg;
     54   }
     55 
     56   std::wstring out;
     57   out.push_back(L'"');
     58   for (size_t i = 0; i < arg.size(); ++i) {
     59     if (arg[i] == '\\') {
     60       // Find the extent of this run of backslashes.
     61       size_t start = i, end = start + 1;
     62       for (; end < arg.size() && arg[end] == '\\'; ++end)
     63         /* empty */;
     64       size_t backslash_count = end - start;
     65 
     66       // Backslashes are escapes only if the run is followed by a double quote.
     67       // Since we also will end the string with a double quote, we escape for
     68       // either a double quote or the end of the string.
     69       if (end == arg.size() || arg[end] == '"') {
     70         // To quote, we need to output 2x as many backslashes.
     71         backslash_count *= 2;
     72       }
     73       for (size_t j = 0; j < backslash_count; ++j)
     74         out.push_back('\\');
     75 
     76       // Advance i to one before the end to balance i++ in loop.
     77       i = end - 1;
     78     } else if (arg[i] == '"') {
     79       out.push_back('\\');
     80       out.push_back('"');
     81     } else {
     82       out.push_back(arg[i]);
     83     }
     84   }
     85   out.push_back('"');
     86 
     87   return out;
     88 }
     89 #endif
     90 
     91 // Returns true and fills in |switch_string| and |switch_value| if
     92 // |parameter_string| represents a switch.
     93 bool IsSwitch(const CommandLine::StringType& parameter_string,
     94               std::string* switch_string,
     95               CommandLine::StringType* switch_value) {
     96   switch_string->clear();
     97   switch_value->clear();
     98 
     99   for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
    100     CommandLine::StringType prefix(kSwitchPrefixes[i]);
    101     if (parameter_string.find(prefix) != 0)
    102       continue;
    103 
    104     const size_t switch_start = prefix.length();
    105     const size_t equals_position = parameter_string.find(
    106         kSwitchValueSeparator, switch_start);
    107     CommandLine::StringType switch_native;
    108     if (equals_position == CommandLine::StringType::npos) {
    109       switch_native = parameter_string.substr(switch_start);
    110     } else {
    111       switch_native = parameter_string.substr(
    112           switch_start, equals_position - switch_start);
    113       *switch_value = parameter_string.substr(equals_position + 1);
    114     }
    115 #if defined(OS_WIN)
    116     *switch_string = WideToASCII(switch_native);
    117     Lowercase(switch_string);
    118 #else
    119     *switch_string = switch_native;
    120 #endif
    121 
    122     return true;
    123   }
    124 
    125   return false;
    126 }
    127 
    128 }  // namespace
    129 
    130 CommandLine::CommandLine(NoProgram no_program) {
    131 #if defined(OS_POSIX)
    132   // Push an empty argument, because we always assume argv_[0] is a program.
    133   argv_.push_back("");
    134 #endif
    135 }
    136 
    137 CommandLine::CommandLine(const FilePath& program) {
    138 #if defined(OS_WIN)
    139   if (!program.empty()) {
    140     program_ = program.value();
    141     // TODO(evanm): proper quoting here.
    142     command_line_string_ = L'"' + program.value() + L'"';
    143   }
    144 #elif defined(OS_POSIX)
    145   argv_.push_back(program.value());
    146 #endif
    147 }
    148 
    149 #if defined(OS_POSIX)
    150 CommandLine::CommandLine(int argc, const char* const* argv) {
    151   InitFromArgv(argc, argv);
    152 }
    153 
    154 CommandLine::CommandLine(const StringVector& argv) {
    155   InitFromArgv(argv);
    156 }
    157 #endif  // OS_POSIX
    158 
    159 CommandLine::~CommandLine() {
    160 }
    161 
    162 // static
    163 void CommandLine::Init(int argc, const char* const* argv) {
    164   delete current_process_commandline_;
    165   current_process_commandline_ = new CommandLine;
    166 #if defined(OS_WIN)
    167   current_process_commandline_->ParseFromString(::GetCommandLineW());
    168 #elif defined(OS_POSIX)
    169   current_process_commandline_->InitFromArgv(argc, argv);
    170 #endif
    171 }
    172 
    173 // static
    174 void CommandLine::Reset() {
    175   DCHECK(current_process_commandline_);
    176   delete current_process_commandline_;
    177   current_process_commandline_ = NULL;
    178 }
    179 
    180 // static
    181 CommandLine* CommandLine::ForCurrentProcess() {
    182   DCHECK(current_process_commandline_);
    183   return current_process_commandline_;
    184 }
    185 
    186 #if defined(OS_WIN)
    187 // static
    188 CommandLine CommandLine::FromString(const std::wstring& command_line) {
    189   CommandLine cmd;
    190   cmd.ParseFromString(command_line);
    191   return cmd;
    192 }
    193 #endif  // OS_WIN
    194 
    195 #if defined(OS_POSIX)
    196 void CommandLine::InitFromArgv(int argc, const char* const* argv) {
    197   for (int i = 0; i < argc; ++i)
    198     argv_.push_back(argv[i]);
    199   InitFromArgv(argv_);
    200 }
    201 
    202 void CommandLine::InitFromArgv(const StringVector& argv) {
    203   argv_ = argv;
    204   bool parse_switches = true;
    205   for (size_t i = 1; i < argv_.size(); ++i) {
    206     const std::string& arg = argv_[i];
    207 
    208     if (!parse_switches) {
    209       args_.push_back(arg);
    210       continue;
    211     }
    212 
    213     if (arg == kSwitchTerminator) {
    214       parse_switches = false;
    215       continue;
    216     }
    217 
    218     std::string switch_string;
    219     StringType switch_value;
    220     if (IsSwitch(arg, &switch_string, &switch_value)) {
    221       switches_[switch_string] = switch_value;
    222     } else {
    223       args_.push_back(arg);
    224     }
    225   }
    226 }
    227 #endif  // OS_POSIX
    228 
    229 CommandLine::StringType CommandLine::command_line_string() const {
    230 #if defined(OS_WIN)
    231   return command_line_string_;
    232 #elif defined(OS_POSIX)
    233   return JoinString(argv_, ' ');
    234 #endif
    235 }
    236 
    237 FilePath CommandLine::GetProgram() const {
    238 #if defined(OS_WIN)
    239   return FilePath(program_);
    240 #else
    241   DCHECK_GT(argv_.size(), 0U);
    242   return FilePath(argv_[0]);
    243 #endif
    244 }
    245 
    246 bool CommandLine::HasSwitch(const std::string& switch_string) const {
    247   std::string lowercased_switch(switch_string);
    248 #if defined(OS_WIN)
    249   Lowercase(&lowercased_switch);
    250 #endif
    251   return switches_.find(lowercased_switch) != switches_.end();
    252 }
    253 
    254 std::string CommandLine::GetSwitchValueASCII(
    255     const std::string& switch_string) const {
    256   CommandLine::StringType value = GetSwitchValueNative(switch_string);
    257   if (!IsStringASCII(value)) {
    258     LOG(WARNING) << "Value of --" << switch_string << " must be ASCII.";
    259     return "";
    260   }
    261 #if defined(OS_WIN)
    262   return WideToASCII(value);
    263 #else
    264   return value;
    265 #endif
    266 }
    267 
    268 FilePath CommandLine::GetSwitchValuePath(
    269     const std::string& switch_string) const {
    270   return FilePath(GetSwitchValueNative(switch_string));
    271 }
    272 
    273 CommandLine::StringType CommandLine::GetSwitchValueNative(
    274     const std::string& switch_string) const {
    275   std::string lowercased_switch(switch_string);
    276 #if defined(OS_WIN)
    277   Lowercase(&lowercased_switch);
    278 #endif
    279 
    280   SwitchMap::const_iterator result = switches_.find(lowercased_switch);
    281 
    282   if (result == switches_.end()) {
    283     return CommandLine::StringType();
    284   } else {
    285     return result->second;
    286   }
    287 }
    288 
    289 size_t CommandLine::GetSwitchCount() const {
    290   return switches_.size();
    291 }
    292 
    293 void CommandLine::AppendSwitch(const std::string& switch_string) {
    294 #if defined(OS_WIN)
    295   command_line_string_.append(L" ");
    296   command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string));
    297   switches_[switch_string] = L"";
    298 #elif defined(OS_POSIX)
    299   argv_.push_back(kSwitchPrefixes[0] + switch_string);
    300   switches_[switch_string] = "";
    301 #endif
    302 }
    303 
    304 void CommandLine::AppendSwitchPath(const std::string& switch_string,
    305                                    const FilePath& path) {
    306   AppendSwitchNative(switch_string, path.value());
    307 }
    308 
    309 void CommandLine::AppendSwitchNative(const std::string& switch_string,
    310                                      const CommandLine::StringType& value) {
    311 #if defined(OS_WIN)
    312   StringType combined_switch_string =
    313       kSwitchPrefixes[0] + ASCIIToWide(switch_string);
    314   if (!value.empty())
    315     combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value);
    316 
    317   command_line_string_.append(L" ");
    318   command_line_string_.append(combined_switch_string);
    319 
    320   switches_[switch_string] = value;
    321 #elif defined(OS_POSIX)
    322   StringType combined_switch_string = kSwitchPrefixes[0] + switch_string;
    323   if (!value.empty())
    324     combined_switch_string += kSwitchValueSeparator + value;
    325   argv_.push_back(combined_switch_string);
    326   switches_[switch_string] = value;
    327 #endif
    328 }
    329 
    330 void CommandLine::AppendSwitchASCII(const std::string& switch_string,
    331                                     const std::string& value_string) {
    332 #if defined(OS_WIN)
    333   AppendSwitchNative(switch_string, ASCIIToWide(value_string));
    334 #elif defined(OS_POSIX)
    335   AppendSwitchNative(switch_string, value_string);
    336 #endif
    337 }
    338 
    339 void CommandLine::AppendSwitches(const CommandLine& other) {
    340   SwitchMap::const_iterator i;
    341   for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
    342     AppendSwitchNative(i->first, i->second);
    343 }
    344 
    345 void CommandLine::CopySwitchesFrom(const CommandLine& source,
    346                                    const char* const switches[],
    347                                    size_t count) {
    348   for (size_t i = 0; i < count; ++i) {
    349     if (source.HasSwitch(switches[i])) {
    350       StringType value = source.GetSwitchValueNative(switches[i]);
    351       AppendSwitchNative(switches[i], value);
    352     }
    353   }
    354 }
    355 
    356 void CommandLine::AppendArg(const std::string& value) {
    357 #if defined(OS_WIN)
    358   DCHECK(IsStringUTF8(value));
    359   AppendArgNative(UTF8ToWide(value));
    360 #elif defined(OS_POSIX)
    361   AppendArgNative(value);
    362 #endif
    363 }
    364 
    365 void CommandLine::AppendArgPath(const FilePath& path) {
    366   AppendArgNative(path.value());
    367 }
    368 
    369 void CommandLine::AppendArgNative(const CommandLine::StringType& value) {
    370 #if defined(OS_WIN)
    371   command_line_string_.append(L" ");
    372   command_line_string_.append(WindowsStyleQuote(value));
    373   args_.push_back(value);
    374 #elif defined(OS_POSIX)
    375   DCHECK(IsStringUTF8(value));
    376   argv_.push_back(value);
    377 #endif
    378 }
    379 
    380 void CommandLine::AppendArgs(const CommandLine& other) {
    381   if(other.args_.size() <= 0)
    382     return;
    383 #if defined(OS_WIN)
    384   command_line_string_.append(L" --");
    385 #endif  // OS_WIN
    386   StringVector::const_iterator i;
    387   for (i = other.args_.begin(); i != other.args_.end(); ++i)
    388     AppendArgNative(*i);
    389 }
    390 
    391 void CommandLine::AppendArguments(const CommandLine& other,
    392                                   bool include_program) {
    393 #if defined(OS_WIN)
    394   // Verify include_program is used correctly.
    395   DCHECK(!include_program || !other.GetProgram().empty());
    396   if (include_program)
    397     program_ = other.program_;
    398 
    399   if (!command_line_string_.empty())
    400     command_line_string_ += L' ';
    401 
    402   command_line_string_ += other.command_line_string_;
    403 #elif defined(OS_POSIX)
    404   // Verify include_program is used correctly.
    405   // Logic could be shorter but this is clearer.
    406   DCHECK_EQ(include_program, !other.GetProgram().empty());
    407 
    408   if (include_program)
    409     argv_[0] = other.argv_[0];
    410 
    411   // Skip the first arg when copying since it's the program but push all
    412   // arguments to our arg vector.
    413   for (size_t i = 1; i < other.argv_.size(); ++i)
    414     argv_.push_back(other.argv_[i]);
    415 #endif
    416 
    417   SwitchMap::const_iterator i;
    418   for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
    419     switches_[i->first] = i->second;
    420 }
    421 
    422 void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) {
    423   // The wrapper may have embedded arguments (like "gdb --args"). In this case,
    424   // we don't pretend to do anything fancy, we just split on spaces.
    425   if (wrapper.empty())
    426     return;
    427   StringVector wrapper_and_args;
    428 #if defined(OS_WIN)
    429   base::SplitString(wrapper, ' ', &wrapper_and_args);
    430   program_ = wrapper_and_args[0];
    431   command_line_string_ = wrapper + L" " + command_line_string_;
    432 #elif defined(OS_POSIX)
    433   base::SplitString(wrapper, ' ', &wrapper_and_args);
    434   argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end());
    435 #endif
    436 }
    437 
    438 #if defined(OS_WIN)
    439 void CommandLine::ParseFromString(const std::wstring& command_line) {
    440   TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
    441 
    442   if (command_line_string_.empty())
    443     return;
    444 
    445   int num_args = 0;
    446   wchar_t** args = NULL;
    447 
    448   args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
    449 
    450   // Populate program_ with the trimmed version of the first arg.
    451   TrimWhitespace(args[0], TRIM_ALL, &program_);
    452 
    453   bool parse_switches = true;
    454   for (int i = 1; i < num_args; ++i) {
    455     std::wstring arg;
    456     TrimWhitespace(args[i], TRIM_ALL, &arg);
    457 
    458     if (!parse_switches) {
    459       args_.push_back(arg);
    460       continue;
    461     }
    462 
    463     if (arg == kSwitchTerminator) {
    464       parse_switches = false;
    465       continue;
    466     }
    467 
    468     std::string switch_string;
    469     std::wstring switch_value;
    470     if (IsSwitch(arg, &switch_string, &switch_value)) {
    471       switches_[switch_string] = switch_value;
    472     } else {
    473       args_.push_back(arg);
    474     }
    475   }
    476 
    477   if (args)
    478     LocalFree(args);
    479 }
    480 #endif
    481 
    482 CommandLine::CommandLine() {
    483 }
    484