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
23CommandLine* CommandLine::current_process_commandline_ = NULL;
24
25namespace {
26typedef CommandLine::StringType::value_type CharType;
27
28const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
29const 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)
33const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
34#elif defined(OS_POSIX)
35// Unixes don't use slash as a switch.
36const 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.
42void 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.
48std::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.
93bool 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
130CommandLine::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
137CommandLine::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)
150CommandLine::CommandLine(int argc, const char* const* argv) {
151  InitFromArgv(argc, argv);
152}
153
154CommandLine::CommandLine(const StringVector& argv) {
155  InitFromArgv(argv);
156}
157#endif  // OS_POSIX
158
159CommandLine::~CommandLine() {
160}
161
162// static
163void 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
174void CommandLine::Reset() {
175  DCHECK(current_process_commandline_);
176  delete current_process_commandline_;
177  current_process_commandline_ = NULL;
178}
179
180// static
181CommandLine* CommandLine::ForCurrentProcess() {
182  DCHECK(current_process_commandline_);
183  return current_process_commandline_;
184}
185
186#if defined(OS_WIN)
187// static
188CommandLine 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)
196void 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
202void 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
229CommandLine::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
237FilePath 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
246bool 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
254std::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
268FilePath CommandLine::GetSwitchValuePath(
269    const std::string& switch_string) const {
270  return FilePath(GetSwitchValueNative(switch_string));
271}
272
273CommandLine::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
289size_t CommandLine::GetSwitchCount() const {
290  return switches_.size();
291}
292
293void 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
304void CommandLine::AppendSwitchPath(const std::string& switch_string,
305                                   const FilePath& path) {
306  AppendSwitchNative(switch_string, path.value());
307}
308
309void 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
330void 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
339void 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
345void 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
356void 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
365void CommandLine::AppendArgPath(const FilePath& path) {
366  AppendArgNative(path.value());
367}
368
369void 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
380void 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
391void 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
422void 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)
439void 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
482CommandLine::CommandLine() {
483}
484