blob: b027d2a22a121f69de76292750b8965dd4fba108 [file] [log] [blame]
msw@chromium.orgda7a5482011-02-20 15:33:04 +09001// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botf003cfe2008-08-24 09:55:55 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit3f4a7322008-07-27 06:49:38 +09004
evanm@google.com91cdff82008-08-08 05:07:32 +09005#include "base/command_line.h"
6
initial.commit3f4a7322008-07-27 06:49:38 +09007#include <algorithm>
jhawkins@chromium.org8e73d062011-04-05 03:04:37 +09008#include <ostream>
initial.commit3f4a7322008-07-27 06:49:38 +09009
jhawkins@chromium.org8e73d062011-04-05 03:04:37 +090010#include "base/basictypes.h"
thestig@chromium.org58727a42009-10-10 05:37:56 +090011#include "base/file_path.h"
initial.commit3f4a7322008-07-27 06:49:38 +090012#include "base/logging.h"
tfarina@chromium.orge995a8b2010-09-24 13:52:11 +090013#include "base/string_split.h"
initial.commit3f4a7322008-07-27 06:49:38 +090014#include "base/string_util.h"
brettw@chromium.orgc7cd8372010-08-01 02:47:09 +090015#include "base/utf_string_conversions.h"
brettw@chromium.org6a3802c2010-12-30 06:06:43 +090016#include "build/build_config.h"
initial.commit3f4a7322008-07-27 06:49:38 +090017
brettw@chromium.org6a3802c2010-12-30 06:06:43 +090018#if defined(OS_WIN)
19#include <windows.h>
20#include <shellapi.h>
mdm@chromium.org18175a02009-09-11 03:02:17 +090021#endif
22
evan@chromium.org4bbc6132009-01-21 10:00:22 +090023CommandLine* CommandLine::current_process_commandline_ = NULL;
initial.commit3f4a7322008-07-27 06:49:38 +090024
msw@chromium.org7b2af612011-03-01 11:28:42 +090025namespace {
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.
pinkerton@google.com0c0e01c2008-08-09 05:46:21 +090032#if defined(OS_WIN)
msw@chromium.org7b2af612011-03-01 11:28:42 +090033const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
pinkerton@google.com0c0e01c2008-08-09 05:46:21 +090034#elif defined(OS_POSIX)
evanm@google.com638e9fb2008-08-12 10:14:37 +090035// Unixes don't use slash as a switch.
msw@chromium.org7b2af612011-03-01 11:28:42 +090036const CharType* const kSwitchPrefixes[] = {"--", "-"};
pinkerton@google.com0c0e01c2008-08-09 05:46:21 +090037#endif
initial.commit3f4a7322008-07-27 06:49:38 +090038
evanm@google.com91cdff82008-08-08 05:07:32 +090039#if defined(OS_WIN)
msw@chromium.org7b2af612011-03-01 11:28:42 +090040// 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.commit3f4a7322008-07-27 06:49:38 +090044}
msw@chromium.orgda7a5482011-02-20 15:33:04 +090045
msw@chromium.org7b2af612011-03-01 11:28:42 +090046// Quote a string if necessary, such that CommandLineToArgvW() will always
47// process it as a single argument.
msw@chromium.orgda7a5482011-02-20 15:33:04 +090048std::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}
evan@chromium.org4bbc6132009-01-21 10:00:22 +090089#endif
initial.commit3f4a7322008-07-27 06:49:38 +090090
msw@chromium.org7b2af612011-03-01 11:28:42 +090091// 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
msw@chromium.org96b695b2011-03-02 05:47:58 +0900159CommandLine::~CommandLine() {
160}
161
msw@chromium.org7b2af612011-03-01 11:28:42 +0900162// 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_;
erg@chromium.org493f5f62010-07-16 06:03:54 +0900184}
185
evanm@google.com91cdff82008-08-08 05:07:32 +0900186#if defined(OS_WIN)
msw@chromium.org7b2af612011-03-01 11:28:42 +0900187// 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_);
evan@chromium.orgc4fee2c2009-10-27 07:39:33 +0900200}
201
msw@chromium.org7b2af612011-03-01 11:28:42 +0900202void 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)
evan@chromium.org4bbc6132009-01-21 10:00:22 +0900439void 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) {
evan@chromium.orgbb42a352010-07-22 00:57:23 +0900459 args_.push_back(arg);
evan@chromium.org4bbc6132009-01-21 10:00:22 +0900460 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 {
evan@chromium.orgbb42a352010-07-22 00:57:23 +0900473 args_.push_back(arg);
evan@chromium.org4bbc6132009-01-21 10:00:22 +0900474 }
475 }
476
477 if (args)
478 LocalFree(args);
479}
evanm@google.com91cdff82008-08-08 05:07:32 +0900480#endif
msw@chromium.org96b695b2011-03-02 05:47:58 +0900481
482CommandLine::CommandLine() {
483}