OLD | NEW |
| (Empty) |
1 // Copyright 2005-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 // Parse command-line options | |
17 | |
18 #include "omaha/base/commands.h" | |
19 #include <cstdlib> | |
20 #include "base/scoped_ptr.h" | |
21 #include "omaha/base/cgi.h" | |
22 #include "omaha/base/debug.h" | |
23 #include "omaha/base/error.h" | |
24 #include "omaha/base/file.h" | |
25 #include "omaha/base/logging.h" | |
26 #include "omaha/base/string.h" | |
27 #include "omaha/base/utils.h" | |
28 | |
29 namespace omaha { | |
30 | |
31 #define kNameValueChar _T('=') | |
32 #define kTrueValue _T("true") | |
33 #define kFalseValue _T("false") | |
34 #define kOnValue _T("on") | |
35 #define kOffValue _T("off") | |
36 | |
37 | |
38 // | |
39 // Helper functions | |
40 // | |
41 | |
42 template<class T> | |
43 HRESULT ConvertValue(const TCHAR* str_value, T* value); | |
44 | |
45 // Convert the three-valued value from the string representation | |
46 template<> | |
47 HRESULT ConvertValue<ThreeValue>(const TCHAR* str_value, ThreeValue* value) { | |
48 ASSERT1(value); | |
49 | |
50 *value = VALUE_NOT_SET; | |
51 if (str_value && *str_value) { | |
52 if (String_StrNCmp(str_value, | |
53 kTrueValue, | |
54 TSTR_SIZE(kTrueValue) + 1, | |
55 true) == 0 || | |
56 String_StrNCmp(str_value, | |
57 kOnValue, | |
58 TSTR_SIZE(kOnValue) + 1, | |
59 true) == 0) { | |
60 *value = TRUE_VALUE; | |
61 } else if (String_StrNCmp(str_value, | |
62 kFalseValue, | |
63 TSTR_SIZE(kFalseValue) + 1, | |
64 true) == 0 || | |
65 String_StrNCmp(str_value, | |
66 kOffValue, | |
67 TSTR_SIZE(kOffValue) + 1, | |
68 true) == 0) { | |
69 *value = FALSE_VALUE; | |
70 } else { | |
71 return CI_E_INVALID_ARG; | |
72 } | |
73 } | |
74 return S_OK; | |
75 } | |
76 | |
77 // Convert the int value from the string representation | |
78 template<> | |
79 HRESULT ConvertValue<int>(const TCHAR* str_value, int* value) { | |
80 ASSERT1(str_value && *str_value); | |
81 ASSERT1(value); | |
82 | |
83 if (_set_errno(0)) { | |
84 return E_FAIL; | |
85 } | |
86 | |
87 *value = _tcstol(str_value, NULL, 0); | |
88 if (errno == ERANGE) { | |
89 return CI_E_INVALID_ARG; | |
90 } | |
91 return S_OK; | |
92 } | |
93 | |
94 // Convert the unsigned int value from the string representation | |
95 template<> | |
96 HRESULT ConvertValue<uint32>(const TCHAR* str_value, uint32* value) { | |
97 ASSERT1(str_value && *str_value); | |
98 ASSERT1(value); | |
99 | |
100 if (_set_errno(0)) { | |
101 return E_FAIL; | |
102 } | |
103 | |
104 *value = _tcstoul(str_value, NULL, 0); | |
105 if (errno == ERANGE) { | |
106 return CI_E_INVALID_ARG; | |
107 } | |
108 return S_OK; | |
109 } | |
110 | |
111 // Convert the string value from the string representation | |
112 HRESULT ConvertValue(const TCHAR* str_value, CString* value, bool to_unescape) { | |
113 ASSERT1(str_value && *str_value); | |
114 ASSERT1(value); | |
115 | |
116 *value = str_value; | |
117 | |
118 if (to_unescape) { | |
119 int length = value->GetLength(); | |
120 scoped_array<TCHAR> unescaped_value(new TCHAR[length + 1]); | |
121 RET_IF_FALSE(CGI::UnescapeString(*value, length, unescaped_value.get(), | |
122 length + 1), CI_E_INVALID_ARG); | |
123 *value = unescaped_value.get(); | |
124 } | |
125 | |
126 return S_OK; | |
127 } | |
128 | |
129 // | |
130 // Struct CommandOption | |
131 // | |
132 void CommandOption::Init(const TCHAR* name, CommandOptionType type, | |
133 void* value, int max_value_len) { | |
134 this->name = name; | |
135 this->type = type; | |
136 this->value = value; | |
137 this->max_value_len = max_value_len; | |
138 } | |
139 | |
140 void CommandOption::Copy(const CommandOption& option) { | |
141 Init(option.name, option.type, option.value, option.max_value_len); | |
142 } | |
143 | |
144 // | |
145 // Class CommandParsingSimple | |
146 // | |
147 | |
148 // Constructor | |
149 CommandParsingSimple::CommandParsingSimple() | |
150 : separator_(_T(' ')) { | |
151 } | |
152 | |
153 // Constructor | |
154 CommandParsingSimple::CommandParsingSimple(TCHAR separator) | |
155 : separator_(separator) { | |
156 } | |
157 | |
158 // Parse a command line string into args | |
159 HRESULT CommandParsingSimple::ParseSimple(const TCHAR* cmd_line) { | |
160 ASSERT1(cmd_line); | |
161 | |
162 UTIL_LOG(L3, (_T("[CommandParsingSimple::ParseSimple][%s]"), cmd_line)); | |
163 | |
164 args_.clear(); | |
165 | |
166 // Split command line string into list of arguments | |
167 for (const TCHAR* s = cmd_line; *s; ++s) { | |
168 // Handle separator | |
169 if (*s == separator_) { | |
170 continue; | |
171 } | |
172 | |
173 // Handle single/double quote | |
174 if (*s == _T('"') || *s == _T('\'')) { | |
175 int right_quote = String_FindChar(s + 1, *s); | |
176 if (right_quote == -1) { | |
177 UTIL_LOG(LE, (_T("[CommandParsingSimple::ParseSimple]") | |
178 _T("[single/double quote mismatches]"))); | |
179 return CI_E_INVALID_ARG; | |
180 } | |
181 args_.push_back(CString(s + 1, right_quote)); | |
182 s += right_quote + 1; | |
183 continue; | |
184 } | |
185 | |
186 // Handle all other char | |
187 int next_space = String_FindChar(s + 1, separator_); | |
188 if (next_space == -1) { | |
189 args_.push_back(CString(s)); | |
190 break; | |
191 } else { | |
192 args_.push_back(CString(s, next_space + 1)); | |
193 s += next_space + 1; | |
194 } | |
195 } | |
196 | |
197 return S_OK; | |
198 } | |
199 | |
200 // Get the arg at specified position from the command line | |
201 HRESULT CommandParsingSimple::GetAt(uint32 position, CString* arg) { | |
202 ASSERT1(arg); | |
203 ASSERT1(position < args_.size()); | |
204 | |
205 if (!arg || position >= args_.size()) { | |
206 return E_INVALIDARG; | |
207 } | |
208 | |
209 *arg = args_[position]; | |
210 return S_OK; | |
211 } | |
212 | |
213 // Remove the arg at specified position from the command line | |
214 HRESULT CommandParsingSimple::RemoveAt(uint32 position) { | |
215 ASSERT1(position < args_.size()); | |
216 | |
217 if (position >= args_.size()) { | |
218 return E_INVALIDARG; | |
219 } | |
220 | |
221 uint32 i = 0; | |
222 std::vector<CString>::iterator it(args_.begin()); | |
223 for (; i < position; ++it, ++i) { | |
224 ASSERT1(it != args_.end()); | |
225 } | |
226 args_.erase(it); | |
227 return S_OK; | |
228 } | |
229 | |
230 // Converted to the string | |
231 HRESULT CommandParsingSimple::ToString(CString* cmd_line) { | |
232 ASSERT1(cmd_line); | |
233 | |
234 bool is_first = true; | |
235 cmd_line->Empty(); | |
236 for (std::vector<CString>::const_iterator it(args_.begin()); | |
237 it != args_.end(); | |
238 ++it) { | |
239 if (is_first) { | |
240 is_first = false; | |
241 } else { | |
242 cmd_line->AppendChar(separator_); | |
243 } | |
244 const TCHAR* arg = it->GetString(); | |
245 if (String_FindChar(arg, separator_) != -1) { | |
246 cmd_line->AppendChar(_T('"')); | |
247 cmd_line->Append(arg); | |
248 cmd_line->AppendChar(_T('"')); | |
249 } else { | |
250 cmd_line->Append(arg); | |
251 } | |
252 } | |
253 | |
254 return S_OK; | |
255 } | |
256 | |
257 // Static Helper function that splits a command line | |
258 // string into executable and any arguments | |
259 HRESULT CommandParsingSimple::SplitExeAndArgs(const TCHAR* cmd_line, | |
260 CString* exe, | |
261 CString* args) { | |
262 ASSERT1(cmd_line); | |
263 ASSERT1(exe); | |
264 ASSERT1(args); | |
265 | |
266 // Do the parsing | |
267 CommandParsingSimple cmd_parsing_simple; | |
268 | |
269 RET_IF_FAILED(cmd_parsing_simple.ParseSimple(cmd_line)); | |
270 RET_IF_FAILED(cmd_parsing_simple.GetAt(0, exe)); | |
271 exe->Trim(); | |
272 RET_IF_FAILED(cmd_parsing_simple.RemoveAt(0)); | |
273 return (cmd_parsing_simple.ToString(args)); | |
274 } | |
275 | |
276 HRESULT CommandParsingSimple::SplitExeAndArgsGuess(const TCHAR* cmd_line, | |
277 CString* exe, | |
278 CString* args) { | |
279 ASSERT1(cmd_line); | |
280 ASSERT1(exe); | |
281 ASSERT1(args); | |
282 | |
283 if (File::Exists(cmd_line)) { | |
284 // Optimization for the single executable case. | |
285 // Fill the [out] parameters and return. | |
286 *exe = cmd_line; | |
287 exe->Trim(); | |
288 args->Empty(); | |
289 return S_OK; | |
290 } | |
291 | |
292 CString command_line(cmd_line); | |
293 // Check if the command line is properly enclosed, or that it does not have | |
294 // spaces | |
295 if (command_line.GetAt(0) != _T('"') && command_line.Find(_T(' ')) != -1) { | |
296 // File::Exists() does not handle leading spaces so remove it. | |
297 command_line.Trim(); | |
298 | |
299 // If not, need to find the executable, and if valid, enclose it in | |
300 // double quotes | |
301 const TCHAR* index_dot_exe = stristrW(command_line.GetString(), _T(".EXE")); | |
302 | |
303 if (index_dot_exe != NULL) { | |
304 int dot_exe_end = (index_dot_exe - command_line.GetString()) | |
305 + arraysize(_T(".EXE")) - 1; | |
306 if (File::Exists(CString(command_line, dot_exe_end))) { | |
307 // Enclose the EXE in double quotes | |
308 command_line.Insert(dot_exe_end, _T('"')); | |
309 command_line.Insert(0, _T('"')); | |
310 } else { | |
311 UTIL_LOG(L1, (_T("[CommandParsing::SplitExeAndArgsGuess]") | |
312 _T("[Could not guess the Executable file within [%s]. ") | |
313 _T("Passing on to SplitExeAndArgs as-is."), | |
314 command_line)); | |
315 } | |
316 } | |
317 } | |
318 | |
319 // Do the parsing | |
320 return SplitExeAndArgs(command_line, exe, args); | |
321 } | |
322 | |
323 | |
324 // Static Helper function that returns the number of arguments | |
325 // in the passed in cmd_line | |
326 HRESULT CommandParsingSimple::GetNumberOfArgs(const TCHAR* cmd_line, | |
327 uint32* number_of_args) { | |
328 ASSERT1(cmd_line); | |
329 ASSERT1(number_of_args); | |
330 | |
331 // Do the parsing | |
332 CommandParsingSimple cmd_parsing_simple; | |
333 | |
334 RET_IF_FAILED(cmd_parsing_simple.ParseSimple(cmd_line)); | |
335 *number_of_args = cmd_parsing_simple.args_.size(); | |
336 return S_OK; | |
337 } | |
338 | |
339 | |
340 // | |
341 // Class CommandParsing | |
342 // | |
343 | |
344 // Constructor | |
345 CommandParsing::CommandParsing(CommandOption* options, int options_count) | |
346 : CommandParsingSimple(), | |
347 options_(options), | |
348 options_count_(options_count), | |
349 as_name_value_pair_(false) { | |
350 } | |
351 | |
352 // Constructor | |
353 CommandParsing::CommandParsing(CommandOption* options, int options_count, | |
354 TCHAR separator, bool as_name_value_pair) | |
355 : CommandParsingSimple(separator), | |
356 options_(options), | |
357 options_count_(options_count), | |
358 as_name_value_pair_(as_name_value_pair) { | |
359 } | |
360 | |
361 // Parse a command line string | |
362 HRESULT CommandParsing::Parse(const TCHAR* cmd_line, bool ignore_unknown_args) { | |
363 ASSERT1(cmd_line); | |
364 | |
365 UTIL_LOG(L3, (_T("[CommandParsing::Parse][%s][%d]"), | |
366 cmd_line, ignore_unknown_args)); | |
367 | |
368 // Parse into args_ vector | |
369 RET_IF_FAILED(ParseSimple(cmd_line)); | |
370 | |
371 // Do the internal parsing | |
372 return InternalParse(ignore_unknown_args); | |
373 } | |
374 | |
375 // Parse a list of command line arguments | |
376 HRESULT CommandParsing::ParseArguments(int argc, TCHAR* argv[]) { | |
377 if (argc <= 1) { | |
378 return S_OK; | |
379 } | |
380 | |
381 // Push each argument | |
382 args_.clear(); | |
383 for (int i = 1; i < argc; ++i) { | |
384 args_.push_back(CString(argv[i])); | |
385 } | |
386 | |
387 // Do the internal parsing | |
388 return InternalParse(false); | |
389 } | |
390 | |
391 // Internal parsing | |
392 HRESULT CommandParsing::InternalParse(bool ignore_unknown_args) { | |
393 CString name, value; | |
394 for (std::vector<CString>::const_iterator it(args_.begin()); | |
395 it != args_.end(); | |
396 ++it) { | |
397 RET_IF_FAILED(ExtractName(&name, &it)); | |
398 | |
399 int i = FindOption(name); | |
400 if (i == -1) { | |
401 if (ignore_unknown_args) { | |
402 UTIL_LOG(L3, (_T("[CommandParsing::Parse][unknown arg %s]"), name)); | |
403 continue; | |
404 } else { | |
405 UTIL_LOG(LE, (_T("[CommandParsing::Parse][invalid arg %s]"), name)); | |
406 return CI_E_INVALID_ARG; | |
407 } | |
408 } | |
409 | |
410 if (options_[i].type != COMMAND_OPTION_BOOL) { | |
411 RET_IF_FAILED(ExtractValue(options_[i], &value, &it, args_.end())); | |
412 } | |
413 | |
414 switch (options_[i].type & COMMAND_OPTION_FLAGS_MASK) { | |
415 case COMMAND_OPTION_BOOL: { | |
416 bool bool_value = true; | |
417 SetParsedValue(options_[i], bool_value); | |
418 break; | |
419 } | |
420 | |
421 case COMMAND_OPTION_THREE: { | |
422 ThreeValue three_value = VALUE_NOT_SET; | |
423 RET_IF_FAILED(ConvertValue(value, &three_value)); | |
424 SetParsedValue(options_[i], three_value); | |
425 break; | |
426 } | |
427 | |
428 case COMMAND_OPTION_INT: { | |
429 int int_value = 0; | |
430 RET_IF_FAILED(ConvertValue(value, &int_value)); | |
431 SetParsedValue(options_[i], int_value); | |
432 break; | |
433 } | |
434 | |
435 case COMMAND_OPTION_UINT: { | |
436 int uint_value = 0; | |
437 RET_IF_FAILED(ConvertValue(value, &uint_value)); | |
438 SetParsedValue(options_[i], uint_value); | |
439 break; | |
440 } | |
441 | |
442 case COMMAND_OPTION_STRING: { | |
443 CString str_value; | |
444 bool is_unescape = (options_[i].type & COMMAND_OPTION_UNESCAPE) != 0; | |
445 RET_IF_FAILED(ConvertValue(value, &str_value, is_unescape)); | |
446 SetParsedValue(options_[i], str_value); | |
447 break; | |
448 } | |
449 | |
450 default: | |
451 ASSERT1(false); | |
452 break; | |
453 } | |
454 } | |
455 | |
456 return S_OK; | |
457 } | |
458 | |
459 // Extract the name | |
460 HRESULT CommandParsing::ExtractName(CString* name, | |
461 std::vector<CString>::const_iterator* it) { | |
462 ASSERT1(name); | |
463 ASSERT1(it); | |
464 | |
465 if (as_name_value_pair_) { | |
466 int idx = (*it)->Find(kNameValueChar); | |
467 if (idx == -1) { | |
468 return CI_E_INVALID_ARG; | |
469 } else { | |
470 *name = (*it)->Left(idx); | |
471 } | |
472 } else { | |
473 *name = (*it)->GetString(); | |
474 } | |
475 return S_OK; | |
476 } | |
477 | |
478 // Extract the value | |
479 // Also validate the value length if necessary | |
480 HRESULT CommandParsing::ExtractValue( | |
481 const CommandOption& option, | |
482 CString* value, | |
483 std::vector<CString>::const_iterator* it, | |
484 const std::vector<CString>::const_iterator& end) { | |
485 ASSERT1(value); | |
486 ASSERT1(it); | |
487 | |
488 if (as_name_value_pair_) { | |
489 int idx = (*it)->Find(kNameValueChar); | |
490 if (idx == -1) { | |
491 return CI_E_INVALID_ARG; | |
492 } else { | |
493 *value = (*it)->Right((*it)->GetLength() - idx - 1); | |
494 } | |
495 } else { | |
496 ++(*it); | |
497 if (*it == end) { | |
498 UTIL_LOG(LE, (_T("[CommandParsing::ExtractValue]") | |
499 _T("[argument %s missing value]"), option.name)); | |
500 return CI_E_INVALID_ARG; | |
501 } | |
502 *value = (*it)->GetString(); | |
503 } | |
504 | |
505 if (option.max_value_len >= 0) { | |
506 if (value->GetLength() > option.max_value_len) { | |
507 return CI_E_INVALID_ARG; | |
508 } | |
509 } | |
510 | |
511 return S_OK; | |
512 } | |
513 | |
514 // Set the parsed value | |
515 template<class T> | |
516 void CommandParsing::SetParsedValue(const CommandOption& option, | |
517 const T& value) { | |
518 if (option.type & COMMAND_OPTION_MULTIPLE) { | |
519 ASSERT((option.type & COMMAND_OPTION_FLAGS_MASK) != COMMAND_OPTION_BOOL, | |
520 (_T("COMMAND_OPTION_BOOL can't be used with COMMAND_OPTION_MULTIPLE"))); | |
521 ASSERT((option.type & COMMAND_OPTION_FLAGS_MASK) != COMMAND_OPTION_THREE, | |
522 (_T("COMMAND_OPTION_THREE can't be used with COMMAND_OPTION_MULTIPLE"))); | |
523 | |
524 std::vector<T>* ptr = reinterpret_cast<std::vector<T>*>(option.value); | |
525 ptr->push_back(value); | |
526 } else { | |
527 T* ptr = reinterpret_cast<T*>(option.value); | |
528 *ptr = value; | |
529 } | |
530 } | |
531 | |
532 // Helper function to find an option in the CommandOption list | |
533 int CommandParsing::FindOption(const TCHAR* option_name) { | |
534 ASSERT1(option_name); | |
535 | |
536 for (int i = 0; i < options_count_; ++i) { | |
537 if (String_StrNCmp(option_name, | |
538 options_[i].name, | |
539 options_[i].name.GetLength() + 1, | |
540 false) == 0) { | |
541 return i; | |
542 } | |
543 } | |
544 | |
545 return -1; | |
546 } | |
547 | |
548 // Remove an option from the command line | |
549 HRESULT CommandParsing::Remove(const TCHAR* option_name) { | |
550 ASSERT1(option_name); | |
551 | |
552 for (std::vector<CString>::iterator it(args_.begin()); | |
553 it != args_.end(); | |
554 ++it) { | |
555 if (*it == option_name) { | |
556 int i = FindOption(option_name); | |
557 if (i == -1) { | |
558 return E_FAIL; | |
559 } | |
560 args_.erase(it); | |
561 if (!as_name_value_pair_) { | |
562 if (options_[i].type != COMMAND_OPTION_BOOL) { | |
563 if (it == args_.end()) { | |
564 return E_FAIL; | |
565 } | |
566 args_.erase(it); | |
567 } | |
568 } | |
569 | |
570 return S_OK; | |
571 } | |
572 } | |
573 | |
574 return E_FAIL; | |
575 } | |
576 | |
577 } // namespace omaha | |
578 | |
OLD | NEW |