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