00001
00014 #include <algorithm>
00015 #include <sstream>
00016 #include <dlrCommon/exception.h>
00017 #include <dlrUtilities/optionParser.h>
00018 #include <dlrUtilities/stringManipulation.h>
00019
00020 namespace dlr {
00021
00022 namespace utilities {
00023
00024
00025 OptionParser::
00026 OptionParser(bool allowExtraArguments,
00027 bool allowStackedShortOptions,
00028 bool allowOptionishArguments)
00029 : m_allowExtraArguments(allowExtraArguments),
00030 m_allowOptionishArguments(allowOptionishArguments),
00031 m_allowStackedShortOptions(allowStackedShortOptions),
00032 m_errorBehavior(ThrowOnError),
00033 m_exitCode(0),
00034 m_extraArgumentValues(),
00035 m_handleHelp(false),
00036 m_numberOfPosArgRequired(0),
00037 m_numberOfPosArgParsed(0),
00038 m_optionCounts(),
00039 m_optionDescriptions(),
00040 m_optionValues(),
00041 m_positionalArgumentNames(),
00042 m_positionalArgumentDefaultValues(),
00043 m_positionalArgumentDocs(),
00044 m_programName()
00045 {
00046
00047 }
00048
00049
00050
00051 OptionParser::
00052 OptionParser(int exitCode,
00053 bool handleMinusMinusHelp,
00054 bool printUsage,
00055 bool allowExtraArguments,
00056 bool allowStackedShortOptions,
00057 bool allowOptionishArguments)
00058 : m_allowExtraArguments(allowExtraArguments),
00059 m_allowOptionishArguments(allowOptionishArguments),
00060 m_allowStackedShortOptions(allowStackedShortOptions),
00061 m_errorBehavior(printUsage ? UsageAndExitOnError : ExitOnError),
00062 m_exitCode(exitCode),
00063 m_extraArgumentValues(),
00064 m_handleHelp(handleMinusMinusHelp),
00065 m_numberOfPosArgRequired(0),
00066 m_numberOfPosArgParsed(0),
00067 m_optionCounts(),
00068 m_optionDescriptions(),
00069 m_optionValues(),
00070 m_positionalArgumentNames(),
00071 m_positionalArgumentDefaultValues(),
00072 m_positionalArgumentDocs(),
00073 m_programName()
00074 {
00075
00076 }
00077
00078
00079
00080 OptionParser::
00081 ~OptionParser()
00082 {
00083
00084 }
00085
00086
00087 void
00088 OptionParser::
00089 addOption(const std::string& name,
00090 const std::string& shortVersion,
00091 const std::string& longVersion,
00092 const std::string& docString,
00093 bool allowPartialMatch)
00094 {
00095 if(m_optionDescriptions.find(name) != m_optionDescriptions.end()) {
00096 std::ostringstream message;
00097 message << "Option \"" << name << "\" has already been specified.";
00098 DLR_THROW(StateException, "OptionParser::addOption()",
00099 message.str().c_str());
00100 }
00101 OptionDescription newOption(
00102 name, shortVersion, longVersion, false, allowPartialMatch, docString);
00103 m_optionDescriptions[name] = newOption;
00104 m_optionCounts[name] = 0;
00105 }
00106
00107
00108 void
00109 OptionParser::
00110 addOptionWithValue(const std::string& name,
00111 const std::string& shortVersion,
00112 const std::string& longVersion,
00113 const std::string& defaultValue,
00114 const std::string& docString,
00115 bool requireArgument,
00116 bool allowPartialMatch,
00117 bool allowOptionishValue)
00118 {
00119 if(requireArgument == false) {
00120 DLR_THROW(NotImplementedException,
00121 "OptionParser::addOptionWithValue()",
00122 "Options with optional arguments not yet supported.");
00123 }
00124
00125 if(m_optionDescriptions.find(name) != m_optionDescriptions.end()) {
00126 std::ostringstream message;
00127 message << "Option \"" << name << "\" has already been specified.";
00128 DLR_THROW(StateException, "OptionParser::addOptionWithValue()",
00129 message.str().c_str());
00130 }
00131 OptionDescription newOption(
00132 name, shortVersion, longVersion, true, allowPartialMatch, docString,
00133 defaultValue);
00134 m_optionDescriptions[name] = newOption;
00135 m_optionCounts[name] = 0;
00136 }
00137
00138
00139 void
00140 OptionParser::
00141 addPositionalArgument(const std::string& name,
00142 const std::string& docString,
00143 bool isRequired,
00144 const std::string& defaultValue)
00145 {
00146 m_positionalArgumentNames.push_back(name);
00147 m_positionalArgumentDocs.push_back(docString);
00148 m_positionalArgumentDefaultValues.push_back(defaultValue);
00149 if(isRequired) {
00150 m_numberOfPosArgRequired = m_positionalArgumentNames.size();
00151 }
00152 }
00153
00154
00155 size_t
00156 OptionParser::
00157 getCount(const std::string& name) const
00158 {
00159 std::map<std::string, size_t>::const_iterator iter =
00160 m_optionCounts.find(name);
00161 if(iter == m_optionCounts.end()) {
00162 std::ostringstream message;
00163 message << "Invalid option name: " << name << ".";
00164 DLR_THROW(ValueException, "OptionParser::getCount()",
00165 message.str().c_str());
00166 }
00167 return iter->second;
00168 }
00169
00170
00171 std::vector<std::string>
00172 OptionParser::
00173 getExtraPositionalArguments()
00174 {
00175 return m_extraArgumentValues;
00176 }
00177
00178
00179 std::string
00180 OptionParser::
00181 getOptionsDescription()
00182 {
00183 std::ostringstream optionsStream;
00184 if(!m_optionDescriptions.empty()) {
00185 std::map<std::string, OptionDescription>::const_iterator iter =
00186 m_optionDescriptions.begin();
00187 while(iter != m_optionDescriptions.end()) {
00188 optionsStream << iter->second << "\n";
00189 ++iter;
00190 }
00191 }
00192 return optionsStream.str();
00193 }
00194
00195
00196 std::string
00197 OptionParser::
00198 getUsage()
00199 {
00200 std::string indentString = " ";
00201 std::ostringstream usageStream;
00202 usageStream << "Usage:\n"
00203 << " " << m_programName << " ";
00204 if(!m_optionDescriptions.empty()) {
00205 usageStream << "[options] ";
00206 }
00207 size_t argIndex = 0;
00208 while(argIndex < m_numberOfPosArgRequired) {
00209 usageStream << m_positionalArgumentNames[argIndex] << " ";
00210 ++argIndex;
00211 }
00212 while(argIndex < m_positionalArgumentNames.size()) {
00213 usageStream << "[" << m_positionalArgumentNames[argIndex] << "] ";
00214 ++argIndex;
00215 }
00216 if(!m_positionalArgumentNames.empty()) {
00217 usageStream << "\n\nPositional arguments:\n";
00218 for(argIndex = 0; argIndex < m_numberOfPosArgRequired; ++argIndex) {
00219 usageStream
00220 << " " << m_positionalArgumentNames[argIndex] << "\n"
00221 << wrapString(indentString + m_positionalArgumentDocs[argIndex],
00222 indentString)
00223 << "\n";
00224 }
00225 while(argIndex < m_positionalArgumentNames.size()) {
00226 usageStream
00227 << " " << m_positionalArgumentNames[argIndex] << "\n"
00228 << wrapString(indentString + m_positionalArgumentDocs[argIndex],
00229 indentString) << "\n"
00230 << wrapString((indentString + "Default value: \""
00231 + m_positionalArgumentDefaultValues[argIndex]),
00232 indentString) << "\""
00233 << "\n";
00234 ++argIndex;
00235 }
00236 }
00237
00238 if(!m_optionDescriptions.empty()) {
00239 usageStream << "\nOptions:\n"
00240 << this->getOptionsDescription();
00241 }
00242
00243 return usageStream.str();
00244 }
00245
00246
00247 std::string
00248 OptionParser::
00249 getValue(const std::string& name)
00250 {
00251 return this->getValue(name, -1);
00252 }
00253
00254
00255 std::string
00256 OptionParser::
00257 getValue(const std::string& name, int valueIndex)
00258 {
00259
00260 std::map< std::string, std::vector<std::string> >::const_iterator vIter =
00261 m_optionValues.find(name);
00262 if(vIter != m_optionValues.end()) {
00263
00264 if(valueIndex >= static_cast<int>((vIter->second).size())) {
00265 std::ostringstream message;
00266 message << "Option/argument " << name << " was specified "
00267 << (vIter->second).size() << " times, "
00268 << "yet getValue() was called with valueIndex set to "
00269 << valueIndex << ".";
00270 DLR_THROW(IndexException, "OptionParser::getValue()",
00271 message.str().c_str());
00272 }
00273 if(valueIndex < 0
00274 || valueIndex >= static_cast<int>((vIter->second).size())) {
00275 valueIndex = (vIter->second).size() - 1;
00276 }
00277 return (vIter->second)[valueIndex];
00278 }
00279
00280
00281
00282
00283 std::map<std::string, OptionDescription>::const_iterator odIter =
00284 m_optionDescriptions.find(name);
00285 if(odIter != m_optionDescriptions.end()) {
00286 return (odIter->second).getDefaultValue();
00287 }
00288
00289
00290
00291
00292 std::vector<std::string>::const_iterator panIter =
00293 std::find(m_positionalArgumentNames.begin(),
00294 m_positionalArgumentNames.end(),
00295 name);
00296 if(panIter != m_positionalArgumentNames.end()) {
00297 size_t argIndex = panIter - m_positionalArgumentNames.begin();
00298 return m_positionalArgumentDefaultValues[argIndex];
00299 }
00300
00301
00302
00303
00304
00305
00306 std::ostringstream message;
00307 message << "The value of Option/argument " << name << " was requested, "
00308 << "but no option or argument with this name exists.";
00309 DLR_THROW(ValueException, "OptionParser::getValue()",
00310 message.str().c_str());
00311 }
00312
00313
00314 void
00315 OptionParser::
00316 parseCommandLine(int argc, char* argv[])
00317 {
00318
00319 enum ParseState {
00320 DLR_OP_GETTING_NEW_ARG,
00321 DLR_OP_IDENTIFYING_ARG,
00322 DLR_OP_RECORDING_POS_ARG,
00323 DLR_OP_RECORDING_OPTION,
00324 DLR_OP_RECORDING_VALUE,
00325 DLR_OP_FINISHING_SHORT_OPTION,
00326 DLR_OP_FINISHING_LONG_OPTION,
00327 DLR_OP_FINISHED,
00328 DLR_OP_ERROR
00329 };
00330
00331 if(argc < 1) {
00332 DLR_THROW(ValueException, "OptionParser::parseCommandLine()",
00333 "Argument argc must be greater than or equal to 1.");
00334 }
00335
00336
00337 m_optionValues.clear();
00338 std::map<std::string, size_t>::iterator iter = m_optionCounts.begin();
00339 while(iter != m_optionCounts.end()) {
00340 iter->second = 0;
00341 ++iter;
00342 }
00343 m_extraArgumentValues.clear();
00344
00345
00346 m_programName = argv[0];
00347
00348
00349 try {
00350 int argIndex = 1;
00351 std::string currentArgument = "";
00352 OptionDescription optionDescription;
00353 size_t typedLength;
00354 bool isShortMatch;
00355 std::ostringstream errorMessage;
00356
00357 ParseState currentState = DLR_OP_GETTING_NEW_ARG;
00358 while(currentState != DLR_OP_FINISHED) {
00359 switch(currentState) {
00360 case DLR_OP_GETTING_NEW_ARG:
00361 if(argIndex >= argc) {
00362 currentState = DLR_OP_FINISHED;
00363 } else {
00364 currentArgument = argv[argIndex];
00365 ++argIndex;
00366 currentState = DLR_OP_IDENTIFYING_ARG;
00367 }
00368 break;
00369 case DLR_OP_IDENTIFYING_ARG:
00370
00371 if(m_handleHelp && (currentArgument == "--help")) {
00372 std::cout << this->getUsage() << std::endl;
00373 exit(0);
00374 }
00375
00376
00377 {
00378 bool optionFound = this->findOptionDescription(
00379 currentArgument, optionDescription, typedLength, isShortMatch);
00380 if(!optionFound) {
00381 currentState = DLR_OP_RECORDING_POS_ARG;
00382 } else {
00383 currentState = DLR_OP_RECORDING_OPTION;
00384 }
00385 }
00386 break;
00387 case DLR_OP_RECORDING_POS_ARG:
00388
00389
00390 if(!m_allowOptionishArguments && currentArgument[0] == '-') {
00391 errorMessage << "Unrecognized option \"" << currentArgument
00392 << "\".";
00393 currentState = DLR_OP_ERROR;
00394 } else {
00395 this->recordPositionalArgument(currentArgument);
00396 currentState = DLR_OP_GETTING_NEW_ARG;
00397 }
00398 break;
00399 case DLR_OP_RECORDING_OPTION:
00400
00401 ++((this->m_optionCounts.find(optionDescription.getName()))
00402 ->second);
00403
00404
00405 if(optionDescription.requiresValue()) {
00406 currentState = DLR_OP_RECORDING_VALUE;
00407 } else if(isShortMatch) {
00408 currentState = DLR_OP_FINISHING_SHORT_OPTION;
00409 } else {
00410 currentState = DLR_OP_FINISHING_LONG_OPTION;
00411 }
00412 break;
00413 case DLR_OP_RECORDING_VALUE:
00414
00415 if(typedLength < currentArgument.size()) {
00416 this->m_optionValues[optionDescription.getName()].push_back(
00417 currentArgument.substr(typedLength, std::string::npos));
00418 currentState = DLR_OP_GETTING_NEW_ARG;
00419 } else if(argIndex < argc) {
00420 this->m_optionValues[optionDescription.getName()].push_back(
00421 argv[argIndex]);
00422 ++argIndex;
00423 currentState = DLR_OP_GETTING_NEW_ARG;
00424 } else {
00425 errorMessage << "Missing value for option \"" << currentArgument
00426 << "\".";
00427 currentState = DLR_OP_ERROR;
00428 }
00429 break;
00430 case DLR_OP_FINISHING_SHORT_OPTION:
00431 if(typedLength >= currentArgument.size()) {
00432 currentState = DLR_OP_GETTING_NEW_ARG;
00433 } else if(m_allowStackedShortOptions
00434 && typedLength >= 2
00435 && currentArgument[0] == '-') {
00436 currentArgument =
00437 "-" + currentArgument.substr(typedLength, std::string::npos);
00438 currentState = DLR_OP_IDENTIFYING_ARG;
00439 } else {
00440 errorMessage
00441 << "Option \""
00442 << currentArgument.substr(0, typedLength)
00443 << "\" does not take a value, yet was specified as \""
00444 << currentArgument << ".";
00445 currentState = DLR_OP_ERROR;
00446 }
00447 break;
00448 case DLR_OP_FINISHING_LONG_OPTION:
00449 if(typedLength >= currentArgument.size()) {
00450 currentState = DLR_OP_GETTING_NEW_ARG;
00451 } else {
00452 errorMessage
00453 << "Option \""
00454 << currentArgument.substr(0, typedLength)
00455 << "\" does not take a value, yet was specified as \""
00456 << currentArgument << ".";
00457 currentState = DLR_OP_ERROR;
00458 }
00459 break;
00460 case DLR_OP_ERROR:
00461 DLR_THROW(IOException, "OptionParser::parseCommandLine()",
00462 errorMessage.str().c_str());
00463 break;
00464 default:
00465
00466 break;
00467 }
00468 }
00469
00470
00471 if(m_numberOfPosArgParsed < m_numberOfPosArgRequired) {
00472 std::ostringstream message;
00473 message << "Expected " << m_numberOfPosArgRequired
00474 << " positional argument(s), but only found "
00475 << m_numberOfPosArgParsed << ".";
00476 DLR_THROW(IOException, "OptionParser::parseCommandLine()",
00477 message.str().c_str());
00478 }
00479 if(!m_allowExtraArguments && (m_extraArgumentValues.size() != 0)) {
00480 std::ostringstream message;
00481 message << "Expected no more than "
00482 << m_positionalArgumentNames.size()
00483 << " positional arguments, but found "
00484 << m_numberOfPosArgParsed << ".";
00485 DLR_THROW(IOException, "OptionParser::parseCommandLine()",
00486 message.str().c_str());
00487 }
00488
00489 } catch(const IOException& caughtException) {
00490 switch(m_errorBehavior) {
00491 case UsageAndExitOnError:
00492
00493
00494 std::cout << caughtException.what() << "\n\n";
00495 std::cout << this->getUsage() << std::endl;
00496 case ExitOnError:
00497
00498 exit(m_exitCode);
00499 break;
00500 case ThrowOnError:
00501 default:
00502
00503 throw;
00504 break;
00505 }
00506 }
00507 }
00508
00509
00510 bool
00511 OptionParser::
00512 findOptionDescription(const std::string& argument,
00513 OptionDescription& optionDescription,
00514 size_t& typedLength,
00515 bool& isShortMatch)
00516 {
00517
00518
00519
00520 typedLength = 0;
00521 bool foundOption = false;
00522 std::map<std::string, OptionDescription>::const_iterator iter =
00523 m_optionDescriptions.begin();
00524 while(iter != m_optionDescriptions.end()) {
00525 bool localIsShortMatch;
00526 size_t localTypedLength =
00527 (iter->second).getMatchLength(argument, localIsShortMatch);
00528 if(localTypedLength != 0) {
00529 if(foundOption) {
00530 std::ostringstream message;
00531 message << "Argument \"" << argument << "\" is ambiguous.";
00532 DLR_THROW(IOException, "OptionParser::findOptionDescription()",
00533 message.str().c_str());
00534 } else {
00535 typedLength = localTypedLength;
00536 foundOption = true;
00537 optionDescription = iter->second;
00538 isShortMatch = localIsShortMatch;
00539 }
00540 }
00541 ++iter;
00542 }
00543 return foundOption;
00544 }
00545
00546
00547 void
00548 OptionParser::
00549 recordPositionalArgument(const std::string& argument)
00550 {
00551 if(m_numberOfPosArgParsed < m_positionalArgumentNames.size()) {
00552 std::string argumentName =
00553 m_positionalArgumentNames[m_numberOfPosArgParsed];
00554 m_optionValues[argumentName].push_back(argument);
00555 } else {
00556 m_extraArgumentValues.push_back(argument);
00557 }
00558 ++m_numberOfPosArgParsed;
00559 }
00560
00561
00562 }
00563
00564 }