Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(34)

Side by Side Diff: base/command_line.cc

Issue 18248: CommandLine API rework (Closed)
Patch Set: fixes Created 11 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/command_line.h ('k') | base/command_line_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 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 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 #if defined(OS_WIN) 7 #if defined(OS_WIN)
8 #include <windows.h> 8 #include <windows.h>
9 #include <shellapi.h> 9 #include <shellapi.h>
10 #endif 10 #endif
11 11
12 #include <algorithm> 12 #include <algorithm>
13 13
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/singleton.h" 15 #include "base/singleton.h"
16 #include "base/string_piece.h" 16 #include "base/string_piece.h"
17 #include "base/string_util.h" 17 #include "base/string_util.h"
18 #include "base/sys_string_conversions.h" 18 #include "base/sys_string_conversions.h"
19 19
20 using namespace std; 20 CommandLine* CommandLine::current_process_commandline_ = NULL;
21 21
22 // Since we use a lazy match, make sure that longer versions (like L"--") 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. 23 // are listed before shorter versions (like L"-") of similar prefixes.
24 #if defined(OS_WIN) 24 #if defined(OS_WIN)
25 const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"}; 25 const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
26 const wchar_t kSwitchTerminator[] = L"--";
27 const wchar_t kSwitchValueSeparator[] = L"=";
26 #elif defined(OS_POSIX) 28 #elif defined(OS_POSIX)
27 // Unixes don't use slash as a switch. 29 // Unixes don't use slash as a switch.
28 const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-"}; 30 const char* const kSwitchPrefixes[] = {"--", "-"};
29 #endif 31 const char kSwitchTerminator[] = "--";
30 32 const char kSwitchValueSeparator[] = "=";
31 const wchar_t CommandLine::kSwitchValueSeparator[] = L"="; 33 #endif
32 const wchar_t CommandLine::kSwitchTerminator[] = L"--"; 34
33 35 #if defined(OS_WIN)
34 // Needed to avoid a typecast on the tolower() function pointer in Lowercase(). 36 // Lowercase a string. This is used to lowercase switch names.
35 // MSVC accepts it as-is but GCC requires the typecast. 37 // Is this what we really want? It seems crazy to me. I've left it in
36 static int ToLower(int c) { 38 // for backwards compatibility on Windows.
37 return tolower(c); 39 static void Lowercase(std::wstring* parameter) {
38 } 40 transform(parameter->begin(), parameter->end(), parameter->begin(),
39 41 tolower);
40 static void Lowercase(wstring* parameter) { 42 }
41 transform(parameter->begin(), parameter->end(), parameter->begin(), 43 #endif
42 ToLower); 44
43 } 45 #if defined(OS_WIN)
44 46 void CommandLine::ParseFromString(const std::wstring& command_line) {
45 // CommandLine::Data 47 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
46 // 48
47 // This object holds the parsed data for a command line. We hold this in a 49 if (command_line_string_.empty())
48 // separate object from |CommandLine| so that we can share the parsed data 50 return;
49 // across multiple |CommandLine| objects. When we share |Data|, we might be 51
50 // accessing this object on multiple threads. To ensure thread safety, the 52 int num_args = 0;
51 // public interface of this object is const only. 53 wchar_t** args = NULL;
52 // 54
53 // Do NOT add any non-const methods to this object. You have been warned. 55 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
54 class CommandLine::Data { 56
55 public: 57 // Populate program_ with the trimmed version of the first arg.
56 #if defined(OS_WIN) 58 TrimWhitespace(args[0], TRIM_ALL, &program_);
57 Data() { 59
58 Init(GetCommandLineW()); 60 bool parse_switches = true;
59 } 61 for (int i = 1; i < num_args; ++i) {
60 62 std::wstring arg;
61 Data(const wstring& command_line) { 63 TrimWhitespace(args[i], TRIM_ALL, &arg);
62 Init(command_line); 64
63 } 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 }
64 #elif defined(OS_POSIX) 93 #elif defined(OS_POSIX)
65 Data() { 94 CommandLine::CommandLine(int argc, const char* const* argv) {
66 // Owner must call Init(). 95 for (int i = 0; i < argc; ++i)
67 } 96 argv_.push_back(argv[i]);
68 97 InitFromArgv();
69 Data(int argc, const char* const* argv) { 98 }
70 Init(argc, argv); 99 CommandLine::CommandLine(const std::vector<std::string>& argv) {
71 } 100 argv_ = argv;
72 #endif // defined(OS_POSIX) 101 InitFromArgv();
73 102 }
74 #if defined(OS_WIN) 103
75 // Does the actual parsing of the command line. 104 void CommandLine::InitFromArgv() {
76 void Init(const std::wstring& command_line) { 105 bool parse_switches = true;
77 TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); 106 for (size_t i = 1; i < argv_.size(); ++i) {
78 107 const std::string& arg = argv_[i];
79 if (command_line_string_.empty()) 108
80 return; 109 if (!parse_switches) {
81 110 loose_values_.push_back(arg);
82 int num_args = 0; 111 continue;
83 wchar_t** args = NULL; 112 }
84 113
85 args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); 114 if (arg == kSwitchTerminator) {
86 115 parse_switches = false;
87 // Populate program_ with the trimmed version of the first arg. 116 continue;
88 TrimWhitespace(args[0], TRIM_ALL, &program_); 117 }
89 118
90 bool parse_switches = true; 119 std::string switch_string;
91 for (int i = 1; i < num_args; ++i) { 120 std::string switch_value;
92 wstring arg; 121 if (IsSwitch(arg, &switch_string, &switch_value)) {
93 TrimWhitespace(args[i], TRIM_ALL, &arg); 122 switches_[switch_string] = switch_value;
94 123 } else {
95 if (!parse_switches) { 124 loose_values_.push_back(arg);
96 loose_values_.push_back(arg); 125 }
97 continue; 126 }
98 } 127 }
99 128
100 if (arg == kSwitchTerminator) { 129 CommandLine::CommandLine(const std::wstring& program) {
101 parse_switches = false; 130 argv_.push_back(WideToASCII(program));
102 continue; 131 }
103 } 132 #endif
104 133
105 wstring switch_string; 134 // static
106 wstring switch_value; 135 bool CommandLine::IsSwitch(const StringType& parameter_string,
107 if (IsSwitch(arg, &switch_string, &switch_value)) { 136 std::string* switch_string,
108 switches_[switch_string] = switch_value; 137 StringType* switch_value) {
109 } else { 138 switch_string->clear();
110 loose_values_.push_back(arg); 139 switch_value->clear();
111 } 140
112 } 141 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
113 142 StringType prefix(kSwitchPrefixes[i]);
114 if (args) 143 if (parameter_string.find(prefix) != 0)
115 LocalFree(args); 144 continue;
116 } 145
146 const size_t switch_start = prefix.length();
147 const size_t equals_position = parameter_string.find(
148 kSwitchValueSeparator, switch_start);
149 StringType switch_native;
150 if (equals_position == StringType::npos) {
151 switch_native = parameter_string.substr(switch_start);
152 } else {
153 switch_native = parameter_string.substr(
154 switch_start, equals_position - switch_start);
155 *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 *switch_string = switch_native;
162 #endif
163
164 return true;
165 }
166
167 return false;
168 }
169
170 // static
171 void CommandLine::Init(int argc, const char* const* argv) {
172 DCHECK(current_process_commandline_ == NULL);
173 #if defined(OS_WIN)
174 current_process_commandline_ = new CommandLine;
175 current_process_commandline_->ParseFromString(::GetCommandLineW());
117 #elif defined(OS_POSIX) 176 #elif defined(OS_POSIX)
118 // Does the actual parsing of the command line. 177 current_process_commandline_ = new CommandLine(argc, argv);
119 void Init(int argc, const char* const* argv) { 178 #endif
120 if (argc < 1) 179 }
121 return; 180
122 program_ = base::SysNativeMBToWide(argv[0]); 181 bool CommandLine::HasSwitch(const std::wstring& switch_string) const {
123 argv_.push_back(std::string(argv[0])); 182 std::wstring lowercased_switch(switch_string);
124 command_line_string_ = program_; 183 #if defined(OS_WIN)
125
126 bool parse_switches = true;
127 for (int i = 1; i < argc; ++i) {
128 std::wstring arg = base::SysNativeMBToWide(argv[i]);
129 argv_.push_back(argv[i]);
130 command_line_string_.append(L" ");
131 command_line_string_.append(arg);
132
133 if (!parse_switches) {
134 loose_values_.push_back(arg);
135 continue;
136 }
137
138 if (arg == kSwitchTerminator) {
139 parse_switches = false;
140 continue;
141 }
142
143 wstring switch_string;
144 wstring switch_value;
145 if (IsSwitch(arg, &switch_string, &switch_value)) {
146 switches_[switch_string] = switch_value;
147 } else {
148 loose_values_.push_back(arg);
149 }
150 }
151 }
152 #endif
153
154 const std::wstring& command_line_string() const {
155 return command_line_string_;
156 }
157
158 const std::wstring& program() const {
159 return program_;
160 }
161
162 const std::map<std::wstring, std::wstring>& switches() const {
163 return switches_;
164 }
165
166 const std::vector<std::wstring>& loose_values() const {
167 return loose_values_;
168 }
169
170 #if defined(OS_POSIX)
171 const std::vector<std::string>& argv() const {
172 return argv_;
173 }
174 #endif
175
176 private:
177 // Returns true if parameter_string represents a switch. If true,
178 // switch_string and switch_value are set. (If false, both are
179 // set to the empty string.)
180 static bool IsSwitch(const wstring& parameter_string,
181 wstring* switch_string,
182 wstring* switch_value) {
183
184 *switch_string = L"";
185 *switch_value = L"";
186
187 for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
188 std::wstring prefix(kSwitchPrefixes[i]);
189 if (parameter_string.find(prefix) != 0) // check prefix
190 continue;
191
192 const size_t switch_start = prefix.length();
193 const size_t equals_position = parameter_string.find(
194 kSwitchValueSeparator, switch_start);
195 if (equals_position == wstring::npos) {
196 *switch_string = parameter_string.substr(switch_start);
197 } else {
198 *switch_string = parameter_string.substr(
199 switch_start, equals_position - switch_start);
200 *switch_value = parameter_string.substr(equals_position + 1);
201 }
202 Lowercase(switch_string);
203
204 return true;
205 }
206
207 return false;
208 }
209
210 std::wstring command_line_string_;
211 std::wstring program_;
212 std::map<std::wstring, std::wstring> switches_;
213 std::vector<std::wstring> loose_values_;
214 std::vector<std::string> argv_;
215
216 DISALLOW_EVIL_CONSTRUCTORS(Data);
217 };
218
219 CommandLine::CommandLine()
220 : we_own_data_(false), // The Singleton class will manage it for us.
221 data_(Singleton<Data>::get()) {
222 DCHECK(!data_->command_line_string().empty()) <<
223 "You must call CommandLine::SetArgcArgv before making any CommandLine "
224 "calls.";
225 }
226
227 #if defined(OS_WIN)
228 CommandLine::CommandLine(const wstring& command_line)
229 : we_own_data_(true),
230 data_(new Data(command_line)) {
231 }
232 #elif defined(OS_POSIX)
233 CommandLine::CommandLine(const int argc, const char* const* argv)
234 : we_own_data_(true),
235 data_(new Data(argc, argv)) {
236 }
237
238 CommandLine::CommandLine(const std::vector<std::string>& argv)
239 : we_own_data_(true) {
240 const char* argv_copy[argv.size()];
241 for (size_t i = 0; i < argv.size(); i++) {
242 argv_copy[i] = argv[i].c_str();
243 }
244 data_ = new Data(argv.size(), argv_copy);
245 }
246 #endif
247
248 CommandLine::~CommandLine() {
249 if (we_own_data_)
250 delete data_;
251 }
252
253 // static
254 void CommandLine::SetArgcArgv(int argc, const char* const* argv) {
255 #if !defined(OS_WIN)
256 Singleton<Data>::get()->Init(argc, argv);
257 #endif
258 }
259
260 bool CommandLine::HasSwitch(const wstring& switch_string) const {
261 wstring lowercased_switch(switch_string);
262 Lowercase(&lowercased_switch); 184 Lowercase(&lowercased_switch);
263 return data_->switches().find(lowercased_switch) != data_->switches().end(); 185 #endif
264 } 186 return switches_.find(WideToASCII(lowercased_switch)) != switches_.end();
265 187 }
266 wstring CommandLine::GetSwitchValue(const wstring& switch_string) const { 188
267 wstring lowercased_switch(switch_string); 189 std::wstring CommandLine::GetSwitchValue(
190 const std::wstring& switch_string) const {
191 std::wstring lowercased_switch(switch_string);
192 #if defined(OS_WIN)
268 Lowercase(&lowercased_switch); 193 Lowercase(&lowercased_switch);
269 194 #endif
270 const map<wstring, wstring>::const_iterator result = 195
271 data_->switches().find(lowercased_switch); 196 std::map<std::string, StringType>::const_iterator result =
272 197 switches_.find(WideToASCII(lowercased_switch));
273 if (result == data_->switches().end()) { 198
199 if (result == switches_.end()) {
274 return L""; 200 return L"";
275 } else { 201 } else {
202 #if defined(OS_WIN)
276 return result->second; 203 return result->second;
277 } 204 #else
278 } 205 return ASCIIToWide(result->second);
279 206 #endif
280 size_t CommandLine::GetLooseValueCount() const { 207 }
281 return data_->loose_values().size(); 208 }
282 } 209
283 210 #if defined(OS_WIN)
284 CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const { 211 std::vector<std::wstring> CommandLine::GetLooseValues() const {
285 return data_->loose_values().begin(); 212 return loose_values_;
286 } 213 }
287
288 CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const {
289 return data_->loose_values().end();
290 }
291
292 std::wstring CommandLine::command_line_string() const {
293 return data_->command_line_string();
294 }
295
296 #if defined(OS_POSIX)
297 const std::vector<std::string>& CommandLine::argv() const {
298 return data_->argv();
299 }
300 #endif
301
302 std::wstring CommandLine::program() const { 214 std::wstring CommandLine::program() const {
303 return data_->program(); 215 return program_;
304 } 216 }
305 217 #else
306 // static 218 std::vector<std::wstring> CommandLine::GetLooseValues() const {
307 wstring CommandLine::PrefixedSwitchString(const wstring& switch_string) { 219 std::vector<std::wstring> values;
220 for (size_t i = 0; i < loose_values_.size(); ++i)
221 values.push_back(ASCIIToWide(loose_values_[i]));
222 return values;
223 }
224 std::wstring CommandLine::program() const {
225 DCHECK(argv_.size() > 0);
226 return ASCIIToWide(argv_[0]);
227 }
228 #endif
229
230
231 // static
232 std::wstring CommandLine::PrefixedSwitchString(
233 const std::wstring& switch_string) {
308 return StringPrintf(L"%ls%ls", 234 return StringPrintf(L"%ls%ls",
309 kSwitchPrefixes[0], 235 kSwitchPrefixes[0],
310 switch_string.c_str()); 236 switch_string.c_str());
311 } 237 }
312 238
313 // static 239 // static
314 wstring CommandLine::PrefixedSwitchStringWithValue( 240 std::wstring CommandLine::PrefixedSwitchStringWithValue(
315 const wstring& switch_string, const wstring& value_string) { 241 const std::wstring& switch_string, const std::wstring& value_string) {
316 if (value_string.empty()) { 242 if (value_string.empty()) {
317 return PrefixedSwitchString(switch_string); 243 return PrefixedSwitchString(switch_string);
318 } 244 }
319 245
320 return StringPrintf(L"%ls%ls%ls%ls", 246 return StringPrintf(L"%ls%ls%ls%ls",
321 kSwitchPrefixes[0], 247 kSwitchPrefixes[0],
322 switch_string.c_str(), 248 switch_string.c_str(),
323 kSwitchValueSeparator, 249 kSwitchValueSeparator,
324 value_string.c_str()); 250 value_string.c_str());
325 } 251 }
326 252
327 // static 253 #if defined(OS_WIN)
328 void CommandLine::AppendSwitch(wstring* command_line_string, 254 void CommandLine::AppendSwitch(const std::wstring& switch_string) {
329 const wstring& switch_string) { 255 std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string);
330 DCHECK(command_line_string); 256 command_line_string_.append(L" ");
331 wstring prefixed_switch_string = PrefixedSwitchString(switch_string); 257 command_line_string_.append(prefixed_switch_string);
332 command_line_string->append(L" "); 258 switches_[WideToASCII(switch_string)] = L"";
333 command_line_string->append(prefixed_switch_string);
334 } 259 }
335 260
336 // static 261 void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
337 void CommandLine::AppendSwitchWithValue(wstring* command_line_string, 262 const std::wstring& value_string) {
338 const wstring& switch_string, 263 std::wstring value_string_edit;
339 const wstring& value_string) {
340 wstring value_string_edit;
341 264
342 // NOTE(jhughes): If the value contains a quotation mark at one 265 // NOTE(jhughes): If the value contains a quotation mark at one
343 // end but not both, you may get unusable output. 266 // end but not both, you may get unusable output.
344 if (!value_string.empty() && 267 if (!value_string.empty() &&
345 (value_string.find(L" ") != std::wstring::npos) && 268 (value_string.find(L" ") != std::wstring::npos) &&
346 (value_string[0] != L'"') && 269 (value_string[0] != L'"') &&
347 (value_string[value_string.length() - 1] != L'"')) { 270 (value_string[value_string.length() - 1] != L'"')) {
348 // need to provide quotes 271 // need to provide quotes
349 value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str()); 272 value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str());
350 } else { 273 } else {
351 value_string_edit = value_string; 274 value_string_edit = value_string;
352 } 275 }
353 276
354 wstring combined_switch_string = 277 std::wstring combined_switch_string =
355 PrefixedSwitchStringWithValue(switch_string, value_string_edit); 278 PrefixedSwitchStringWithValue(switch_string, value_string_edit);
356 279
357 command_line_string->append(L" "); 280 command_line_string_.append(L" ");
358 command_line_string->append(combined_switch_string); 281 command_line_string_.append(combined_switch_string);
282
283 switches_[WideToASCII(switch_string)] = value_string;
359 } 284 }
360 285
286 void CommandLine::AppendLooseValue(const std::wstring& value) {
287 // TODO(evan): quoting?
288 command_line_string_.append(L" ");
289 command_line_string_.append(value);
290 }
291
292 void CommandLine::AppendArguments(const CommandLine& other,
293 bool include_program) {
294 // Verify include_program is used correctly.
295 // Logic could be shorter but this is clearer.
296 DCHECK(include_program ? !other.program().empty() : other.program().empty());
297 command_line_string_ += L" " + other.command_line_string_;
298 std::map<std::string, StringType>::const_iterator i;
299 for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
300 switches_[i->first] = i->second;
301 }
302
303 #elif defined(OS_POSIX)
304 void CommandLine::AppendSwitch(const std::wstring& switch_string) {
305 std::string ascii_switch = WideToASCII(switch_string);
306 argv_.push_back(kSwitchPrefixes[0] + ascii_switch);
307 switches_[ascii_switch] = "";
308 }
309
310 void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
311 const std::wstring& value_string) {
312 std::string ascii_switch = WideToASCII(switch_string);
313 std::string ascii_value = WideToASCII(value_string);
314
315 argv_.push_back(kSwitchPrefixes[0] + ascii_switch +
316 kSwitchValueSeparator + ascii_value);
317 switches_[ascii_switch] = ascii_value;
318 }
319
320 void CommandLine::AppendLooseValue(const std::wstring& value) {
321 argv_.push_back(WideToASCII(value));
322 }
323
324 void CommandLine::AppendArguments(const CommandLine& other,
325 bool include_program) {
326 // Verify include_program is used correctly.
327 // Logic could be shorter but this is clearer.
328 DCHECK(include_program ? !other.program().empty() : other.program().empty());
329
330 size_t first_arg = include_program ? 0 : 1;
331 for (size_t i = first_arg; i < other.argv_.size(); ++i)
332 argv_.push_back(other.argv_[i]);
333 std::map<std::string, StringType>::const_iterator i;
334 for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
335 switches_[i->first] = i->second;
336 }
337 #endif
338
OLDNEW
« no previous file with comments | « base/command_line.h ('k') | base/command_line_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698