command_line.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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#if defined(OS_WIN)
8#include <windows.h>
9#include <shellapi.h>
10#elif defined(OS_POSIX)
11#include <limits.h>
12#include <stdlib.h>
13#include <unistd.h>
14#endif
15#if defined(OS_LINUX)
16#include <sys/prctl.h>
17#endif
18
19#include <algorithm>
20
21#include "base/file_path.h"
22#include "base/logging.h"
23#include "base/singleton.h"
24#include "base/string_piece.h"
25#include "base/string_util.h"
26#include "base/sys_string_conversions.h"
27
28#if defined(OS_LINUX)
29// Linux/glibc doesn't natively have setproctitle().
30#include "base/setproctitle_linux.h"
31#endif
32
33CommandLine* CommandLine::current_process_commandline_ = NULL;
34
35// Since we use a lazy match, make sure that longer versions (like L"--")
36// are listed before shorter versions (like L"-") of similar prefixes.
37#if defined(OS_WIN)
38const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
39const wchar_t kSwitchTerminator[] = L"--";
40const wchar_t kSwitchValueSeparator[] = L"=";
41#elif defined(OS_POSIX)
42// Unixes don't use slash as a switch.
43const char* const kSwitchPrefixes[] = {"--", "-"};
44const char kSwitchTerminator[] = "--";
45const char kSwitchValueSeparator[] = "=";
46#endif
47
48#if defined(OS_WIN)
49// Lowercase a string.  This is used to lowercase switch names.
50// Is this what we really want?  It seems crazy to me.  I've left it in
51// for backwards compatibility on Windows.
52static void Lowercase(std::string* parameter) {
53  transform(parameter->begin(), parameter->end(), parameter->begin(),
54            tolower);
55}
56#endif
57
58CommandLine::~CommandLine() {
59}
60
61#if defined(OS_WIN)
62CommandLine::CommandLine(ArgumentsOnly args_only) {
63}
64
65void CommandLine::ParseFromString(const std::wstring& command_line) {
66  TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
67
68  if (command_line_string_.empty())
69    return;
70
71  int num_args = 0;
72  wchar_t** args = NULL;
73
74  args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
75
76  // Populate program_ with the trimmed version of the first arg.
77  TrimWhitespace(args[0], TRIM_ALL, &program_);
78
79  bool parse_switches = true;
80  for (int i = 1; i < num_args; ++i) {
81    std::wstring arg;
82    TrimWhitespace(args[i], TRIM_ALL, &arg);
83
84    if (!parse_switches) {
85      args_.push_back(arg);
86      continue;
87    }
88
89    if (arg == kSwitchTerminator) {
90      parse_switches = false;
91      continue;
92    }
93
94    std::string switch_string;
95    std::wstring switch_value;
96    if (IsSwitch(arg, &switch_string, &switch_value)) {
97      switches_[switch_string] = switch_value;
98    } else {
99      args_.push_back(arg);
100    }
101  }
102
103  if (args)
104    LocalFree(args);
105}
106
107CommandLine::CommandLine(const FilePath& program) {
108  if (!program.empty()) {
109    program_ = program.value();
110    command_line_string_ = L'"' + program.value() + L'"';
111  }
112}
113
114#elif defined(OS_POSIX)
115CommandLine::CommandLine(ArgumentsOnly args_only) {
116  // Push an empty argument, because we always assume argv_[0] is a program.
117  argv_.push_back("");
118}
119
120void CommandLine::InitFromArgv(int argc, const char* const* argv) {
121  for (int i = 0; i < argc; ++i)
122    argv_.push_back(argv[i]);
123  InitFromArgv(argv_);
124}
125
126void CommandLine::InitFromArgv(const std::vector<std::string>& argv) {
127  argv_ = argv;
128  bool parse_switches = true;
129  for (size_t i = 1; i < argv_.size(); ++i) {
130    const std::string& arg = argv_[i];
131
132    if (!parse_switches) {
133      args_.push_back(arg);
134      continue;
135    }
136
137    if (arg == kSwitchTerminator) {
138      parse_switches = false;
139      continue;
140    }
141
142    std::string switch_string;
143    std::string switch_value;
144    if (IsSwitch(arg, &switch_string, &switch_value)) {
145      switches_[switch_string] = switch_value;
146    } else {
147      args_.push_back(arg);
148    }
149  }
150}
151
152CommandLine::CommandLine(const FilePath& program) {
153  argv_.push_back(program.value());
154}
155
156#endif
157
158// static
159bool CommandLine::IsSwitch(const StringType& parameter_string,
160                           std::string* switch_string,
161                           StringType* switch_value) {
162  switch_string->clear();
163  switch_value->clear();
164
165  for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
166    StringType prefix(kSwitchPrefixes[i]);
167    if (parameter_string.find(prefix) != 0)
168      continue;
169
170    const size_t switch_start = prefix.length();
171    const size_t equals_position = parameter_string.find(
172        kSwitchValueSeparator, switch_start);
173    StringType switch_native;
174    if (equals_position == StringType::npos) {
175      switch_native = parameter_string.substr(switch_start);
176    } else {
177      switch_native = parameter_string.substr(
178          switch_start, equals_position - switch_start);
179      *switch_value = parameter_string.substr(equals_position + 1);
180    }
181#if defined(OS_WIN)
182    *switch_string = WideToASCII(switch_native);
183    Lowercase(switch_string);
184#else
185    *switch_string = switch_native;
186#endif
187
188    return true;
189  }
190
191  return false;
192}
193
194// static
195void CommandLine::Init(int argc, const char* const* argv) {
196  delete current_process_commandline_;
197  current_process_commandline_ = new CommandLine;
198#if defined(OS_WIN)
199  current_process_commandline_->ParseFromString(::GetCommandLineW());
200#elif defined(OS_POSIX)
201  current_process_commandline_->InitFromArgv(argc, argv);
202#endif
203
204#if defined(OS_LINUX)
205  if (argv)
206    setproctitle_init(const_cast<char**>(argv));
207#endif
208}
209
210#if defined(OS_POSIX) && !defined(OS_MACOSX)
211// static
212void CommandLine::SetProcTitle() {
213  // Build a single string which consists of all the arguments separated
214  // by spaces. We can't actually keep them separate due to the way the
215  // setproctitle() function works.
216  std::string title;
217  bool have_argv0 = false;
218#if defined(OS_LINUX)
219  // In Linux we sometimes exec ourselves from /proc/self/exe, but this makes us
220  // show up as "exe" in process listings. Read the symlink /proc/self/exe and
221  // use the path it points at for our process title. Note that this is only for
222  // display purposes and has no TOCTTOU security implications.
223  char buffer[PATH_MAX];
224  // Note: readlink() does not append a null byte to terminate the string.
225  ssize_t length = readlink("/proc/self/exe", buffer, sizeof(buffer));
226  DCHECK(length <= static_cast<ssize_t>(sizeof(buffer)));
227  if (length > 0) {
228    have_argv0 = true;
229    title.assign(buffer, length);
230    // If the binary has since been deleted, Linux appends " (deleted)" to the
231    // symlink target. Remove it, since this is not really part of our name.
232    const std::string kDeletedSuffix = " (deleted)";
233    if (EndsWith(title, kDeletedSuffix, true))
234      title.resize(title.size() - kDeletedSuffix.size());
235#if defined(PR_SET_NAME)
236    // If PR_SET_NAME is available at compile time, we try using it. We ignore
237    // any errors if the kernel does not support it at runtime though. When
238    // available, this lets us set the short process name that shows when the
239    // full command line is not being displayed in most process listings.
240    prctl(PR_SET_NAME, FilePath(title).BaseName().value().c_str());
241#endif
242  }
243#endif
244  for (size_t i = 1; i < current_process_commandline_->argv_.size(); ++i) {
245    if (!title.empty())
246      title += " ";
247    title += current_process_commandline_->argv_[i];
248  }
249  // Disable prepending argv[0] with '-' if we prepended it ourselves above.
250  setproctitle(have_argv0 ? "-%s" : "%s", title.c_str());
251}
252#endif
253
254void CommandLine::Reset() {
255  DCHECK(current_process_commandline_ != NULL);
256  delete current_process_commandline_;
257  current_process_commandline_ = NULL;
258}
259
260bool CommandLine::HasSwitch(const std::string& switch_string) const {
261  std::string lowercased_switch(switch_string);
262#if defined(OS_WIN)
263  Lowercase(&lowercased_switch);
264#endif
265  return switches_.find(lowercased_switch) != switches_.end();
266}
267
268std::wstring CommandLine::GetSwitchValue(
269    const std::string& switch_string) const {
270  std::string lowercased_switch(switch_string);
271#if defined(OS_WIN)
272  Lowercase(&lowercased_switch);
273#endif
274
275  std::map<std::string, StringType>::const_iterator result =
276      switches_.find(lowercased_switch);
277
278  if (result == switches_.end()) {
279    return L"";
280  } else {
281#if defined(OS_WIN)
282    return result->second;
283#else
284    return base::SysNativeMBToWide(result->second);
285#endif
286  }
287}
288
289#if defined(OS_WIN)
290std::wstring CommandLine::program() const {
291  return program_;
292}
293#else
294std::wstring CommandLine::program() const {
295  DCHECK_GT(argv_.size(), 0U);
296  return base::SysNativeMBToWide(argv_[0]);
297}
298#endif
299
300#if defined(OS_POSIX)
301std::string CommandLine::command_line_string() const {
302  return JoinString(argv_, ' ');
303}
304#endif
305
306// static
307std::wstring CommandLine::PrefixedSwitchString(
308    const std::string& switch_string) {
309#if defined(OS_WIN)
310  return kSwitchPrefixes[0] + ASCIIToWide(switch_string);
311#else
312  return ASCIIToWide(kSwitchPrefixes[0] + switch_string);
313#endif
314}
315
316// static
317std::wstring CommandLine::PrefixedSwitchStringWithValue(
318    const std::string& switch_string, const std::wstring& value_string) {
319  if (value_string.empty()) {
320    return PrefixedSwitchString(switch_string);
321  }
322
323  return PrefixedSwitchString(switch_string +
324#if defined(OS_WIN)
325                              WideToASCII(kSwitchValueSeparator)
326#else
327                              kSwitchValueSeparator
328#endif
329                              ) + value_string;
330}
331
332#if defined(OS_WIN)
333void CommandLine::AppendSwitch(const std::string& switch_string) {
334  std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string);
335  command_line_string_.append(L" ");
336  command_line_string_.append(prefixed_switch_string);
337  switches_[switch_string] = L"";
338}
339
340void CommandLine::AppendSwitchWithValue(const std::string& switch_string,
341                                        const std::wstring& value_string) {
342  std::wstring value_string_edit;
343
344  // NOTE(jhughes): If the value contains a quotation mark at one
345  //                end but not both, you may get unusable output.
346  if (!value_string.empty() &&
347      (value_string.find(L" ") != std::wstring::npos) &&
348      (value_string[0] != L'"') &&
349      (value_string[value_string.length() - 1] != L'"')) {
350    // need to provide quotes
351    value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str());
352  } else {
353    value_string_edit = value_string;
354  }
355
356  std::wstring combined_switch_string =
357      PrefixedSwitchStringWithValue(switch_string, value_string_edit);
358
359  command_line_string_.append(L" ");
360  command_line_string_.append(combined_switch_string);
361
362  switches_[switch_string] = value_string;
363}
364
365void CommandLine::AppendLooseValue(const std::wstring& value) {
366  // TODO(evan): quoting?
367  command_line_string_.append(L" ");
368  command_line_string_.append(value);
369  args_.push_back(value);
370}
371
372void CommandLine::AppendArguments(const CommandLine& other,
373                                  bool include_program) {
374  // Verify include_program is used correctly.
375  // Logic could be shorter but this is clearer.
376  DCHECK_EQ(include_program, !other.GetProgram().empty());
377  command_line_string_ += L" " + other.command_line_string_;
378  std::map<std::string, StringType>::const_iterator i;
379  for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
380    switches_[i->first] = i->second;
381}
382
383void CommandLine::PrependWrapper(const std::wstring& wrapper) {
384  // The wrapper may have embedded arguments (like "gdb --args"). In this case,
385  // we don't pretend to do anything fancy, we just split on spaces.
386  std::vector<std::wstring> wrapper_and_args;
387  SplitString(wrapper, ' ', &wrapper_and_args);
388  program_ = wrapper_and_args[0];
389  command_line_string_ = wrapper + L" " + command_line_string_;
390}
391
392#elif defined(OS_POSIX)
393void CommandLine::AppendSwitch(const std::string& switch_string) {
394  argv_.push_back(kSwitchPrefixes[0] + switch_string);
395  switches_[switch_string] = "";
396}
397
398void CommandLine::AppendSwitchWithValue(const std::string& switch_string,
399                                        const std::wstring& value_string) {
400  std::string mb_value = base::SysWideToNativeMB(value_string);
401
402  argv_.push_back(kSwitchPrefixes[0] + switch_string +
403                  kSwitchValueSeparator + mb_value);
404  switches_[switch_string] = mb_value;
405}
406
407void CommandLine::AppendLooseValue(const std::wstring& value) {
408  argv_.push_back(base::SysWideToNativeMB(value));
409}
410
411void CommandLine::AppendArguments(const CommandLine& other,
412                                  bool include_program) {
413  // Verify include_program is used correctly.
414  // Logic could be shorter but this is clearer.
415  DCHECK_EQ(include_program, !other.GetProgram().empty());
416  size_t first_arg = include_program ? 0 : 1;
417  for (size_t i = first_arg; i < other.argv_.size(); ++i)
418    argv_.push_back(other.argv_[i]);
419  std::map<std::string, StringType>::const_iterator i;
420  for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
421    switches_[i->first] = i->second;
422}
423
424void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) {
425  // The wrapper may have embedded arguments (like "gdb --args"). In this case,
426  // we don't pretend to do anything fancy, we just split on spaces.
427  const std::string wrapper = base::SysWideToNativeMB(wrapper_wide);
428  std::vector<std::string> wrapper_and_args;
429  SplitString(wrapper, ' ', &wrapper_and_args);
430  argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end());
431}
432
433#endif
434
435// private
436CommandLine::CommandLine() {
437}
438