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