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