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