1 : // Copyright (c) 2006-2008 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 : #if defined(OS_WIN)
8 : #include <windows.h>
9 : #include <shellapi.h>
10 : #endif
11 :
12 : #include <algorithm>
13 :
14 : #include "base/logging.h"
15 : #include "base/singleton.h"
16 : #include "base/string_piece.h"
17 : #include "base/string_util.h"
18 : #include "base/sys_string_conversions.h"
19 :
20 : CommandLine* CommandLine::current_process_commandline_ = NULL;
21 :
22 : // Since we use a lazy match, make sure that longer versions (like L"--")
23 : // are listed before shorter versions (like L"-") of similar prefixes.
24 : #if defined(OS_WIN)
25 : const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
26 : const wchar_t kSwitchTerminator[] = L"--";
27 : const wchar_t kSwitchValueSeparator[] = L"=";
28 : #elif defined(OS_POSIX)
29 : // Unixes don't use slash as a switch.
30 : const char* const kSwitchPrefixes[] = {"--", "-"};
31 : const char kSwitchTerminator[] = "--";
32 : const char kSwitchValueSeparator[] = "=";
33 : #endif
34 :
35 : #if defined(OS_WIN)
36 : // Lowercase a string. This is used to lowercase switch names.
37 : // Is this what we really want? It seems crazy to me. I've left it in
38 : // for backwards compatibility on Windows.
39 : static void Lowercase(std::wstring* parameter) {
40 : transform(parameter->begin(), parameter->end(), parameter->begin(),
41 : tolower);
42 : }
43 : #endif
44 :
45 : #if defined(OS_WIN)
46 : void CommandLine::ParseFromString(const std::wstring& command_line) {
47 : TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
48 :
49 : if (command_line_string_.empty())
50 : return;
51 :
52 : int num_args = 0;
53 : wchar_t** args = NULL;
54 :
55 : args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
56 :
57 : // Populate program_ with the trimmed version of the first arg.
58 : TrimWhitespace(args[0], TRIM_ALL, &program_);
59 :
60 : bool parse_switches = true;
61 : for (int i = 1; i < num_args; ++i) {
62 : std::wstring arg;
63 : TrimWhitespace(args[i], TRIM_ALL, &arg);
64 :
65 : if (!parse_switches) {
66 : loose_values_.push_back(arg);
67 : continue;
68 : }
69 :
70 : if (arg == kSwitchTerminator) {
71 : parse_switches = false;
72 : continue;
73 : }
74 :
75 : std::string switch_string;
76 : std::wstring switch_value;
77 : if (IsSwitch(arg, &switch_string, &switch_value)) {
78 : switches_[switch_string] = switch_value;
79 : } else {
80 : loose_values_.push_back(arg);
81 : }
82 : }
83 :
84 : if (args)
85 : LocalFree(args);
86 : }
87 : CommandLine::CommandLine(const std::wstring& program) {
88 : if (!program.empty()) {
89 : program_ = program;
90 : command_line_string_ = L'"' + program + L'"';
91 : }
92 : }
93 : #elif defined(OS_POSIX)
94 1443 : CommandLine::CommandLine(int argc, const char* const* argv) {
95 2955 : for (int i = 0; i < argc; ++i)
96 1512 : argv_.push_back(argv[i]);
97 1443 : InitFromArgv();
98 1443 : }
99 0 : CommandLine::CommandLine(const std::vector<std::string>& argv) {
100 0 : argv_ = argv;
101 0 : InitFromArgv();
102 0 : }
103 :
104 1443 : void CommandLine::InitFromArgv() {
105 1443 : bool parse_switches = true;
106 1512 : for (size_t i = 1; i < argv_.size(); ++i) {
107 69 : const std::string& arg = argv_[i];
108 :
109 69 : if (!parse_switches) {
110 0 : loose_values_.push_back(arg);
111 0 : continue;
112 : }
113 :
114 69 : if (arg == kSwitchTerminator) {
115 0 : parse_switches = false;
116 0 : continue;
117 : }
118 :
119 138 : std::string switch_string;
120 138 : std::string switch_value;
121 69 : if (IsSwitch(arg, &switch_string, &switch_value)) {
122 25 : switches_[switch_string] = switch_value;
123 : } else {
124 44 : loose_values_.push_back(arg);
125 : }
126 : }
127 1443 : }
128 :
129 0 : CommandLine::CommandLine(const std::wstring& program) {
130 0 : argv_.push_back(WideToASCII(program));
131 0 : }
132 : #endif
133 :
134 : // static
135 69 : bool CommandLine::IsSwitch(const StringType& parameter_string,
136 : std::string* switch_string,
137 : StringType* switch_value) {
138 69 : switch_string->clear();
139 69 : switch_value->clear();
140 :
141 182 : for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
142 276 : StringType prefix(kSwitchPrefixes[i]);
143 138 : if (parameter_string.find(prefix) != 0)
144 113 : continue;
145 :
146 25 : const size_t switch_start = prefix.length();
147 : const size_t equals_position = parameter_string.find(
148 25 : kSwitchValueSeparator, switch_start);
149 50 : StringType switch_native;
150 25 : if (equals_position == StringType::npos) {
151 25 : switch_native = parameter_string.substr(switch_start);
152 : } else {
153 : switch_native = parameter_string.substr(
154 0 : switch_start, equals_position - switch_start);
155 0 : *switch_value = parameter_string.substr(equals_position + 1);
156 : }
157 : #if defined(OS_WIN)
158 : Lowercase(&switch_native);
159 : *switch_string = WideToASCII(switch_native);
160 : #else
161 25 : *switch_string = switch_native;
162 : #endif
163 :
164 25 : return true;
165 : }
166 :
167 44 : return false;
168 : }
169 :
170 : // static
171 1443 : void CommandLine::Init(int argc, const char* const* argv) {
172 1443 : DCHECK(current_process_commandline_ == NULL);
173 : #if defined(OS_WIN)
174 : current_process_commandline_ = new CommandLine;
175 : current_process_commandline_->ParseFromString(::GetCommandLineW());
176 : #elif defined(OS_POSIX)
177 1443 : current_process_commandline_ = new CommandLine(argc, argv);
178 : #endif
179 1443 : }
180 :
181 1419 : void CommandLine::Terminate() {
182 1419 : DCHECK(current_process_commandline_ != NULL);
183 1419 : delete current_process_commandline_;
184 1419 : current_process_commandline_ = NULL;
185 1419 : }
186 :
187 1 : bool CommandLine::HasSwitch(const std::wstring& switch_string) const {
188 2 : std::wstring lowercased_switch(switch_string);
189 : #if defined(OS_WIN)
190 : Lowercase(&lowercased_switch);
191 : #endif
192 1 : return switches_.find(WideToASCII(lowercased_switch)) != switches_.end();
193 : }
194 :
195 1 : std::wstring CommandLine::GetSwitchValue(
196 : const std::wstring& switch_string) const {
197 2 : std::wstring lowercased_switch(switch_string);
198 : #if defined(OS_WIN)
199 : Lowercase(&lowercased_switch);
200 : #endif
201 :
202 : std::map<std::string, StringType>::const_iterator result =
203 1 : switches_.find(WideToASCII(lowercased_switch));
204 :
205 1 : if (result == switches_.end()) {
206 1 : return L"";
207 : } else {
208 : #if defined(OS_WIN)
209 : return result->second;
210 : #else
211 0 : return ASCIIToWide(result->second);
212 : #endif
213 : }
214 : }
215 :
216 : #if defined(OS_WIN)
217 : std::vector<std::wstring> CommandLine::GetLooseValues() const {
218 : return loose_values_;
219 : }
220 : std::wstring CommandLine::program() const {
221 : return program_;
222 : }
223 : #else
224 0 : std::vector<std::wstring> CommandLine::GetLooseValues() const {
225 0 : std::vector<std::wstring> values;
226 0 : for (size_t i = 0; i < loose_values_.size(); ++i)
227 0 : values.push_back(ASCIIToWide(loose_values_[i]));
228 : return values;
229 : }
230 0 : std::wstring CommandLine::program() const {
231 0 : DCHECK(argv_.size() > 0);
232 0 : return ASCIIToWide(argv_[0]);
233 : }
234 : #endif
235 :
236 :
237 : // static
238 0 : std::wstring CommandLine::PrefixedSwitchString(
239 : const std::wstring& switch_string) {
240 : return StringPrintf(L"%ls%ls",
241 0 : kSwitchPrefixes[0],
242 0 : switch_string.c_str());
243 : }
244 :
245 : // static
246 0 : std::wstring CommandLine::PrefixedSwitchStringWithValue(
247 : const std::wstring& switch_string, const std::wstring& value_string) {
248 0 : if (value_string.empty()) {
249 0 : return PrefixedSwitchString(switch_string);
250 : }
251 :
252 : return StringPrintf(L"%ls%ls%ls%ls",
253 0 : kSwitchPrefixes[0],
254 : switch_string.c_str(),
255 : kSwitchValueSeparator,
256 0 : value_string.c_str());
257 : }
258 :
259 : #if defined(OS_WIN)
260 : void CommandLine::AppendSwitch(const std::wstring& switch_string) {
261 : std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string);
262 : command_line_string_.append(L" ");
263 : command_line_string_.append(prefixed_switch_string);
264 : switches_[WideToASCII(switch_string)] = L"";
265 : }
266 :
267 : // Quote a string if necessary, such that CommandLineToArgvW() will
268 : // always process it as a single argument.
269 : static std::wstring WindowsStyleQuote(const std::wstring& arg) {
270 : // We follow the quoting rules of CommandLineToArgvW.
271 : // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
272 : if (arg.find_first_of(L" \\\"\t") == std::wstring::npos) {
273 : // No quoting necessary.
274 : return arg;
275 : }
276 :
277 : std::wstring out;
278 : out.push_back(L'"');
279 : for (size_t i = 0; i < arg.size(); ++i) {
280 : if (arg[i] == '\\') {
281 : // Find the extent of this run of backslashes.
282 : size_t start = i, end = start + 1;
283 : for (; end < arg.size() && arg[end] == '\\'; ++end)
284 : /* empty */;
285 : size_t backslash_count = end - start;
286 :
287 : // Backslashes are escapes only if the run is followed by a double quote.
288 : // Since we also will end the string with a double quote, we escape for
289 : // either a double quote or the end of the string.
290 : if (end == arg.size() || arg[end] == '"') {
291 : // To quote, we need to output 2x as many backslashes.
292 : backslash_count *= 2;
293 : }
294 : for (size_t j = 0; j < backslash_count; ++j)
295 : out.push_back('\\');
296 :
297 : // Advance i to one before the end to balance i++ in loop.
298 : i = end - 1;
299 : } else if (arg[i] == '"') {
300 : out.push_back('\\');
301 : out.push_back('"');
302 : } else {
303 : out.push_back(arg[i]);
304 : }
305 : }
306 : out.push_back('"');
307 :
308 : return out;
309 : }
310 :
311 : void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
312 : const std::wstring& value_string) {
313 : std::wstring quoted_value_string = WindowsStyleQuote(value_string);
314 : std::wstring combined_switch_string =
315 : PrefixedSwitchStringWithValue(switch_string, quoted_value_string);
316 :
317 : command_line_string_.append(L" ");
318 : command_line_string_.append(combined_switch_string);
319 :
320 : switches_[WideToASCII(switch_string)] = value_string;
321 : }
322 :
323 : void CommandLine::AppendLooseValue(const std::wstring& value) {
324 : command_line_string_.append(L" ");
325 : command_line_string_.append(WindowsStyleQuote(value));
326 : }
327 :
328 : void CommandLine::AppendArguments(const CommandLine& other,
329 : bool include_program) {
330 : // Verify include_program is used correctly.
331 : // Logic could be shorter but this is clearer.
332 : DCHECK(include_program ? !other.program().empty() : other.program().empty());
333 : command_line_string_ += L" " + other.command_line_string_;
334 : std::map<std::string, StringType>::const_iterator i;
335 : for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
336 : switches_[i->first] = i->second;
337 : }
338 :
339 : void CommandLine::PrependWrapper(const std::wstring& wrapper) {
340 : // The wrapper may have embedded arguments (like "gdb --args"). In this case,
341 : // we don't pretend to do anything fancy, we just split on spaces.
342 : std::vector<std::wstring> wrapper_and_args;
343 : SplitString(wrapper, ' ', &wrapper_and_args);
344 : program_ = wrapper_and_args[0];
345 : command_line_string_ = wrapper + L" " + command_line_string_;
346 : }
347 :
348 : #elif defined(OS_POSIX)
349 0 : void CommandLine::AppendSwitch(const std::wstring& switch_string) {
350 0 : std::string ascii_switch = WideToASCII(switch_string);
351 0 : argv_.push_back(kSwitchPrefixes[0] + ascii_switch);
352 0 : switches_[ascii_switch] = "";
353 0 : }
354 :
355 0 : void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
356 : const std::wstring& value_string) {
357 0 : std::string ascii_switch = WideToASCII(switch_string);
358 0 : std::string ascii_value = WideToASCII(value_string);
359 :
360 0 : argv_.push_back(kSwitchPrefixes[0] + ascii_switch +
361 0 : kSwitchValueSeparator + ascii_value);
362 0 : switches_[ascii_switch] = ascii_value;
363 0 : }
364 :
365 0 : void CommandLine::AppendLooseValue(const std::wstring& value) {
366 0 : argv_.push_back(WideToASCII(value));
367 0 : }
368 :
369 0 : void CommandLine::AppendArguments(const CommandLine& other,
370 : bool include_program) {
371 : // Verify include_program is used correctly.
372 : // Logic could be shorter but this is clearer.
373 0 : DCHECK(include_program ? !other.program().empty() : other.program().empty());
374 :
375 0 : size_t first_arg = include_program ? 0 : 1;
376 0 : for (size_t i = first_arg; i < other.argv_.size(); ++i)
377 0 : argv_.push_back(other.argv_[i]);
378 0 : std::map<std::string, StringType>::const_iterator i;
379 0 : for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
380 0 : switches_[i->first] = i->second;
381 0 : }
382 :
383 0 : void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) {
384 : // The wrapper may have embedded arguments (like "gdb --args"). In this case,
385 : // we don't pretend to do anything fancy, we just split on spaces.
386 0 : const std::string wrapper = WideToASCII(wrapper_wide);
387 0 : std::vector<std::string> wrapper_and_args;
388 0 : SplitString(wrapper, ' ', &wrapper_and_args);
389 0 : argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end());
390 0 : }
391 :
392 : #endif
|