blob: eefd6ac46cbdd6e50ada5192e281d77e41c7494f [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>
8
[email protected]8f681e42009-10-09 20:37:569#include "base/file_path.h"
[email protected]723571a2010-12-03 17:37:5410#include "base/file_util.h"
initial.commitd7cae122008-07-26 21:49:3811#include "base/logging.h"
12#include "base/singleton.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]05b5d792008-08-07 22:28:2415#include "base/sys_string_conversions.h"
[email protected]f1d81922010-07-31 17:47:0916#include "base/utf_string_conversions.h"
[email protected]74e9fa22010-12-29 21:06:4317#include "build/build_config.h"
initial.commitd7cae122008-07-26 21:49:3818
[email protected]74e9fa22010-12-29 21:06:4319#if defined(OS_WIN)
20#include <windows.h>
21#include <shellapi.h>
22#elif defined(OS_POSIX)
23#include <limits.h>
24#include <stdlib.h>
25#include <unistd.h>
[email protected]7f113f32009-09-10 18:02:1726#endif
27
[email protected]bb975362009-01-21 01:00:2228CommandLine* CommandLine::current_process_commandline_ = NULL;
initial.commitd7cae122008-07-26 21:49:3829
[email protected]06cc083a2011-03-01 02:28:4230namespace {
31typedef CommandLine::StringType::value_type CharType;
32
33const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
34const CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
35// Since we use a lazy match, make sure that longer versions (like "--") are
36// listed before shorter versions (like "-") of similar prefixes.
[email protected]5d426332008-08-08 20:46:2137#if defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:4238const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
[email protected]5d426332008-08-08 20:46:2139#elif defined(OS_POSIX)
[email protected]1a48f312008-08-12 01:14:3740// Unixes don't use slash as a switch.
[email protected]06cc083a2011-03-01 02:28:4241const CharType* const kSwitchPrefixes[] = {"--", "-"};
[email protected]5d426332008-08-08 20:46:2142#endif
initial.commitd7cae122008-07-26 21:49:3843
[email protected]f3adb5c2008-08-07 20:07:3244#if defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:4245// Lowercase a string for case-insensitivity of switches.
46// Is this desirable? It exists for backwards compatibility on Windows.
47void Lowercase(std::string* arg) {
48 transform(arg->begin(), arg->end(), arg->begin(), tolower);
initial.commitd7cae122008-07-26 21:49:3849}
[email protected]0fd23af2011-02-20 06:33:0450
[email protected]06cc083a2011-03-01 02:28:4251// Quote a string if necessary, such that CommandLineToArgvW() will always
52// process it as a single argument.
[email protected]0fd23af2011-02-20 06:33:0453std::wstring WindowsStyleQuote(const std::wstring& arg) {
54 // We follow the quoting rules of CommandLineToArgvW.
55 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
56 if (arg.find_first_of(L" \\\"") == std::wstring::npos) {
57 // No quoting necessary.
58 return arg;
59 }
60
61 std::wstring out;
62 out.push_back(L'"');
63 for (size_t i = 0; i < arg.size(); ++i) {
64 if (arg[i] == '\\') {
65 // Find the extent of this run of backslashes.
66 size_t start = i, end = start + 1;
67 for (; end < arg.size() && arg[end] == '\\'; ++end)
68 /* empty */;
69 size_t backslash_count = end - start;
70
71 // Backslashes are escapes only if the run is followed by a double quote.
72 // Since we also will end the string with a double quote, we escape for
73 // either a double quote or the end of the string.
74 if (end == arg.size() || arg[end] == '"') {
75 // To quote, we need to output 2x as many backslashes.
76 backslash_count *= 2;
77 }
78 for (size_t j = 0; j < backslash_count; ++j)
79 out.push_back('\\');
80
81 // Advance i to one before the end to balance i++ in loop.
82 i = end - 1;
83 } else if (arg[i] == '"') {
84 out.push_back('\\');
85 out.push_back('"');
86 } else {
87 out.push_back(arg[i]);
88 }
89 }
90 out.push_back('"');
91
92 return out;
93}
[email protected]bb975362009-01-21 01:00:2294#endif
initial.commitd7cae122008-07-26 21:49:3895
[email protected]06cc083a2011-03-01 02:28:4296// Returns true and fills in |switch_string| and |switch_value| if
97// |parameter_string| represents a switch.
98bool IsSwitch(const CommandLine::StringType& parameter_string,
99 std::string* switch_string,
100 CommandLine::StringType* switch_value) {
101 switch_string->clear();
102 switch_value->clear();
103
104 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
105 CommandLine::StringType prefix(kSwitchPrefixes[i]);
106 if (parameter_string.find(prefix) != 0)
107 continue;
108
109 const size_t switch_start = prefix.length();
110 const size_t equals_position = parameter_string.find(
111 kSwitchValueSeparator, switch_start);
112 CommandLine::StringType switch_native;
113 if (equals_position == CommandLine::StringType::npos) {
114 switch_native = parameter_string.substr(switch_start);
115 } else {
116 switch_native = parameter_string.substr(
117 switch_start, equals_position - switch_start);
118 *switch_value = parameter_string.substr(equals_position + 1);
119 }
120#if defined(OS_WIN)
121 *switch_string = WideToASCII(switch_native);
122 Lowercase(switch_string);
123#else
124 *switch_string = switch_native;
125#endif
126
127 return true;
128 }
129
130 return false;
131}
132
133} // namespace
134
135CommandLine::CommandLine(NoProgram no_program) {
136#if defined(OS_POSIX)
137 // Push an empty argument, because we always assume argv_[0] is a program.
138 argv_.push_back("");
139#endif
140}
141
142CommandLine::CommandLine(const FilePath& program) {
143#if defined(OS_WIN)
144 if (!program.empty()) {
145 program_ = program.value();
146 // TODO(evanm): proper quoting here.
147 command_line_string_ = L'"' + program.value() + L'"';
148 }
149#elif defined(OS_POSIX)
150 argv_.push_back(program.value());
151#endif
152}
153
154#if defined(OS_POSIX)
155CommandLine::CommandLine(int argc, const char* const* argv) {
156 InitFromArgv(argc, argv);
157}
158
159CommandLine::CommandLine(const StringVector& argv) {
160 InitFromArgv(argv);
161}
162#endif // OS_POSIX
163
164// static
165void CommandLine::Init(int argc, const char* const* argv) {
166 delete current_process_commandline_;
167 current_process_commandline_ = new CommandLine;
168#if defined(OS_WIN)
169 current_process_commandline_->ParseFromString(::GetCommandLineW());
170#elif defined(OS_POSIX)
171 current_process_commandline_->InitFromArgv(argc, argv);
172#endif
173}
174
175// static
176void CommandLine::Reset() {
177 DCHECK(current_process_commandline_);
178 delete current_process_commandline_;
179 current_process_commandline_ = NULL;
180}
181
182// static
183CommandLine* CommandLine::ForCurrentProcess() {
184 DCHECK(current_process_commandline_);
185 return current_process_commandline_;
[email protected]3a3d47472010-07-15 21:03:54186}
187
[email protected]f3adb5c2008-08-07 20:07:32188#if defined(OS_WIN)
[email protected]06cc083a2011-03-01 02:28:42189// static
190CommandLine CommandLine::FromString(const std::wstring& command_line) {
191 CommandLine cmd;
192 cmd.ParseFromString(command_line);
193 return cmd;
194}
195#endif // OS_WIN
196
197#if defined(OS_POSIX)
198void CommandLine::InitFromArgv(int argc, const char* const* argv) {
199 for (int i = 0; i < argc; ++i)
200 argv_.push_back(argv[i]);
201 InitFromArgv(argv_);
[email protected]51343d5a2009-10-26 22:39:33202}
203
[email protected]06cc083a2011-03-01 02:28:42204void CommandLine::InitFromArgv(const StringVector& argv) {
205 argv_ = argv;
206 bool parse_switches = true;
207 for (size_t i = 1; i < argv_.size(); ++i) {
208 const std::string& arg = argv_[i];
209
210 if (!parse_switches) {
211 args_.push_back(arg);
212 continue;
213 }
214
215 if (arg == kSwitchTerminator) {
216 parse_switches = false;
217 continue;
218 }
219
220 std::string switch_string;
221 StringType switch_value;
222 if (IsSwitch(arg, &switch_string, &switch_value)) {
223 switches_[switch_string] = switch_value;
224 } else {
225 args_.push_back(arg);
226 }
227 }
228}
229#endif // OS_POSIX
230
231CommandLine::StringType CommandLine::command_line_string() const {
232#if defined(OS_WIN)
233 return command_line_string_;
234#elif defined(OS_POSIX)
235 return JoinString(argv_, ' ');
236#endif
237}
238
239FilePath CommandLine::GetProgram() const {
240#if defined(OS_WIN)
241 return FilePath(program_);
242#else
243 DCHECK_GT(argv_.size(), 0U);
244 return FilePath(argv_[0]);
245#endif
246}
247
248bool CommandLine::HasSwitch(const std::string& switch_string) const {
249 std::string lowercased_switch(switch_string);
250#if defined(OS_WIN)
251 Lowercase(&lowercased_switch);
252#endif
253 return switches_.find(lowercased_switch) != switches_.end();
254}
255
256std::string CommandLine::GetSwitchValueASCII(
257 const std::string& switch_string) const {
258 CommandLine::StringType value = GetSwitchValueNative(switch_string);
259 if (!IsStringASCII(value)) {
260 LOG(WARNING) << "Value of --" << switch_string << " must be ASCII.";
261 return "";
262 }
263#if defined(OS_WIN)
264 return WideToASCII(value);
265#else
266 return value;
267#endif
268}
269
270FilePath CommandLine::GetSwitchValuePath(
271 const std::string& switch_string) const {
272 return FilePath(GetSwitchValueNative(switch_string));
273}
274
275CommandLine::StringType CommandLine::GetSwitchValueNative(
276 const std::string& switch_string) const {
277 std::string lowercased_switch(switch_string);
278#if defined(OS_WIN)
279 Lowercase(&lowercased_switch);
280#endif
281
282 SwitchMap::const_iterator result = switches_.find(lowercased_switch);
283
284 if (result == switches_.end()) {
285 return CommandLine::StringType();
286 } else {
287 return result->second;
288 }
289}
290
291size_t CommandLine::GetSwitchCount() const {
292 return switches_.size();
293}
294
295void CommandLine::AppendSwitch(const std::string& switch_string) {
296#if defined(OS_WIN)
297 command_line_string_.append(L" ");
298 command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string));
299 switches_[switch_string] = L"";
300#elif defined(OS_POSIX)
301 argv_.push_back(kSwitchPrefixes[0] + switch_string);
302 switches_[switch_string] = "";
303#endif
304}
305
306void CommandLine::AppendSwitchPath(const std::string& switch_string,
307 const FilePath& path) {
308 AppendSwitchNative(switch_string, path.value());
309}
310
311void CommandLine::AppendSwitchNative(const std::string& switch_string,
312 const CommandLine::StringType& value) {
313#if defined(OS_WIN)
314 StringType combined_switch_string =
315 kSwitchPrefixes[0] + ASCIIToWide(switch_string);
316 if (!value.empty())
317 combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value);
318
319 command_line_string_.append(L" ");
320 command_line_string_.append(combined_switch_string);
321
322 switches_[switch_string] = value;
323#elif defined(OS_POSIX)
324 StringType combined_switch_string = kSwitchPrefixes[0] + switch_string;
325 if (!value.empty())
326 combined_switch_string += kSwitchValueSeparator + value;
327 argv_.push_back(combined_switch_string);
328 switches_[switch_string] = value;
329#endif
330}
331
332void CommandLine::AppendSwitchASCII(const std::string& switch_string,
333 const std::string& value_string) {
334#if defined(OS_WIN)
335 AppendSwitchNative(switch_string, ASCIIToWide(value_string));
336#elif defined(OS_POSIX)
337 AppendSwitchNative(switch_string, value_string);
338#endif
339}
340
341void CommandLine::AppendSwitches(const CommandLine& other) {
342 SwitchMap::const_iterator i;
343 for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
344 AppendSwitchNative(i->first, i->second);
345}
346
347void CommandLine::CopySwitchesFrom(const CommandLine& source,
348 const char* const switches[],
349 size_t count) {
350 for (size_t i = 0; i < count; ++i) {
351 if (source.HasSwitch(switches[i])) {
352 StringType value = source.GetSwitchValueNative(switches[i]);
353 AppendSwitchNative(switches[i], value);
354 }
355 }
356}
357
358void CommandLine::AppendArg(const std::string& value) {
359#if defined(OS_WIN)
360 DCHECK(IsStringUTF8(value));
361 AppendArgNative(UTF8ToWide(value));
362#elif defined(OS_POSIX)
363 AppendArgNative(value);
364#endif
365}
366
367void CommandLine::AppendArgPath(const FilePath& path) {
368 AppendArgNative(path.value());
369}
370
371void CommandLine::AppendArgNative(const CommandLine::StringType& value) {
372#if defined(OS_WIN)
373 command_line_string_.append(L" ");
374 command_line_string_.append(WindowsStyleQuote(value));
375 args_.push_back(value);
376#elif defined(OS_POSIX)
377 DCHECK(IsStringUTF8(value));
378 argv_.push_back(value);
379#endif
380}
381
382void CommandLine::AppendArgs(const CommandLine& other) {
383 if(other.args_.size() <= 0)
384 return;
385#if defined(OS_WIN)
386 command_line_string_.append(L" --");
387#endif // OS_WIN
388 StringVector::const_iterator i;
389 for (i = other.args_.begin(); i != other.args_.end(); ++i)
390 AppendArgNative(*i);
391}
392
393void CommandLine::AppendArguments(const CommandLine& other,
394 bool include_program) {
395#if defined(OS_WIN)
396 // Verify include_program is used correctly.
397 DCHECK(!include_program || !other.GetProgram().empty());
398 if (include_program)
399 program_ = other.program_;
400
401 if (!command_line_string_.empty())
402 command_line_string_ += L' ';
403
404 command_line_string_ += other.command_line_string_;
405#elif defined(OS_POSIX)
406 // Verify include_program is used correctly.
407 // Logic could be shorter but this is clearer.
408 DCHECK_EQ(include_program, !other.GetProgram().empty());
409
410 if (include_program)
411 argv_[0] = other.argv_[0];
412
413 // Skip the first arg when copying since it's the program but push all
414 // arguments to our arg vector.
415 for (size_t i = 1; i < other.argv_.size(); ++i)
416 argv_.push_back(other.argv_[i]);
417#endif
418
419 SwitchMap::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 CommandLine::StringType& wrapper) {
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 if (wrapper.empty())
428 return;
429 StringVector wrapper_and_args;
430#if defined(OS_WIN)
431 base::SplitString(wrapper, ' ', &wrapper_and_args);
432 program_ = wrapper_and_args[0];
433 command_line_string_ = wrapper + L" " + command_line_string_;
434#elif defined(OS_POSIX)
435 base::SplitString(wrapper, ' ', &wrapper_and_args);
436 argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end());
437#endif
438}
439
440#if defined(OS_WIN)
[email protected]bb975362009-01-21 01:00:22441void CommandLine::ParseFromString(const std::wstring& command_line) {
442 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
443
444 if (command_line_string_.empty())
445 return;
446
447 int num_args = 0;
448 wchar_t** args = NULL;
449
450 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
451
452 // Populate program_ with the trimmed version of the first arg.
453 TrimWhitespace(args[0], TRIM_ALL, &program_);
454
455 bool parse_switches = true;
456 for (int i = 1; i < num_args; ++i) {
457 std::wstring arg;
458 TrimWhitespace(args[i], TRIM_ALL, &arg);
459
460 if (!parse_switches) {
[email protected]2e4c50c2010-07-21 15:57:23461 args_.push_back(arg);
[email protected]bb975362009-01-21 01:00:22462 continue;
463 }
464
465 if (arg == kSwitchTerminator) {
466 parse_switches = false;
467 continue;
468 }
469
470 std::string switch_string;
471 std::wstring switch_value;
472 if (IsSwitch(arg, &switch_string, &switch_value)) {
473 switches_[switch_string] = switch_value;
474 } else {
[email protected]2e4c50c2010-07-21 15:57:23475 args_.push_back(arg);
[email protected]bb975362009-01-21 01:00:22476 }
477 }
478
479 if (args)
480 LocalFree(args);
481}
[email protected]f3adb5c2008-08-07 20:07:32482#endif