OLD | NEW |
| (Empty) |
1 // Copyright 2008-2009 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 | |
16 #include "omaha/base/command_line_parser.h" | |
17 #include "omaha/base/command_line_parser_internal.h" | |
18 #include <shellapi.h> | |
19 #include "omaha/base/debug.h" | |
20 #include "omaha/base/error.h" | |
21 #include "omaha/base/logging.h" | |
22 #include "omaha/base/path.h" | |
23 | |
24 namespace omaha { | |
25 | |
26 namespace internal { | |
27 | |
28 void CommandLineParserArgs::Reset() { | |
29 switch_arguments_.clear(); | |
30 } | |
31 | |
32 // Assumes switch_name is already lower case. | |
33 HRESULT CommandLineParserArgs::AddSwitch(const CString& switch_name) { | |
34 ASSERT1(CString(switch_name).MakeLower().Compare(switch_name) == 0); | |
35 if (switch_arguments_.find(switch_name) != switch_arguments_.end()) { | |
36 return E_INVALIDARG; | |
37 } | |
38 | |
39 StringVector string_vector; | |
40 switch_arguments_[switch_name] = string_vector; | |
41 return S_OK; | |
42 } | |
43 | |
44 // Assumes switch_name is already lower case. | |
45 HRESULT CommandLineParserArgs::AddSwitchArgument(const CString& switch_name, | |
46 const CString& value) { | |
47 ASSERT1(CString(switch_name).MakeLower().Compare(switch_name) == 0); | |
48 ASSERT1(!switch_name.IsEmpty()); | |
49 if (switch_name.IsEmpty()) { | |
50 // We don't have a switch yet, so this is just a base argument. | |
51 // Example command line: "foo.exe myarg /someswitch" | |
52 // Here, myarg would be a base argument. | |
53 // TODO(omaha): base_args_.push_back(switch_name_str); | |
54 return E_INVALIDARG; | |
55 } | |
56 | |
57 SwitchAndArgumentsMap::iterator iter = switch_arguments_.find(switch_name); | |
58 if (iter == switch_arguments_.end()) { | |
59 return E_UNEXPECTED; | |
60 } | |
61 (*iter).second.push_back(value); | |
62 | |
63 return S_OK; | |
64 } | |
65 | |
66 int CommandLineParserArgs::GetSwitchCount() const { | |
67 return switch_arguments_.size(); | |
68 } | |
69 | |
70 bool CommandLineParserArgs::HasSwitch(const CString& switch_name) const { | |
71 CString switch_name_lower = switch_name; | |
72 switch_name_lower.MakeLower(); | |
73 return switch_arguments_.find(switch_name_lower) != switch_arguments_.end(); | |
74 } | |
75 | |
76 // The value at a particular index may change if switch_names are added | |
77 // since we're using a map underneath. But this keeps us from having to write | |
78 // an interator and expose it externally. | |
79 HRESULT CommandLineParserArgs::GetSwitchNameAtIndex(int index, | |
80 CString* name) const { | |
81 ASSERT1(name); | |
82 | |
83 if (index >= static_cast<int>(switch_arguments_.size())) { | |
84 return E_INVALIDARG; | |
85 } | |
86 | |
87 SwitchAndArgumentsMapIter iter = switch_arguments_.begin(); | |
88 for (int i = 0; i < index; ++i) { | |
89 ++iter; | |
90 } | |
91 | |
92 *name = (*iter).first; | |
93 | |
94 return S_OK; | |
95 } | |
96 | |
97 HRESULT CommandLineParserArgs::GetSwitchArgumentCount( | |
98 const CString& switch_name, | |
99 int* count) const { | |
100 ASSERT1(count); | |
101 | |
102 CString switch_name_lower = switch_name; | |
103 switch_name_lower.MakeLower(); | |
104 | |
105 SwitchAndArgumentsMapIter iter = switch_arguments_.find(switch_name_lower); | |
106 if (iter == switch_arguments_.end()) { | |
107 return E_INVALIDARG; | |
108 } | |
109 | |
110 *count = (*iter).second.size(); | |
111 return S_OK; | |
112 } | |
113 | |
114 HRESULT CommandLineParserArgs::GetSwitchArgumentValue( | |
115 const CString& switch_name, | |
116 int argument_index, | |
117 CString* argument_value) const { | |
118 ASSERT1(argument_value); | |
119 | |
120 CString switch_name_lower = switch_name; | |
121 switch_name_lower.MakeLower(); | |
122 | |
123 int count = 0; | |
124 HRESULT hr = GetSwitchArgumentCount(switch_name_lower, &count); | |
125 if (FAILED(hr)) { | |
126 return hr; | |
127 } | |
128 | |
129 if (argument_index >= count) { | |
130 return E_INVALIDARG; | |
131 } | |
132 | |
133 SwitchAndArgumentsMapIter iter = switch_arguments_.find(switch_name_lower); | |
134 if (iter == switch_arguments_.end()) { | |
135 return E_INVALIDARG; | |
136 } | |
137 | |
138 *argument_value = (*iter).second[argument_index]; | |
139 return S_OK; | |
140 } | |
141 | |
142 } // namespace internal | |
143 | |
144 CommandLineParser::CommandLineParser() { | |
145 required_args_.reset(new internal::CommandLineParserArgs); | |
146 optional_args_.reset(new internal::CommandLineParserArgs); | |
147 } | |
148 | |
149 CommandLineParser::~CommandLineParser() { | |
150 } | |
151 | |
152 HRESULT CommandLineParser::ParseFromString(const wchar_t* command_line) { | |
153 CString command_line_str(command_line); | |
154 command_line_str.Trim(_T(" ")); | |
155 | |
156 if (command_line_str.IsEmpty()) { | |
157 // If the first arg to CommandLineToArgvW "is an empty string the function | |
158 // returns the path to the current executable file." However, it does not | |
159 // correctly handle the case when the path contains spaces - it breaks the | |
160 // path up into separate argv elements. To avoid issues while maintaining | |
161 // the expected behavior, manually get the command line in the empty case. | |
162 // See http://msdn.microsoft.com/en-us/library/bb776391.aspx. | |
163 VERIFY1(::GetModuleFileName(NULL, | |
164 CStrBuf(command_line_str, MAX_PATH), | |
165 MAX_PATH)); | |
166 EnclosePath(&command_line_str); | |
167 } | |
168 | |
169 int argc = 0; | |
170 wchar_t** argv = ::CommandLineToArgvW(command_line_str, &argc); | |
171 if (!argv) { | |
172 return HRESULTFromLastError(); | |
173 } | |
174 | |
175 HRESULT hr = ParseFromArgv(argc, argv); | |
176 ::LocalFree(argv); | |
177 return hr; | |
178 } | |
179 | |
180 // TODO(Omaha): Move the rule parser into a separate class. | |
181 // TODO(Omaha): Fail the regular command parser if [/ switch is passed. | |
182 // ParseFromArgv parses either a rule or a command line. | |
183 // | |
184 // Rules have required and optional parameters. An example of a rule is: | |
185 // "gu.exe /install <extraargs> [/oem [/appargs <appargs> [/silent" | |
186 // This creates a rule for a command line that requires "/install" for the rule | |
187 // to match. The other parameters are optional, indicated by prefixes of "[/". | |
188 // | |
189 // Command lines do not use "[/", and use "/" for all parameters. | |
190 // A command line that looks like this: | |
191 // "gu.exe /install <extraargs> /oem /appargs <appargs>" | |
192 // will match the rule above. | |
193 HRESULT CommandLineParser::ParseFromArgv(int argc, wchar_t** argv) { | |
194 if (argc == 0 || !argv) { | |
195 return E_INVALIDARG; | |
196 } | |
197 | |
198 Reset(); | |
199 | |
200 if (argc == 1) { | |
201 // We only have the program name. So, we're done parsing. | |
202 ASSERT1(!IsSwitch(argv[0])); | |
203 return S_OK; | |
204 } | |
205 | |
206 CString current_switch_name; | |
207 bool is_optional_switch = false; | |
208 | |
209 // Start parsing at the first argument after the program name (index 1). | |
210 for (int i = 1; i < argc; ++i) { | |
211 HRESULT hr = S_OK; | |
212 CString token = argv[i]; | |
213 token.Trim(_T(" ")); | |
214 if (IsSwitch(token)) { | |
215 hr = StripSwitchNameFromArgv(token, ¤t_switch_name); | |
216 if (FAILED(hr)) { | |
217 return hr; | |
218 } | |
219 hr = AddSwitch(current_switch_name); | |
220 if (FAILED(hr)) { | |
221 CORE_LOG(LE, (_T("[AddSwitch failed][%s][0x%x]"), | |
222 current_switch_name, hr)); | |
223 return hr; | |
224 } | |
225 is_optional_switch = false; | |
226 } else if (IsOptionalSwitch(token)) { | |
227 hr = StripOptionalSwitchNameFromArgv(token, ¤t_switch_name); | |
228 if (FAILED(hr)) { | |
229 return hr; | |
230 } | |
231 hr = AddOptionalSwitch(current_switch_name); | |
232 if (FAILED(hr)) { | |
233 CORE_LOG(LE, (_T("[AddOptionalSwitch failed][%s][0x%x]"), | |
234 current_switch_name, hr)); | |
235 return hr; | |
236 } | |
237 is_optional_switch = true; | |
238 } else { | |
239 hr = is_optional_switch ? | |
240 AddOptionalSwitchArgument(current_switch_name, token) : | |
241 AddSwitchArgument(current_switch_name, token); | |
242 | |
243 if (FAILED(hr)) { | |
244 CORE_LOG(LE, (_T("[Adding switch argument failed][%d][%s][%s][0x%x]"), | |
245 is_optional_switch, current_switch_name, token, hr)); | |
246 return hr; | |
247 } | |
248 } | |
249 } | |
250 | |
251 return S_OK; | |
252 } | |
253 | |
254 bool CommandLineParser::IsSwitch(const CString& param) const { | |
255 // Switches must have a prefix (/) or (-), and at least one character. | |
256 if (param.GetLength() < 2) { | |
257 return false; | |
258 } | |
259 | |
260 // All switches must start with / or -, and not contain any spaces. | |
261 // Since the argv parser strips out the enclosing quotes around an argument, | |
262 // we need to handle the following cases properly: | |
263 // * foo.exe /switch arg -- /switch is a switch, arg is an arg | |
264 // * foo.exe /switch "/x y" -- /switch is a switch, '/x y' is an arg and it | |
265 // will get here _without_ the quotes. | |
266 // If param_str starts with / and contains no spaces, then it's a switch. | |
267 return ((param[0] == _T('/')) || (param[0] == _T('-'))) && | |
268 (param.Find(_T(" ")) == -1) && | |
269 (param.Find(_T("%20")) == -1); | |
270 } | |
271 | |
272 bool CommandLineParser::IsOptionalSwitch(const CString& param) const { | |
273 // Optional switches must have a prefix ([/) or ([-), and at least one | |
274 // character. | |
275 return param[0] == _T('[') && IsSwitch(param.Right(param.GetLength() - 1)); | |
276 } | |
277 | |
278 HRESULT CommandLineParser::StripSwitchNameFromArgv(const CString& param, | |
279 CString* switch_name) { | |
280 ASSERT1(switch_name); | |
281 | |
282 if (!IsSwitch(param)) { | |
283 return E_INVALIDARG; | |
284 } | |
285 | |
286 *switch_name = param.Right(param.GetLength() - 1); | |
287 switch_name->Trim(_T(" ")); | |
288 switch_name->MakeLower(); | |
289 return S_OK; | |
290 } | |
291 | |
292 HRESULT CommandLineParser::StripOptionalSwitchNameFromArgv(const CString& param, | |
293 CString* name) { | |
294 ASSERT1(name); | |
295 | |
296 if (!IsOptionalSwitch(param)) { | |
297 return E_INVALIDARG; | |
298 } | |
299 | |
300 return StripSwitchNameFromArgv(param.Right(param.GetLength() - 1), name); | |
301 } | |
302 | |
303 void CommandLineParser::Reset() { | |
304 required_args_->Reset(); | |
305 optional_args_->Reset(); | |
306 } | |
307 | |
308 HRESULT CommandLineParser::AddSwitch(const CString& switch_name) { | |
309 ASSERT1(switch_name == CString(switch_name).MakeLower()); | |
310 return required_args_->AddSwitch(switch_name); | |
311 } | |
312 | |
313 HRESULT CommandLineParser::AddSwitchArgument(const CString& switch_name, | |
314 const CString& argument_value) { | |
315 ASSERT1(switch_name == CString(switch_name).MakeLower()); | |
316 return required_args_->AddSwitchArgument(switch_name, argument_value); | |
317 } | |
318 | |
319 int CommandLineParser::GetSwitchCount() const { | |
320 return required_args_->GetSwitchCount(); | |
321 } | |
322 | |
323 bool CommandLineParser::HasSwitch(const CString& switch_name) const { | |
324 return required_args_->HasSwitch(switch_name); | |
325 } | |
326 | |
327 // The value at a particular index may change if switch_names are added | |
328 // since we're using a map underneath. But this keeps us from having to write | |
329 // an interator and expose it externally. | |
330 HRESULT CommandLineParser::GetSwitchNameAtIndex(int index, | |
331 CString* switch_name) const { | |
332 return required_args_->GetSwitchNameAtIndex(index, switch_name); | |
333 } | |
334 | |
335 HRESULT CommandLineParser::GetSwitchArgumentCount(const CString& switch_name, | |
336 int* count) const { | |
337 return required_args_->GetSwitchArgumentCount(switch_name, count); | |
338 } | |
339 | |
340 HRESULT CommandLineParser::GetSwitchArgumentValue( | |
341 const CString& switch_name, | |
342 int argument_index, | |
343 CString* argument_value) const { | |
344 return required_args_->GetSwitchArgumentValue(switch_name, | |
345 argument_index, | |
346 argument_value); | |
347 } | |
348 | |
349 HRESULT CommandLineParser::AddOptionalSwitch(const CString& switch_name) { | |
350 ASSERT1(switch_name == CString(switch_name).MakeLower()); | |
351 return optional_args_->AddSwitch(switch_name); | |
352 } | |
353 | |
354 HRESULT CommandLineParser::AddOptionalSwitchArgument(const CString& switch_name, | |
355 const CString& value) { | |
356 ASSERT1(switch_name == CString(switch_name).MakeLower()); | |
357 return optional_args_->AddSwitchArgument(switch_name, value); | |
358 } | |
359 | |
360 int CommandLineParser::GetOptionalSwitchCount() const { | |
361 return optional_args_->GetSwitchCount(); | |
362 } | |
363 | |
364 bool CommandLineParser::HasOptionalSwitch(const CString& switch_name) const { | |
365 return optional_args_->HasSwitch(switch_name); | |
366 } | |
367 | |
368 // The value at a particular index may change if switch_names are added | |
369 // since we're using a map underneath. But this keeps us from having to write | |
370 // an interator and expose it externally. | |
371 HRESULT CommandLineParser::GetOptionalSwitchNameAtIndex(int index, | |
372 CString* name) const { | |
373 return optional_args_->GetSwitchNameAtIndex(index, name); | |
374 } | |
375 | |
376 HRESULT CommandLineParser::GetOptionalSwitchArgumentCount(const CString& name, | |
377 int* count) const { | |
378 return optional_args_->GetSwitchArgumentCount(name, count); | |
379 } | |
380 | |
381 HRESULT CommandLineParser::GetOptionalSwitchArgumentValue(const CString& name, | |
382 int argument_index, | |
383 CString* val) const { | |
384 return optional_args_->GetSwitchArgumentValue(name, | |
385 argument_index, | |
386 val); | |
387 } | |
388 | |
389 } // namespace omaha | |
OLD | NEW |