1 //============================================================================== 2 // 3 // This file is part of GPSTk, the GPS Toolkit. 4 // 5 // The GPSTk is free software; you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published 7 // by the Free Software Foundation; either version 3.0 of the License, or 8 // any later version. 9 // 10 // The GPSTk is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with GPSTk; if not, write to the Free Software Foundation, 17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 18 // 19 // This software was developed by Applied Research Laboratories at the 20 // University of Texas at Austin. 21 // Copyright 2004-2020, The Board of Regents of The University of Texas System 22 // 23 //============================================================================== 24 25 //============================================================================== 26 // 27 // This software was developed by Applied Research Laboratories at the 28 // University of Texas at Austin, under contract to an agency or agencies 29 // within the U.S. Department of Defense. The U.S. Government retains all 30 // rights to use, duplicate, distribute, disclose, or release this software. 31 // 32 // Pursuant to DoD Directive 523024 33 // 34 // DISTRIBUTION STATEMENT A: This software has been approved for public 35 // release, distribution is unlimited. 36 // 37 //============================================================================== 38 39 /** 40 * @file CommandOption.cpp 41 * Command line options 42 */ 43 44 #include "CommandOption.hpp" 45 #include "CommandOptionParser.hpp" 46 #include "StringUtils.hpp" 47 48 #include <sstream> 49 50 using namespace std; 51 using namespace gpstk::StringUtils; 52 53 namespace gpstk 54 { 55 CommandOptionVec defaultCommandOptionList; 56 CommandOption(const CommandOption::CommandOptionFlag of,const CommandOption::CommandOptionType ot,const char shOpt,const std::string & loOpt,const std::string & desc,const bool req,CommandOptionVec & optVectorList)57 CommandOption::CommandOption( 58 const CommandOption::CommandOptionFlag of, 59 const CommandOption::CommandOptionType ot, 60 const char shOpt, 61 const std::string& loOpt, 62 const std::string& desc, 63 const bool req, 64 CommandOptionVec& optVectorList) 65 : optFlag(of), optType(ot), 66 shortOpt(shOpt), longOpt(loOpt), description(desc), 67 required(req), count(0), maxCount(0), order(0), parser(NULL) 68 { 69 if (ot == CommandOption::stdType) 70 { 71 if ( (shOpt == 0) && (loOpt.size() == 0) ) 72 { 73 InvalidParameter exc("A short or long command option must be specified"); 74 GPSTK_THROW(exc); 75 } 76 // if short option is specified, allow only printable, non-space characters 77 if ( (shOpt != 0) && !isgraph(shOpt) ) 78 { 79 InvalidParameter exc("Invalid short command option character"); 80 GPSTK_THROW(exc); 81 } 82 // if long option is specified, allow only printable, non-space characters 83 for ( size_t i = longOpt.size(); i > 0; --i ) 84 { 85 if ( !isgraph(longOpt[i - 1]) ) 86 { 87 InvalidParameter exc("Invalid long command option character"); 88 GPSTK_THROW(exc); 89 } 90 } 91 } 92 optVectorList.push_back(this); 93 } 94 95 // Prints out short options with a leading '-' and long ones with '--'. 96 // Puts a '|' between them if it has both. getOptionString() const97 string CommandOption::getOptionString() const 98 { 99 string toReturn; 100 if (shortOpt != 0) 101 { 102 toReturn += string("-") + string(1, shortOpt); 103 if (!longOpt.empty()) 104 toReturn += string(" | --") + longOpt; 105 } 106 else 107 { 108 toReturn += string("--") + longOpt; 109 } 110 return toReturn; 111 } 112 113 // Prints out short options with a leading '-' and long ones with '--'. 114 // Puts a ',' between them if it has both. getFullOptionString() const115 string CommandOption::getFullOptionString() const 116 { 117 string toReturn(" "); 118 if (shortOpt != 0) 119 { 120 toReturn += string("-") + string(1, shortOpt); 121 if (!longOpt.empty()) 122 { 123 toReturn += string(", --") + longOpt; 124 if (optFlag == hasArgument) 125 toReturn += "=" + getArgString(); 126 } 127 else 128 { 129 if (optFlag == hasArgument) 130 toReturn += " " + getArgString(); 131 } 132 } 133 else 134 { 135 toReturn += string(" --") + longOpt; 136 if (optFlag == hasArgument) 137 toReturn += "=" + getArgString(); 138 } 139 return toReturn; 140 } 141 142 // creates the struct option for getopt_long toGetoptLongOption() const143 struct option CommandOption::toGetoptLongOption() const 144 { 145 struct option o = {longOpt.c_str(), optFlag, NULL, 0}; 146 return o; 147 } 148 149 // makes the string for getopt toGetoptShortOption() const150 std::string CommandOption::toGetoptShortOption() const 151 { 152 std::string opt(1, shortOpt); 153 if (optFlag == hasArgument) opt += ":"; 154 return opt; 155 } 156 157 // get the order of the specified instance of this command option getOrder(unsigned long idx) const158 unsigned long CommandOption::getOrder(unsigned long idx) const 159 { 160 if (order.size() == 0) 161 return 0; 162 163 if (idx == (unsigned long)-1) 164 return order[order.size()-1]; 165 166 if (idx >= order.size()) 167 return 0; 168 169 return order[idx]; 170 } 171 172 // writes out the vector of values for this command option dumpValue(std::ostream & out) const173 std::ostream& CommandOption::dumpValue(std::ostream& out) const 174 { 175 std::vector<std::string>::const_iterator itr = value.begin(); 176 while(itr != value.end()) 177 { 178 out << *itr << std::endl; 179 itr++; 180 } 181 return out; 182 } 183 184 // returns a string like this: 185 // 186 // -f | --foo <arg> 187 // this is the description 188 // getDescription() const189 std::string CommandOption::getDescription() const 190 { 191 ostringstream out; 192 // do the option itself first 193 out << '\t'; 194 if (shortOpt != 0) 195 { 196 out << '-' << shortOpt; 197 if (!longOpt.empty()) 198 out << " | "; 199 else 200 out << '\t'; 201 } 202 if (! longOpt.empty()) 203 { 204 out << "--" << longOpt; 205 } 206 if (optFlag == hasArgument) 207 { 208 out << " " << getArgString(); 209 } 210 // and the description goes on a new line 211 out << endl << prettyPrint(description, 212 "\n", 213 " ", 214 " "); 215 if (maxCount != 0) 216 { 217 out << "\t\tUp to " << maxCount << " may be used on the command line." 218 << endl; 219 } 220 return out.str(); 221 } 222 223 // this checks if it expects number or string type arguments. 224 // it returns a string describing the error, if any. checkArguments()225 string CommandOption::checkArguments() 226 { 227 if (required && (count == 0)) 228 return "Required option " + getOptionString() + " was not found."; 229 230 return string(); 231 } 232 checkArguments()233 string CommandOptionRest::checkArguments() 234 { 235 if (required && (count == 0)) 236 return "Required trailing argument was not found."; 237 238 return string(); 239 } 240 checkArguments()241 string CommandOptionWithNumberArg::checkArguments() 242 { 243 string errstr = CommandOption::checkArguments(); 244 245 if (!errstr.empty()) 246 return errstr; 247 248 vector<string>::size_type vecindex; 249 for(vecindex = 0; vecindex < value.size(); vecindex++) 250 { 251 if (!isDigitString(value[vecindex])) 252 { 253 string errstr("Argument for "); 254 errstr += getOptionString(); 255 errstr += string(" should be a digit string."); 256 return errstr; 257 } 258 } 259 260 return string(); 261 } 262 checkArguments()263 string CommandOptionWithDecimalArg::checkArguments() 264 { 265 string errstr = CommandOption::checkArguments(); 266 267 if (!errstr.empty()) 268 return errstr; 269 270 vector<string>::size_type vecindex; 271 for(vecindex = 0; vecindex < value.size(); vecindex++) 272 { 273 if (!isDecimalString(value[vecindex])) 274 { 275 string errstr("Argument for "); 276 errstr += getOptionString(); 277 errstr += string(" should be a decimal string."); 278 return errstr; 279 } 280 } 281 282 return string(); 283 } 284 checkArguments()285 string CommandOptionWithStringArg::checkArguments() 286 { 287 string errstr = CommandOption::checkArguments(); 288 289 if (!errstr.empty()) 290 return errstr; 291 292 vector<string>::size_type vecindex; 293 for(vecindex = 0; vecindex < value.size(); vecindex++) 294 { 295 if (!isAlphaString(value[vecindex])) 296 { 297 string errstr("Argument for "); 298 errstr += getOptionString(); 299 errstr += string(" should be an alphabetic string."); 300 return errstr; 301 } 302 } 303 return errstr; 304 } 305 checkArguments()306 string CommandOptionMutex::checkArguments() 307 { 308 if (doOneOfChecking) 309 { 310 string oo = CommandOptionOneOf::checkArguments(); 311 if (oo != string()) 312 return oo; 313 } 314 315 // mutex doesn't call CommandOption::checkArguments because 316 // it uses "required" differently 317 string errstr("Only one of the following options may be specified: "); 318 int firstSpec = -1; 319 bool touched = false; 320 321 for (size_t i = 0; i < optionVec.size(); i++) 322 { 323 CommandOption *opt = optionVec[i]; 324 325 if (i) 326 errstr += ", "; 327 errstr += opt->getOptionString(); 328 if (opt->getCount()) 329 { 330 if (firstSpec != -1) 331 touched = true; 332 else 333 firstSpec = i; 334 } 335 } 336 337 if (touched) 338 return errstr; 339 340 return string(); 341 } 342 addOption(CommandOption * opt)343 void CommandOptionNOf::addOption(CommandOption* opt) 344 { 345 if (NULL == opt) 346 { 347 InvalidParameter exc("Invalid option address"); 348 GPSTK_THROW(exc); 349 } 350 optionVec.push_back(opt); 351 } 352 checkArguments()353 string CommandOptionNOf::checkArguments() 354 { 355 // n-of doesn't call CommandOption::checkArguments because 356 // it doesn't use "required" 357 string fewerrstr("At least " + StringUtils::asString(N)); 358 359 string manyerrstr("No more than " + StringUtils::asString(maxCount)); 360 string errstr(" of the following options must be specified: "); 361 362 bool found = false; 363 unsigned long n = 0; 364 365 for (CommandOptionVec::size_type i = 0; i < optionVec.size(); i++) 366 { 367 n += optionVec[i]->getCount(); 368 if (i > 0) 369 errstr += ", "; 370 errstr += optionVec[i]->getOptionString(); 371 } 372 373 if (n < N) 374 return fewerrstr + errstr; 375 if (n > maxCount) 376 return manyerrstr + errstr; 377 378 return string(); 379 } 380 which() const381 std::vector<CommandOption*> CommandOptionNOf::which() const 382 { 383 std::vector<CommandOption*> rv; 384 385 for (CommandOptionVec::size_type i = 0; i < optionVec.size(); i++) 386 { 387 if (optionVec[i]->getCount()) 388 { 389 rv.push_back(optionVec[i]); 390 } 391 } 392 393 return rv; 394 } 395 addOption(CommandOption * opt)396 void CommandOptionOneOf::addOption(CommandOption* opt) 397 { 398 if (NULL == opt) 399 { 400 InvalidParameter exc("Invalid option address"); 401 GPSTK_THROW(exc); 402 } 403 optionVec.push_back(opt); 404 } 405 checkArguments()406 string CommandOptionOneOf::checkArguments() 407 { 408 // one-of doesn't call CommandOption::checkArguments because 409 // it doesn't use "required" 410 string errstr("One of the following options must be specified: "); 411 bool found = false; 412 413 for (size_t i = 0; i < optionVec.size(); i++) 414 { 415 if (optionVec[i]->getCount()) 416 found = true; 417 if (i > 0) 418 errstr += ", "; 419 errstr += optionVec[i]->getOptionString(); 420 } 421 422 if (!found) 423 return errstr; 424 425 return string(); 426 } 427 whichOne() const428 CommandOption* CommandOptionOneOf::whichOne() const 429 { 430 CommandOption *rv = NULL; 431 432 for (size_t i = 0; i < optionVec.size(); i++) 433 { 434 if (optionVec[i]->getCount()) 435 { 436 rv = optionVec[i]; 437 break; 438 } 439 } 440 441 return rv; 442 } 443 checkArguments()444 string CommandOptionAllOf::checkArguments() 445 { 446 string errstr("The following options must be used together: "); 447 bool found = false, notFound = false; 448 449 for (size_t i = 0; i < optionVec.size(); i++) 450 { 451 if (optionVec[i]->getCount()) 452 found = true; 453 else 454 notFound = true; 455 if (i > 0) 456 errstr += ", "; 457 errstr += optionVec[i]->getOptionString(); 458 } 459 460 if (found && notFound) 461 return errstr; 462 463 return string(); 464 } 465 getCount() const466 unsigned long CommandOptionAllOf::getCount() const 467 { 468 unsigned long rv = 0; 469 for (size_t i = 0; i < optionVec.size(); i++) 470 { 471 if (optionVec[i]->getCount() == 0) 472 return 0; 473 rv += optionVec[i]->getCount(); 474 } 475 return rv; 476 } 477 CommandOptionDependent(const CommandOption * parent,const CommandOption * child)478 CommandOptionDependent::CommandOptionDependent( 479 const CommandOption* parent, 480 const CommandOption* child) 481 : CommandOption(noArgument, metaType, 0, "", "") 482 { 483 if (NULL == parent) 484 { 485 InvalidParameter exc("Invalid parent address"); 486 GPSTK_THROW(exc); 487 } 488 if (NULL == child) 489 { 490 InvalidParameter exc("Invalid child address"); 491 GPSTK_THROW(exc); 492 } 493 requiree = parent; 494 requirer = child; 495 } 496 checkArguments()497 string CommandOptionDependent::checkArguments() 498 { 499 // dependent doesn't call CommandOption::checkArguments because 500 // it doesn't use "required" 501 string errstr; 502 503 if (!requiree) 504 errstr = "Null requiree (parent) for CommandOptionDependent"; 505 if (!requirer) 506 errstr = "Null requirer (child) for CommandOptionDependent"; 507 508 if (requirer->getCount() && !requiree->getCount()) 509 errstr = "Option " + requirer->getOptionString() + " requires " + 510 requiree->getOptionString(); 511 512 return errstr; 513 } 514 getOptionString() const515 string CommandOptionGroupOr::getOptionString() const 516 { 517 string rv; 518 if (optionVec.size() > 1) 519 rv += "("; 520 for (size_t i = 0; i < optionVec.size(); i++) 521 { 522 if (i) rv += ","; 523 rv += optionVec[i]->getOptionString(); 524 } 525 if (optionVec.size() > 1) 526 rv += ")"; 527 528 return rv; 529 } 530 getCount() const531 unsigned long CommandOptionGroupOr::getCount() const 532 { 533 unsigned long rv = 0; 534 for (size_t i = 0; i < optionVec.size(); i++) 535 rv += optionVec[i]->getCount(); 536 537 return rv; 538 } 539 getCount() const540 unsigned long CommandOptionGroupAnd::getCount() const 541 { 542 unsigned long rv = 0; 543 for (size_t i = 0; i < optionVec.size(); i++) 544 { 545 if (optionVec[i]->getCount() == 0) 546 return 0; 547 rv += optionVec[i]->getCount(); 548 } 549 return rv; 550 } 551 printHelp(std::ostream & out,bool pretty)552 void CommandOptionHelpUsage::printHelp(std::ostream& out, bool pretty) 553 { 554 GPSTK_ASSERT(parser != NULL); 555 if (getCount()) 556 parser->displayUsage(out, pretty); 557 } 558 559 } // namespace gpstk 560