OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/command_line.h" | 5 #include "base/command_line.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
10 #include "base/file_util.h" | 10 #include "base/file_util.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/singleton.h" | 12 #include "base/singleton.h" |
13 #include "base/string_split.h" | 13 #include "base/string_split.h" |
14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
15 #include "base/sys_string_conversions.h" | 15 #include "base/sys_string_conversions.h" |
16 #include "base/utf_string_conversions.h" | 16 #include "base/utf_string_conversions.h" |
17 #include "build/build_config.h" | 17 #include "build/build_config.h" |
18 | 18 |
19 #if defined(OS_WIN) | 19 #if defined(OS_WIN) |
20 #include <windows.h> | 20 #include <windows.h> |
21 #include <shellapi.h> | 21 #include <shellapi.h> |
22 #elif defined(OS_POSIX) | 22 #elif defined(OS_POSIX) |
23 #include <limits.h> | 23 #include <limits.h> |
24 #include <stdlib.h> | 24 #include <stdlib.h> |
25 #include <unistd.h> | 25 #include <unistd.h> |
26 #endif | 26 #endif |
27 | 27 |
28 CommandLine* CommandLine::current_process_commandline_ = NULL; | 28 CommandLine* CommandLine::current_process_commandline_ = NULL; |
29 | 29 |
30 // Since we use a lazy match, make sure that longer versions (like L"--") | 30 namespace { |
31 // are listed before shorter versions (like L"-") of similar prefixes. | 31 const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); |
32 const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); | |
33 // Since we use a lazy match, make sure that longer versions (like "--") | |
34 // are listed before shorter versions (like "-") of similar prefixes. | |
32 #if defined(OS_WIN) | 35 #if defined(OS_WIN) |
33 const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; | 36 const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; |
34 const wchar_t kSwitchTerminator[] = L"--"; | |
35 const wchar_t kSwitchValueSeparator[] = L"="; | |
36 #elif defined(OS_POSIX) | 37 #elif defined(OS_POSIX) |
37 // Unixes don't use slash as a switch. | 38 // Unixes don't use slash as a switch. |
38 const char* const kSwitchPrefixes[] = {"--", "-"}; | 39 const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; |
39 const char kSwitchTerminator[] = "--"; | |
40 const char kSwitchValueSeparator[] = "="; | |
41 #endif | 40 #endif |
42 | 41 |
43 #if defined(OS_WIN) | 42 void Lowercase(string* arg); |
44 // Lowercase a string. This is used to lowercase switch names. | |
45 // Is this what we really want? It seems crazy to me. I've left it in | |
46 // for backwards compatibility on Windows. | |
47 static void Lowercase(std::string* parameter) { | |
48 transform(parameter->begin(), parameter->end(), parameter->begin(), | |
49 tolower); | |
50 } | |
51 #endif | |
52 | 43 |
53 CommandLine::~CommandLine() { | 44 // Returns true and fills in |switch_string| and |switch_value| if |
54 } | 45 // |parameter_string| represents a switch. |
55 | 46 bool IsSwitch(const CommandLine::StringType& parameter_string, |
56 #if defined(OS_WIN) | 47 string* switch_string, |
57 CommandLine::CommandLine(NoProgram no_program) { | 48 CommandLine::StringType* switch_value) { |
58 } | |
59 | |
60 void CommandLine::ParseFromString(const std::wstring& command_line) { | |
61 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); | |
62 | |
63 if (command_line_string_.empty()) | |
64 return; | |
65 | |
66 int num_args = 0; | |
67 wchar_t** args = NULL; | |
68 | |
69 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); | |
70 | |
71 // Populate program_ with the trimmed version of the first arg. | |
72 TrimWhitespace(args[0], TRIM_ALL, &program_); | |
73 | |
74 bool parse_switches = true; | |
75 for (int i = 1; i < num_args; ++i) { | |
76 std::wstring arg; | |
77 TrimWhitespace(args[i], TRIM_ALL, &arg); | |
78 | |
79 if (!parse_switches) { | |
80 args_.push_back(arg); | |
81 continue; | |
82 } | |
83 | |
evanm
2011/02/24 01:01:05
It's hard to follow what's gone on in this file.
msw
2011/05/10 23:18:43
Done (move was r76339, this change is cleaner).
| |
84 if (arg == kSwitchTerminator) { | |
85 parse_switches = false; | |
86 continue; | |
87 } | |
88 | |
89 std::string switch_string; | |
90 std::wstring switch_value; | |
91 if (IsSwitch(arg, &switch_string, &switch_value)) { | |
92 switches_[switch_string] = switch_value; | |
93 } else { | |
94 args_.push_back(arg); | |
95 } | |
96 } | |
97 | |
98 if (args) | |
99 LocalFree(args); | |
100 } | |
101 | |
102 // static | |
103 CommandLine CommandLine::FromString(const std::wstring& command_line) { | |
104 CommandLine cmd; | |
105 cmd.ParseFromString(command_line); | |
106 return cmd; | |
107 } | |
108 | |
109 CommandLine::CommandLine(const FilePath& program) { | |
110 if (!program.empty()) { | |
111 program_ = program.value(); | |
112 // TODO(evanm): proper quoting here. | |
113 command_line_string_ = L'"' + program.value() + L'"'; | |
114 } | |
115 } | |
116 | |
117 #elif defined(OS_POSIX) | |
118 CommandLine::CommandLine(NoProgram no_program) { | |
119 // Push an empty argument, because we always assume argv_[0] is a program. | |
120 argv_.push_back(""); | |
121 } | |
122 | |
123 CommandLine::CommandLine(int argc, const char* const* argv) { | |
124 InitFromArgv(argc, argv); | |
125 } | |
126 | |
127 CommandLine::CommandLine(const std::vector<std::string>& argv) { | |
128 InitFromArgv(argv); | |
129 } | |
130 | |
131 void CommandLine::InitFromArgv(int argc, const char* const* argv) { | |
132 for (int i = 0; i < argc; ++i) | |
133 argv_.push_back(argv[i]); | |
134 InitFromArgv(argv_); | |
135 } | |
136 | |
137 void CommandLine::InitFromArgv(const std::vector<std::string>& argv) { | |
138 argv_ = argv; | |
139 bool parse_switches = true; | |
140 for (size_t i = 1; i < argv_.size(); ++i) { | |
141 const std::string& arg = argv_[i]; | |
142 | |
143 if (!parse_switches) { | |
144 args_.push_back(arg); | |
145 continue; | |
146 } | |
147 | |
148 if (arg == kSwitchTerminator) { | |
149 parse_switches = false; | |
150 continue; | |
151 } | |
152 | |
153 std::string switch_string; | |
154 std::string switch_value; | |
155 if (IsSwitch(arg, &switch_string, &switch_value)) { | |
156 switches_[switch_string] = switch_value; | |
157 } else { | |
158 args_.push_back(arg); | |
159 } | |
160 } | |
161 } | |
162 | |
163 CommandLine::CommandLine(const FilePath& program) { | |
164 argv_.push_back(program.value()); | |
165 } | |
166 | |
167 #endif | |
168 | |
169 // static | |
170 bool CommandLine::IsSwitch(const StringType& parameter_string, | |
171 std::string* switch_string, | |
172 StringType* switch_value) { | |
173 switch_string->clear(); | 49 switch_string->clear(); |
174 switch_value->clear(); | 50 switch_value->clear(); |
175 | 51 |
176 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { | 52 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { |
177 StringType prefix(kSwitchPrefixes[i]); | 53 CommandLine::StringType prefix(kSwitchPrefixes[i]); |
178 if (parameter_string.find(prefix) != 0) | 54 if (parameter_string.find(prefix) != 0) |
179 continue; | 55 continue; |
180 | 56 |
181 const size_t switch_start = prefix.length(); | 57 const size_t switch_start = prefix.length(); |
182 const size_t equals_position = parameter_string.find( | 58 const size_t equals_position = parameter_string.find( |
183 kSwitchValueSeparator, switch_start); | 59 kSwitchValueSeparator, switch_start); |
184 StringType switch_native; | 60 CommandLine::StringType switch_native; |
185 if (equals_position == StringType::npos) { | 61 if (equals_position == CommandLine::StringType::npos) { |
186 switch_native = parameter_string.substr(switch_start); | 62 switch_native = parameter_string.substr(switch_start); |
187 } else { | 63 } else { |
188 switch_native = parameter_string.substr( | 64 switch_native = parameter_string.substr( |
189 switch_start, equals_position - switch_start); | 65 switch_start, equals_position - switch_start); |
190 *switch_value = parameter_string.substr(equals_position + 1); | 66 *switch_value = parameter_string.substr(equals_position + 1); |
191 } | 67 } |
192 #if defined(OS_WIN) | 68 #if defined(OS_WIN) |
193 *switch_string = WideToASCII(switch_native); | 69 *switch_string = WideToASCII(switch_native); |
194 Lowercase(switch_string); | |
195 #else | 70 #else |
196 *switch_string = switch_native; | 71 *switch_string = switch_native; |
197 #endif | 72 #endif |
73 Lowercase(switch_string); | |
198 | 74 |
199 return true; | 75 return true; |
200 } | 76 } |
201 | 77 |
202 return false; | 78 return false; |
203 } | 79 } |
204 | 80 |
81 #if defined(OS_WIN) | |
82 // Lowercase a string for case-insensitivity of switches *on Windows*. | |
83 // Is this desirable? It exists for backwards compatibility on Windows. | |
84 void Lowercase(string* arg) { | |
85 transform(arg->begin(), arg->end(), arg->begin(), tolower); | |
86 } | |
87 | |
88 CommandLine::StringType Native(const string& string) { | |
89 return ASCIIToWide(string); | |
90 } | |
91 | |
92 CommandLine::StringType Native(const std::wstring& string) { return string; } | |
93 | |
94 // Quote a string if necessary *on Windows*, such that CommandLineToArgvW() will | |
95 // always process it as a single argument. | |
96 CommandLine::StringType Quote(const CommandLine::StringType& arg) { | |
97 // We follow the quoting rules of CommandLineToArgvW. | |
98 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx | |
99 if (arg.find_first_of(L" \\\"") == CommandLine::StringType::npos) { | |
100 // No quoting necessary. | |
101 return arg; | |
102 } | |
103 | |
104 std::wstring out; | |
105 out.push_back(L'"'); | |
106 for (size_t i = 0; i < arg.size(); ++i) { | |
107 if (arg[i] == '\\') { | |
108 // Find the extent of this run of backslashes. | |
109 size_t start = i, end = start + 1; | |
110 for (; end < arg.size() && arg[end] == '\\'; ++end) | |
111 /* empty */; | |
112 size_t backslash_count = end - start; | |
113 | |
114 // Backslashes are escapes only if the run is followed by a double quote. | |
115 // Since we also will end the string with a double quote, we escape for | |
116 // either a double quote or the end of the string. | |
117 if (end == arg.size() || arg[end] == '"') { | |
118 // To quote, we need to output 2x as many backslashes. | |
119 backslash_count *= 2; | |
120 } | |
121 for (size_t j = 0; j < backslash_count; ++j) | |
122 out.push_back('\\'); | |
123 | |
124 // Advance i to one before the end to balance i++ in loop. | |
125 i = end - 1; | |
126 } else if (arg[i] == '"') { | |
127 out.push_back('\\'); | |
128 out.push_back('"'); | |
129 } else { | |
130 out.push_back(arg[i]); | |
131 } | |
132 } | |
133 out.push_back('"'); | |
134 | |
135 return out; | |
136 } | |
137 #elif defined(OS_POSIX) | |
138 void Lowercase(string* arg) {} | |
139 | |
140 CommandLine::StringType Native(const string& string) { return string; } | |
141 | |
142 CommandLine::StringType Native(const std::wstring& string) { | |
143 return WideToASCII(string); | |
144 } | |
145 | |
146 CommandLine::StringType Quote(const CommandLine::StringType& string) { | |
147 return string; | |
148 } | |
149 #endif | |
150 } // namespace | |
151 | |
152 CommandLine::CommandLine(NoProgram no_program) { | |
153 SetProgram(StringType()); | |
154 } | |
155 | |
156 CommandLine::CommandLine(const FilePath& program) { | |
157 SetProgram(program.value()); | |
158 } | |
159 | |
160 CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) { | |
161 InitFromArgv(argc, argv); | |
162 } | |
163 | |
164 CommandLine::CommandLine(const StringVector& argv) { | |
165 InitFromArgv(argv); | |
166 } | |
167 | |
205 // static | 168 // static |
206 void CommandLine::Init(int argc, const char* const* argv) { | 169 void CommandLine::Init(int argc, const char* const* argv) { |
207 delete current_process_commandline_; | 170 delete current_process_commandline_; |
208 current_process_commandline_ = new CommandLine; | 171 current_process_commandline_ = new CommandLine; |
209 #if defined(OS_WIN) | 172 #if defined(OS_WIN) |
210 current_process_commandline_->ParseFromString(::GetCommandLineW()); | 173 current_process_commandline_->ParseFromString(::GetCommandLineW()); |
211 #elif defined(OS_POSIX) | 174 #elif defined(OS_POSIX) |
212 current_process_commandline_->InitFromArgv(argc, argv); | 175 current_process_commandline_->InitFromArgv(argc, argv); |
213 #endif | 176 #endif |
214 } | 177 } |
215 | 178 |
179 // static | |
216 void CommandLine::Reset() { | 180 void CommandLine::Reset() { |
217 DCHECK(current_process_commandline_ != NULL); | 181 DCHECK(current_process_commandline_); |
218 delete current_process_commandline_; | 182 delete current_process_commandline_; |
219 current_process_commandline_ = NULL; | 183 current_process_commandline_ = NULL; |
220 } | 184 } |
221 | 185 |
222 // static | 186 // static |
223 CommandLine* CommandLine::ForCurrentProcess() { | 187 CommandLine* CommandLine::ForCurrentProcess() { |
224 DCHECK(current_process_commandline_); | 188 DCHECK(current_process_commandline_); |
225 return current_process_commandline_; | 189 return current_process_commandline_; |
226 } | 190 } |
227 | 191 |
228 bool CommandLine::HasSwitch(const std::string& switch_string) const { | 192 #if defined(OS_WIN) |
193 // static | |
194 CommandLine CommandLine::FromString( | |
195 const CommandLine::StringType& command_line) { | |
196 CommandLine cmd; | |
197 cmd.ParseFromString(command_line); | |
198 return cmd; | |
199 } | |
200 #endif | |
201 | |
202 void CommandLine::InitFromArgv(int argc, | |
203 const CommandLine::CharType* const* argv) { | |
204 StringVector new_argv; | |
205 for (int i = 0; i < argc; ++i) | |
206 new_argv.push_back(Native(argv[i])); | |
207 InitFromArgv(new_argv); | |
208 } | |
209 | |
210 void CommandLine::InitFromArgv(const StringVector argv) { | |
211 argv_.clear(); | |
212 divider_ = 0; | |
213 if (argv.size() <= 0) | |
214 return; | |
215 | |
216 // Initialize the first program argument. | |
217 SetProgram(argv[0]); | |
218 | |
219 // Copy switches/arguments to our vector, keeping switches before arguments. | |
220 bool seen_kSwitchTerminator = false; | |
221 for (size_t i = 1; i < argv.size(); ++i) { | |
222 StringType arg = argv[i]; | |
223 TrimWhitespace(arg, TRIM_ALL, &arg); | |
224 | |
225 string switch_string; | |
226 StringType switch_value; | |
227 seen_kSwitchTerminator |= arg == kSwitchTerminator; | |
228 if (!seen_kSwitchTerminator && IsSwitch(arg, &switch_string, &switch_value)) | |
229 AppendSwitchNative(switch_string, switch_value); | |
230 else | |
231 AppendArgNative(arg); | |
232 } | |
233 } | |
234 | |
235 bool CommandLine::HasSwitch(const string& switch_string) const { | |
229 std::string lowercased_switch(switch_string); | 236 std::string lowercased_switch(switch_string); |
230 #if defined(OS_WIN) | |
231 Lowercase(&lowercased_switch); | 237 Lowercase(&lowercased_switch); |
232 #endif | |
233 return switches_.find(lowercased_switch) != switches_.end(); | 238 return switches_.find(lowercased_switch) != switches_.end(); |
234 } | 239 } |
235 | 240 |
236 std::string CommandLine::GetSwitchValueASCII( | 241 string CommandLine::GetSwitchValueASCII(const string& switch_string) const { |
237 const std::string& switch_string) const { | 242 StringType value = GetSwitchValueNative(switch_string); |
238 CommandLine::StringType value = GetSwitchValueNative(switch_string); | |
239 if (!IsStringASCII(value)) { | 243 if (!IsStringASCII(value)) { |
240 LOG(WARNING) << "Value of --" << switch_string << " must be ASCII."; | 244 LOG(WARNING) << "Value of --" << switch_string << " must be ASCII."; |
241 return ""; | 245 return ""; |
242 } | 246 } |
243 #if defined(OS_WIN) | 247 #if defined(OS_WIN) |
244 return WideToASCII(value); | 248 return WideToASCII(value); |
245 #else | 249 #else |
246 return value; | 250 return value; |
247 #endif | 251 #endif |
248 } | 252 } |
249 | 253 |
250 FilePath CommandLine::GetSwitchValuePath( | 254 FilePath CommandLine::GetSwitchValuePath(const string& switch_string) const { |
251 const std::string& switch_string) const { | |
252 return FilePath(GetSwitchValueNative(switch_string)); | 255 return FilePath(GetSwitchValueNative(switch_string)); |
253 } | 256 } |
254 | 257 |
255 CommandLine::StringType CommandLine::GetSwitchValueNative( | 258 CommandLine::StringType CommandLine::GetSwitchValueNative( |
256 const std::string& switch_string) const { | 259 const string& switch_string) const { |
257 std::string lowercased_switch(switch_string); | 260 std::string lowercased_switch(switch_string); |
258 #if defined(OS_WIN) | |
259 Lowercase(&lowercased_switch); | 261 Lowercase(&lowercased_switch); |
260 #endif | 262 SwitchMap::const_iterator result = switches_.find(lowercased_switch); |
263 return result == switches_.end() ? StringType() : result->second; | |
264 } | |
261 | 265 |
262 std::map<std::string, StringType>::const_iterator result = | 266 size_t CommandLine::GetSwitchCount() const { |
263 switches_.find(lowercased_switch); | 267 return switches_.size(); |
268 } | |
264 | 269 |
265 if (result == switches_.end()) { | 270 CommandLine::StringVector CommandLine::args() const { |
266 return CommandLine::StringType(); | 271 // Gather all arguments after the last switch (may include kSwitchTerminator). |
267 } else { | 272 StringVector args(argv_.begin() + divider_, argv_.end()); |
268 return result->second; | 273 |
269 } | 274 // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) |
275 StringVector::iterator it = args.begin(); | |
276 for (; it < args.end() && *it != kSwitchTerminator; ++it) | |
277 /* empty */; | |
278 if (it != args.end()) | |
279 args.erase(it); | |
280 | |
281 return args; | |
282 } | |
283 | |
284 CommandLine::StringType CommandLine::command_line_string() const { | |
285 return JoinString(argv_, FILE_PATH_LITERAL(' ')); | |
270 } | 286 } |
271 | 287 |
272 FilePath CommandLine::GetProgram() const { | 288 FilePath CommandLine::GetProgram() const { |
273 #if defined(OS_WIN) | 289 #if defined(OS_WIN) |
274 return FilePath(program_); | 290 // Ensure that quotes are not returned from GetProgram(). |
275 #else | 291 // TODO(evanm): proper quoting/handling here. |
276 DCHECK_GT(argv_.size(), 0U); | 292 return FilePath(argv_.size() > 0 ? argv_[0].substr(1, argv_[0].size() - 2) : |
277 return FilePath(argv_[0]); | 293 StringType()); |
294 #endif | |
295 return FilePath(argv_.size() > 0 ? argv_[0] : StringType()); | |
296 } | |
297 | |
298 void CommandLine::SetProgram(const CommandLine::StringType& program) { | |
299 CommandLine::StringType program_string(program); | |
300 TrimWhitespace(program_string, TRIM_ALL, &program_string); | |
301 | |
302 #if defined(OS_WIN) | |
303 // TODO(evanm): proper quoting here. | |
304 if (!program_string.empty()) | |
305 program_string = L'"' + program + L'"'; | |
306 #endif | |
307 | |
308 if (argv_.size() > 0){ | |
309 argv_[0] = program_string; | |
310 } else { | |
311 argv_.push_back(program_string); | |
312 divider_ = 1; | |
313 } | |
314 } | |
315 | |
316 void CommandLine::AppendSwitch(const string& switch_string) { | |
317 AppendSwitchNative(switch_string, CommandLine::StringType()); | |
318 } | |
319 | |
320 void CommandLine::AppendSwitchPath(const string& switch_string, | |
321 const FilePath& path) { | |
322 AppendSwitchNative(switch_string, path.value()); | |
323 } | |
324 | |
325 void CommandLine::AppendSwitchNative(const string& switch_string, | |
326 const CommandLine::StringType& value) { | |
327 CommandLine::StringType combined = kSwitchPrefixes[0] + Native(switch_string); | |
328 if (!value.empty()) | |
329 combined += kSwitchValueSeparator + Quote(value); | |
330 // Append the switch and update the switches/arguments |divider_|. | |
331 argv_.insert(argv_.begin() + divider_++, combined); | |
332 switches_[switch_string] = value; | |
333 } | |
334 | |
335 void CommandLine::AppendSwitchASCII(const string& switch_string, | |
336 const string& value_string) { | |
337 AppendSwitchNative(switch_string, Native(value_string)); | |
338 } | |
339 | |
340 void CommandLine::AppendSwitches(const CommandLine& other) { | |
341 SwitchMap::const_iterator i; | |
342 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) | |
343 AppendSwitchNative(i->first, i->second); | |
344 } | |
345 | |
346 void CommandLine::AppendArg(const string& value) { | |
347 #if defined(OS_WIN) | |
348 DCHECK(IsStringUTF8(value)); | |
349 AppendArgNative(UTF8ToWide(value)); | |
350 #elif defined(OS_POSIX) | |
351 AppendArgNative(value); | |
278 #endif | 352 #endif |
279 } | 353 } |
280 | 354 |
281 #if defined(OS_POSIX) | 355 void CommandLine::AppendArgPath(const FilePath& path) { |
282 std::string CommandLine::command_line_string() const { | 356 AppendArgNative(path.value()); |
283 return JoinString(argv_, ' '); | |
284 } | |
285 #endif | |
286 | |
287 #if defined(OS_WIN) | |
288 void CommandLine::AppendSwitch(const std::string& switch_string) { | |
289 command_line_string_.append(L" "); | |
290 command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string)); | |
291 switches_[switch_string] = L""; | |
292 } | 357 } |
293 | 358 |
294 void CommandLine::AppendSwitchASCII(const std::string& switch_string, | 359 void CommandLine::AppendArgNative(const CommandLine::StringType& value) { |
295 const std::string& value_string) { | 360 #if defined(OS_POSIX) |
296 AppendSwitchNative(switch_string, ASCIIToWide(value_string)); | |
297 } | |
298 | |
299 // Quote a string if necessary, such that CommandLineToArgvW() will | |
300 // always process it as a single argument. | |
301 static std::wstring WindowsStyleQuote(const std::wstring& arg) { | |
302 // We follow the quoting rules of CommandLineToArgvW. | |
303 // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx | |
304 if (arg.find_first_of(L" \\\"") == std::wstring::npos) { | |
305 // No quoting necessary. | |
306 return arg; | |
307 } | |
308 | |
309 std::wstring out; | |
310 out.push_back(L'"'); | |
311 for (size_t i = 0; i < arg.size(); ++i) { | |
312 if (arg[i] == '\\') { | |
313 // Find the extent of this run of backslashes. | |
314 size_t start = i, end = start + 1; | |
315 for (; end < arg.size() && arg[end] == '\\'; ++end) | |
316 /* empty */; | |
317 size_t backslash_count = end - start; | |
318 | |
319 // Backslashes are escapes only if the run is followed by a double quote. | |
320 // Since we also will end the string with a double quote, we escape for | |
321 // either a double quote or the end of the string. | |
322 if (end == arg.size() || arg[end] == '"') { | |
323 // To quote, we need to output 2x as many backslashes. | |
324 backslash_count *= 2; | |
325 } | |
326 for (size_t j = 0; j < backslash_count; ++j) | |
327 out.push_back('\\'); | |
328 | |
329 // Advance i to one before the end to balance i++ in loop. | |
330 i = end - 1; | |
331 } else if (arg[i] == '"') { | |
332 out.push_back('\\'); | |
333 out.push_back('"'); | |
334 } else { | |
335 out.push_back(arg[i]); | |
336 } | |
337 } | |
338 out.push_back('"'); | |
339 | |
340 return out; | |
341 } | |
342 | |
343 void CommandLine::AppendSwitchNative(const std::string& switch_string, | |
344 const std::wstring& value) { | |
345 std::wstring combined_switch_string = | |
346 kSwitchPrefixes[0] + ASCIIToWide(switch_string); | |
347 if (!value.empty()) | |
348 combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value); | |
349 | |
350 command_line_string_.append(L" "); | |
351 command_line_string_.append(combined_switch_string); | |
352 | |
353 switches_[switch_string] = value; | |
354 } | |
355 | |
356 void CommandLine::AppendArg(const std::string& value) { | |
357 DCHECK(IsStringUTF8(value)); | 361 DCHECK(IsStringUTF8(value)); |
358 AppendArgNative(UTF8ToWide(value)); | 362 #endif |
359 } | 363 argv_.push_back(Quote(value)); |
360 | |
361 void CommandLine::AppendArgNative(const std::wstring& value) { | |
362 command_line_string_.append(L" "); | |
363 command_line_string_.append(WindowsStyleQuote(value)); | |
364 args_.push_back(value); | |
365 } | 364 } |
366 | 365 |
367 void CommandLine::AppendArguments(const CommandLine& other, | 366 void CommandLine::AppendArguments(const CommandLine& other, |
368 bool include_program) { | 367 bool include_program) { |
369 // Verify include_program is used correctly. | 368 // Verify include_program is used correctly. |
370 // Logic could be shorter but this is clearer. | 369 DCHECK(!include_program || other.argv_.size() > 0); |
371 DCHECK_EQ(include_program, !other.GetProgram().empty()); | |
372 if (include_program) | 370 if (include_program) |
373 program_ = other.program_; | 371 SetProgram(other.GetProgram().value()); |
374 | 372 |
375 if (!command_line_string_.empty()) | 373 // Copy switches/arguments to our vector, keeping switches before arguments. |
376 command_line_string_ += L' '; | 374 bool seen_kSwitchTerminator = false; |
375 for (size_t i = 1; i < other.argv_.size(); ++i) { | |
376 StringType arg = other.argv_[i]; | |
377 TrimWhitespace(arg, TRIM_ALL, &arg); | |
377 | 378 |
378 command_line_string_ += other.command_line_string_; | 379 string switch_string; |
379 | 380 StringType switch_value; |
380 std::map<std::string, StringType>::const_iterator i; | 381 seen_kSwitchTerminator |= arg == kSwitchTerminator; |
381 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) | 382 if (!seen_kSwitchTerminator && IsSwitch(arg, &switch_string, &switch_value)) |
382 switches_[i->first] = i->second; | 383 AppendSwitchNative(switch_string, switch_value); |
384 else | |
385 AppendArgNative(arg); | |
386 } | |
383 } | 387 } |
384 | 388 |
385 void CommandLine::PrependWrapper(const std::wstring& wrapper) { | 389 void CommandLine::PrependWrapper(const StringType& wrapper) { |
386 if (wrapper.empty()) | 390 if (wrapper.empty()) |
387 return; | 391 return; |
388 // The wrapper may have embedded arguments (like "gdb --args"). In this case, | 392 // The wrapper may have embedded arguments (like "gdb --args"). In this case, |
389 // we don't pretend to do anything fancy, we just split on spaces. | 393 // we don't pretend to do anything fancy, we just split on spaces. |
390 std::vector<std::wstring> wrapper_and_args; | 394 StringVector prefix; |
391 base::SplitString(wrapper, ' ', &wrapper_and_args); | 395 base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &prefix); |
392 program_ = wrapper_and_args[0]; | 396 // Prepend the wrapper and update the switches/arguments |divider_|. |
393 command_line_string_ = wrapper + L" " + command_line_string_; | 397 argv_.insert(argv_.begin(), prefix.begin(), prefix.end()); |
394 } | 398 divider_ += prefix.size(); |
395 | |
396 #elif defined(OS_POSIX) | |
397 void CommandLine::AppendSwitch(const std::string& switch_string) { | |
398 argv_.push_back(kSwitchPrefixes[0] + switch_string); | |
399 switches_[switch_string] = ""; | |
400 } | |
401 | |
402 void CommandLine::AppendSwitchNative(const std::string& switch_string, | |
403 const std::string& value) { | |
404 std::string combined_switch_string = kSwitchPrefixes[0] + switch_string; | |
405 if (!value.empty()) | |
406 combined_switch_string += kSwitchValueSeparator + value; | |
407 argv_.push_back(combined_switch_string); | |
408 switches_[switch_string] = value; | |
409 } | |
410 | |
411 void CommandLine::AppendSwitchASCII(const std::string& switch_string, | |
412 const std::string& value_string) { | |
413 AppendSwitchNative(switch_string, value_string); | |
414 } | |
415 | |
416 void CommandLine::AppendArg(const std::string& value) { | |
417 AppendArgNative(value); | |
418 } | |
419 | |
420 void CommandLine::AppendArgNative(const std::string& value) { | |
421 DCHECK(IsStringUTF8(value)); | |
422 argv_.push_back(value); | |
423 } | |
424 | |
425 void CommandLine::AppendArguments(const CommandLine& other, | |
426 bool include_program) { | |
427 // Verify include_program is used correctly. | |
428 // Logic could be shorter but this is clearer. | |
429 DCHECK_EQ(include_program, !other.GetProgram().empty()); | |
430 | |
431 if (include_program) | |
432 argv_[0] = other.argv_[0]; | |
433 | |
434 // Skip the first arg when copying since it's the program but push all | |
435 // arguments to our arg vector. | |
436 for (size_t i = 1; i < other.argv_.size(); ++i) | |
437 argv_.push_back(other.argv_[i]); | |
438 | |
439 std::map<std::string, StringType>::const_iterator i; | |
440 for (i = other.switches_.begin(); i != other.switches_.end(); ++i) | |
441 switches_[i->first] = i->second; | |
442 } | |
443 | |
444 void CommandLine::PrependWrapper(const std::string& wrapper) { | |
445 // The wrapper may have embedded arguments (like "gdb --args"). In this case, | |
446 // we don't pretend to do anything fancy, we just split on spaces. | |
447 std::vector<std::string> wrapper_and_args; | |
448 base::SplitString(wrapper, ' ', &wrapper_and_args); | |
449 argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); | |
450 } | |
451 | |
452 #endif | |
453 | |
454 void CommandLine::AppendArgPath(const FilePath& path) { | |
455 AppendArgNative(path.value()); | |
456 } | |
457 | |
458 void CommandLine::AppendSwitchPath(const std::string& switch_string, | |
459 const FilePath& path) { | |
460 AppendSwitchNative(switch_string, path.value()); | |
461 } | 399 } |
462 | 400 |
463 void CommandLine::CopySwitchesFrom(const CommandLine& source, | 401 void CommandLine::CopySwitchesFrom(const CommandLine& source, |
464 const char* const switches[], | 402 const char* const switches[], |
465 size_t count) { | 403 size_t count) { |
466 for (size_t i = 0; i < count; ++i) { | 404 for (size_t i = 0; i < count; ++i) { |
467 if (source.HasSwitch(switches[i])) { | 405 if (source.HasSwitch(switches[i])) { |
468 StringType value = source.GetSwitchValueNative(switches[i]); | 406 StringType value = source.GetSwitchValueNative(switches[i]); |
469 AppendSwitchNative(switches[i], value); | 407 AppendSwitchNative(switches[i], value); |
470 } | 408 } |
471 } | 409 } |
472 } | 410 } |
473 | 411 |
474 // private | 412 #if defined(OS_WIN) |
475 CommandLine::CommandLine() { | 413 void CommandLine::ParseFromString(const CommandLine::StringType& command_line) { |
414 StringType command_line_string; | |
415 TrimWhitespace(command_line, TRIM_ALL, &command_line_string); | |
416 if (command_line_string.empty()) | |
417 return; | |
418 | |
419 int argc = 0; | |
420 wchar_t** argv = NULL; | |
421 argv = ::CommandLineToArgvW(command_line_string.c_str(), &argc); | |
422 | |
423 CHECK(argv); | |
424 InitFromArgv(argc, argv); | |
425 LocalFree(argv); | |
476 } | 426 } |
477 | 427 #endif |
478 // static | |
479 CommandLine* CommandLine::ForCurrentProcessMutable() { | |
480 DCHECK(current_process_commandline_); | |
481 return current_process_commandline_; | |
482 } | |
OLD | NEW |