• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
Lowercase(std::string * arg)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.
WindowsStyleQuote(const std::wstring & arg)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.
IsSwitch(const CommandLine::StringType & parameter_string,std::string * switch_string,CommandLine::StringType * switch_value)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 
CommandLine(NoProgram no_program)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 
CommandLine(const FilePath & program)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)
CommandLine(int argc,const char * const * argv)150 CommandLine::CommandLine(int argc, const char* const* argv) {
151   InitFromArgv(argc, argv);
152 }
153 
CommandLine(const StringVector & argv)154 CommandLine::CommandLine(const StringVector& argv) {
155   InitFromArgv(argv);
156 }
157 #endif  // OS_POSIX
158 
~CommandLine()159 CommandLine::~CommandLine() {
160 }
161 
162 // static
Init(int argc,const char * const * argv)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
Reset()174 void CommandLine::Reset() {
175   DCHECK(current_process_commandline_);
176   delete current_process_commandline_;
177   current_process_commandline_ = NULL;
178 }
179 
180 // static
ForCurrentProcess()181 CommandLine* CommandLine::ForCurrentProcess() {
182   DCHECK(current_process_commandline_);
183   return current_process_commandline_;
184 }
185 
186 #if defined(OS_WIN)
187 // static
FromString(const std::wstring & command_line)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)
InitFromArgv(int argc,const char * const * argv)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 
InitFromArgv(const StringVector & argv)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 
command_line_string() const229 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 
GetProgram() const237 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 
HasSwitch(const std::string & switch_string) const246 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 
GetSwitchValueASCII(const std::string & switch_string) const254 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 
GetSwitchValuePath(const std::string & switch_string) const268 FilePath CommandLine::GetSwitchValuePath(
269     const std::string& switch_string) const {
270   return FilePath(GetSwitchValueNative(switch_string));
271 }
272 
GetSwitchValueNative(const std::string & switch_string) const273 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 
GetSwitchCount() const289 size_t CommandLine::GetSwitchCount() const {
290   return switches_.size();
291 }
292 
AppendSwitch(const std::string & switch_string)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 
AppendSwitchPath(const std::string & switch_string,const FilePath & path)304 void CommandLine::AppendSwitchPath(const std::string& switch_string,
305                                    const FilePath& path) {
306   AppendSwitchNative(switch_string, path.value());
307 }
308 
AppendSwitchNative(const std::string & switch_string,const CommandLine::StringType & value)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 
AppendSwitchASCII(const std::string & switch_string,const std::string & value_string)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 
AppendSwitches(const CommandLine & other)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 
CopySwitchesFrom(const CommandLine & source,const char * const switches[],size_t count)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 
AppendArg(const std::string & value)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 
AppendArgPath(const FilePath & path)365 void CommandLine::AppendArgPath(const FilePath& path) {
366   AppendArgNative(path.value());
367 }
368 
AppendArgNative(const CommandLine::StringType & value)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 
AppendArgs(const CommandLine & other)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 
AppendArguments(const CommandLine & other,bool include_program)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 
PrependWrapper(const CommandLine::StringType & wrapper)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)
ParseFromString(const std::wstring & command_line)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 
CommandLine()482 CommandLine::CommandLine() {
483 }
484