blob: b027d2a22a121f69de76292750b8965dd4fba108 [file] [log] [blame]
[email protected]0fd23af2011-02-20 06:33:041// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
[email protected]f3adb5c2008-08-07 20:07:325#include "base/command_line.h"
6
initial.commitd7cae122008-07-26 21:49:387#include <algorithm>
[email protected]2edc2862011-04-04 18:04:378#include <ostream>
initial.commitd7cae122008-07-26 21:49:389
[email protected]2edc2862011-04-04 18:04:3710#include "base/basictypes.h"
[email protected]8f681e42009-10-09 20:37:5611#include "base/file_path.h"
initial.commitd7cae122008-07-26 21:49:3812#include "base/logging.h"
[email protected]4e5ae20f2010-09-24 04:52:1113#include "base/string_split.h"
initial.commitd7cae122008-07-26 21:49:3814#include "base/string_util.h"
[email protected]f1d81922010-07-31 17:47:0915#include "base/utf_string_conversions.h"
[email protected]74e9fa22010-12-29 21:06:4316#include "build/build_config.h"
initial.commitd7cae122008-07-26 21:49:3817
[email protected]74e9fa22010-12-29 21:06:4318#if defined(OS_WIN)
19#include <windows.h>
20#include <shellapi.h>
[email protected]7f113f32009-09-10 18:02:1721#endif
22
[email protected]bb975362009-01-21 01:00:2223CommandLine* CommandLine::current_process_commandline_ = NULL;
initial.commitd7cae122008-07-26 21:49:3824
[email protected]06cc083a2011-03-01 02:28:4225namespace {
26typedef CommandLine::StringType::value_type CharType;
27
28const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
29const 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.
[email protected]5d426332008-08-08 20:46:2132#if defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:4233const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
[email protected]5d426332008-08-08 20:46:2134#elif defined(OS_POSIX)
[email protected]1a48f312008-08-12 01:14:3735// Unixes don't use slash as a switch.
[email protected]06cc083a2011-03-01 02:28:4236const CharType* const kSwitchPrefixes[] = {"--", "-"};
[email protected]5d426332008-08-08 20:46:2137#endif
initial.commitd7cae122008-07-26 21:49:3838
[email protected]f3adb5c2008-08-07 20:07:3239#if defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:4240// Lowercase a string for case-insensitivity of switches.
41// Is this desirable? It exists for backwards compatibility on Windows.
42void Lowercase(std::string* arg) {
43 transform(arg->begin(), arg->end(), arg->begin(), tolower);
initial.commitd7cae122008-07-26 21:49:3844}
[email protected]0fd23af2011-02-20 06:33:0445
[email protected]06cc083a2011-03-01 02:28:4246// Quote a string if necessary, such that CommandLineToArgvW() will always
47// process it as a single argument.
[email protected]0fd23af2011-02-20 06:33:0448std::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}
[email protected]bb975362009-01-21 01:00:2289#endif
initial.commitd7cae122008-07-26 21:49:3890
[email protected]06cc083a2011-03-01 02:28:4291// Returns true and fills in |switch_string| and |switch_value| if
92// |parameter_string| represents a switch.
93bool 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
130CommandLine::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
137CommandLine::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)
150CommandLine::CommandLine(int argc, const char* const* argv) {
151 InitFromArgv(argc, argv);
152}
153
154CommandLine::CommandLine(const StringVector& argv) {
155 InitFromArgv(argv);
156}
157#endif // OS_POSIX
158
[email protected]acbeb3d2011-03-01 20:47:58159CommandLine::~CommandLine() {
160}
161
[email protected]06cc083a2011-03-01 02:28:42162// static
163void 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
174void CommandLine::Reset() {
175 DCHECK(current_process_commandline_);
176 delete current_process_commandline_;
177 current_process_commandline_ = NULL;
178}
179
180// static
181CommandLine* CommandLine::ForCurrentProcess() {
182 DCHECK(current_process_commandline_);
183 return current_process_commandline_;
[email protected]3a3d47472010-07-15 21:03:54184}
185
[email protected]f3adb5c2008-08-07 20:07:32186#if defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:42187// static
188CommandLine 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)
196void 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_);
[email protected]51343d5a2009-10-26 22:39:33200}
201
[email protected]06cc083a2011-03-01 02:28:42202void 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
229CommandLine::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
237FilePath 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
246bool 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
254std::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
268FilePath CommandLine::GetSwitchValuePath(
269 const std::string& switch_string) const {
270 return FilePath(GetSwitchValueNative(switch_string));
271}
272
273CommandLine::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
289size_t CommandLine::GetSwitchCount() const {
290 return switches_.size();
291}
292
293void 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
304void CommandLine::AppendSwitchPath(const std::string& switch_string,
305 const FilePath& path) {
306 AppendSwitchNative(switch_string, path.value());
307}
308
309void 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
330void 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
339void 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
345void 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
356void 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
365void CommandLine::AppendArgPath(const FilePath& path) {
366 AppendArgNative(path.value());
367}
368
369void 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
380void 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
391void 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
422void 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)
[email protected]bb975362009-01-21 01:00:22439void 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) {
[email protected]2e4c50c2010-07-21 15:57:23459 args_.push_back(arg);
[email protected]bb975362009-01-21 01:00:22460 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 {
[email protected]2e4c50c2010-07-21 15:57:23473 args_.push_back(arg);
[email protected]bb975362009-01-21 01:00:22474 }
475 }
476
477 if (args)
478 LocalFree(args);
479}
[email protected]f3adb5c2008-08-07 20:07:32480#endif
[email protected]acbeb3d2011-03-01 20:47:58481
482CommandLine::CommandLine() {
483}