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