| 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 |