00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "io_common.h"
00044 #include "arg_parser.h"
00045
00046 #include <locale>
00047
00048 namespace lass
00049 {
00050 namespace io
00051 {
00052
00053
00054
00055
00056
00057
00058
00059 ArgParser::ArgParser():
00060 programName_(""),
00061 programVersion_(""),
00062 positionals_(""),
00063 isQuiet_(true)
00064 {
00065 }
00066
00067
00068
00069
00070
00071
00072 ArgParser::ArgParser(const std::string& iProgramName,
00073 const std::string& iProgramVersion,
00074 const std::string& iPositionalArguments):
00075 programName_(iProgramName),
00076 programVersion_(iProgramVersion),
00077 positionals_(iPositionalArguments),
00078 isQuiet_(false)
00079 {
00080 }
00081
00082
00083
00084
00085
00086
00087
00088
00089 bool ArgParser::parse(const TArguments& iArguments, TArguments* oPositionals)
00090 {
00091 bool result = true;
00092 bool allPositionals = false;
00093
00094 for (TArguments::size_type i = 0; i < iArguments.size(); ++i)
00095 {
00096 std::string arg = iArguments[i];
00097 if (arg.length() > 0)
00098 {
00099 if (arg[0] == '-' && !allPositionals)
00100 {
00101 if (arg.length() > 1)
00102 {
00103 if (arg[1] == '-')
00104 {
00105 if (arg.length() > 2)
00106 {
00107
00108 result &= parseLong(iArguments, i);
00109 }
00110 else
00111 {
00112
00113 allPositionals = true;
00114 }
00115 }
00116 else
00117 {
00118
00119 result &= parseShort(iArguments, i);
00120 }
00121 }
00122 else
00123 {
00124
00125 }
00126 }
00127 else
00128 {
00129 if (oPositionals)
00130 {
00131 oPositionals->push_back(arg);
00132 }
00133 }
00134 }
00135 }
00136
00137 return true;
00138 }
00139
00140
00141
00142
00143
00144
00145
00146 bool ArgParser::parse(int iArgc, char* iArgv[], TArguments* oPositionals)
00147 {
00148 TArguments arguments;
00149 std::copy(iArgv + 1, iArgv + iArgc, std::back_inserter(arguments));
00150
00151 return parse(arguments, oPositionals);
00152 }
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162 bool ArgParser::parse(const std::string& iArguments, TArguments* oPositionals)
00163 {
00164 const std::string whitespace = " \t\n";
00165 const std::string quotes = "\"";
00166 const std::string escapes = "\\";
00167
00168
00169
00170 TArguments result;
00171
00172 std::string token;
00173 bool isQuoted = false;
00174 char quote = '#';
00175 bool isEscaped = false;
00176 char escape = '#';
00177
00178 for (std::string::const_iterator ch = iArguments.begin(); ch != iArguments.end(); ++ch)
00179 {
00180 if (whitespace.find(*ch) != std::string::npos && !isQuoted)
00181 {
00182 if (!token.empty())
00183 {
00184 result.push_back(token);
00185 token.clear();
00186 }
00187 }
00188 else if (quotes.find(*ch) != std::string::npos)
00189 {
00190 if (isEscaped)
00191 {
00192 token.append(1, *ch);
00193 isEscaped = false;
00194 }
00195 else
00196 {
00197 if (isQuoted)
00198 {
00199 if (quote == *ch)
00200 {
00201 isQuoted = false;
00202 }
00203 }
00204 else
00205 {
00206 quote = *ch;
00207 isQuoted = true;
00208 }
00209 }
00210 }
00211 else if (escapes.find(*ch) != std::string::npos)
00212 {
00213 if (isEscaped)
00214 {
00215 token.append(1, *ch);
00216 isEscaped = false;
00217 }
00218 else
00219 {
00220 escape = *ch;
00221 isEscaped = true;
00222 }
00223 }
00224 else
00225 {
00226 if (isEscaped)
00227 {
00228 token.append(1, escape);
00229 isEscaped = false;
00230 }
00231 token.append(1, *ch);
00232 }
00233 }
00234
00235 result.push_back(token);
00236 return parse(result, oPositionals);
00237 }
00238
00239
00240
00241 std::string ArgParser::usage() const
00242 {
00243 std::ostringstream result;
00244
00245 result << "usage:";
00246 if (!programName_.empty())
00247 {
00248 result << " " << programName_;
00249 }
00250
00251 result << " [-v|--version] [-h|--help]";
00252
00253 for (TParameters::const_iterator pit = parameters_.begin(); pit != parameters_.end(); ++pit)
00254 {
00255 result << " " << (*pit)->format();
00256 }
00257
00258 if (!positionals_.empty())
00259 {
00260 result << " " << positionals_;
00261 }
00262
00263 return result.str();
00264 }
00265
00266
00267
00268
00269 void ArgParser::subscribe(ArgParameter& iParameter)
00270 {
00271 const std::string& shortName = iParameter.shortName();
00272 const std::string& longName = iParameter.longName();
00273
00274
00275
00276 if (shortName.empty() && longName.empty())
00277 {
00278 LASS_THROW("Subscribing parameter failed because both the short and the long names are "
00279 "empty what makes it impossible to identify the parameter.");
00280 }
00281
00282
00283
00284 if (!shortName.empty())
00285 {
00286 if (shortName.length() != 1 || !std::isalnum(shortName[0], std::locale()))
00287 {
00288 LASS_THROW("Subscribing parameter failed because the short name is not a single "
00289 "alpha-numeric character.");
00290 }
00291 }
00292
00293 if (!isValidLongName(longName))
00294 {
00295 LASS_THROW("Subscribing parameter failed because the long name isn't valid.");
00296 }
00297
00298
00299
00300 for (TParameters::const_iterator pit = parameters_.begin(); pit != parameters_.end(); ++pit)
00301 {
00302 ArgParameter* param = *pit;
00303 if ((!shortName.empty() && param->shortName() == shortName) ||
00304 (!longName.empty() && param->longName() == longName))
00305 {
00306 LASS_THROW("Subscribing parameter failed because there is already an parameter with "
00307 "the same short or long name.");
00308 }
00309 }
00310
00311
00312 parameters_.push_back(&iParameter);
00313 }
00314
00315
00316
00317 bool ArgParser::parseShort(const TArguments& iArguments, TSize& ioIndex)
00318 {
00319 const std::string& arg = iArguments[ioIndex];
00320 LASS_ASSERT(arg.length() > 1);
00321
00322
00323
00324 std::string shortName(arg, 1, 1);
00325 LASS_ASSERT(shortName.length() == 1);
00326 LASS_ASSERT(shortName[0] == arg[1]);
00327 if (writeVersionOrHelp(shortName))
00328 {
00329 return false;
00330 }
00331 for (TParameters::iterator pit = parameters_.begin(); pit != parameters_.end(); ++pit)
00332 {
00333 ArgParameter* param = *pit;
00334 if (param->shortName() == shortName)
00335 {
00336 if (!(param->mode() & amNoValue))
00337 {
00338 std::string value = "";
00339 if (arg.length() > 2)
00340 {
00341 value = arg.substr(2);
00342 }
00343 else
00344 {
00345
00346
00347
00348 if (ioIndex + 1 < iArguments.size())
00349 {
00350 std::string candidate = iArguments[ioIndex + 1];
00351 if (candidate.length() > 0 && candidate[0] != '-')
00352 {
00353 value = candidate;
00354 ++ioIndex;
00355 }
00356 }
00357 }
00358 param->setValue(value);
00359 return true;
00360 }
00361 else
00362 {
00363 param->setValue("");
00364 }
00365 }
00366 }
00367
00368
00369
00370 for (std::string::size_type i = 2; i < arg.length(); ++i)
00371 {
00372 shortName = arg.substr(i, 1);
00373 LASS_ASSERT(shortName.length() == 1);
00374 LASS_ASSERT(shortName[0] == arg[i]);
00375 if (writeVersionOrHelp(shortName))
00376 {
00377 return false;
00378 }
00379 for (TParameters::iterator pit = parameters_.begin(); pit != parameters_.end(); ++pit)
00380 {
00381 ArgParameter* param = *pit;
00382 if (param->shortName() == shortName)
00383 {
00384 if (param->mode() & amNoValue)
00385 {
00386 param->setValue("");
00387 }
00388 else
00389 {
00390 if (!isQuiet_)
00391 {
00392 LASS_COUT << "Bad program arguments: the argument '" << shortName
00393 << "' can take a value. You cannot group it with other arguments like "
00394 << "in '" << arg << "'.\n" << usage() << "\n";
00395 }
00396 return false;
00397 }
00398 }
00399 }
00400 }
00401 return false;
00402 }
00403
00404
00405
00406 bool ArgParser::parseLong(const TArguments& iArguments, TSize iIndex)
00407 {
00408 bool result = true;
00409
00410 const std::string& arg = iArguments[iIndex];
00411 LASS_ASSERT(arg.length() > 2);
00412
00413
00414
00415 std::string longName;
00416 std::string value;
00417 std::string::size_type equalPosition = arg.find('=');
00418 if (equalPosition == std::string::npos)
00419 {
00420 longName = arg.substr(2);
00421 value = "";
00422 }
00423 else
00424 {
00425 longName = arg.substr(2, equalPosition - 2);
00426 value = arg.substr(equalPosition + 1);
00427 }
00428
00429
00430
00431 if (!isValidLongName(longName))
00432 {
00433 if (!isQuiet_)
00434 {
00435 LASS_COUT << "Bad program arguments: the argument '--" << longName << "' is not a "
00436 << "valid long parameter name.\n" << usage() << "\n";
00437 }
00438 return false;
00439 }
00440
00441 if (writeVersionOrHelp(longName))
00442 {
00443 return false;
00444 }
00445
00446 TParameters::iterator match = parameters_.end();
00447
00448
00449
00450 for (TParameters::iterator pit = parameters_.begin(); pit != parameters_.end(); ++pit)
00451 {
00452 ArgParameter* param = *pit;
00453 if (param->longName() == longName)
00454 {
00455 match = pit;
00456 break;
00457 }
00458 }
00459
00460
00461
00462 if (match == parameters_.end())
00463 {
00464 for (std::string::size_type length = longName.length(); length > 0; --length)
00465 {
00466 TParameters::iterator candidate = parameters_.end();
00467 bool isUnique = true;
00468
00469 for (TParameters::iterator pit = parameters_.begin(); pit != parameters_.end(); ++pit)
00470 {
00471 ArgParameter* param = *pit;
00472 if (param->longName().length() >= length)
00473 {
00474 if (param->longName().substr(1, length) == longName.substr(1, length))
00475 {
00476 isUnique = candidate == parameters_.end();
00477 candidate = pit;
00478 }
00479 }
00480 }
00481
00482 if (candidate != parameters_.end() && isUnique == true)
00483 {
00484 match = candidate;
00485 break;
00486 }
00487 }
00488 }
00489
00490
00491
00492 if (match == parameters_.end())
00493 {
00494 if (!isQuiet_)
00495 {
00496 LASS_COUT << "Bad program arguments: the argument '--" << longName << "' is not as a "
00497 << "long parameter name, nor is it a unique abbrevation of one.\n" << usage()
00498 << "\n";
00499 }
00500 return false;
00501 }
00502
00503
00504
00505 (*match)->setValue(value);
00506 return result;
00507 }
00508
00509
00510
00511
00512
00513 bool ArgParser::isValidLongName(const std::string& iLongName) const
00514 {
00515 std::string extra = "-";
00516
00517 for (std::string::size_type i = 0; i < iLongName.length(); ++i)
00518 {
00519 const char ch = iLongName[i];
00520 if (!std::isalnum(ch, std::locale()) && extra.find(ch) == std::string::npos)
00521 {
00522 return false;
00523 }
00524 }
00525 return true;
00526 }
00527
00528
00529
00530 bool ArgParser::writeVersionOrHelp(const std::string& iArgument) const
00531 {
00532 if (iArgument == "v" || iArgument == "version")
00533 {
00534 writeVersion();
00535 return true;
00536 }
00537 if (iArgument == "h" || iArgument == "help")
00538 {
00539 writeHelp();
00540 return true;
00541 }
00542 return false;
00543 }
00544
00545
00546
00547 void ArgParser::writeVersion() const
00548 {
00549 LASS_COUT << programName_ << " version " << programVersion_ << "\n";
00550 }
00551
00552
00553
00554 void ArgParser::writeHelp() const
00555 {
00556 writeVersion();
00557 LASS_COUT << usage() << "\n";
00558 }
00559
00560
00561
00562
00563
00564 ArgParameter::~ArgParameter()
00565 {
00566 }
00567
00568
00569
00570 const std::string& ArgParameter::shortName() const
00571 {
00572 return shortName_;
00573 }
00574
00575
00576
00577 const std::string& ArgParameter::longName() const
00578 {
00579 return longName_;
00580 }
00581
00582
00583
00584 const int ArgParameter::mode() const
00585 {
00586 return mode_;
00587 }
00588
00589
00590
00591 bool ArgParameter::operator!() const
00592 {
00593 return !isSet_;
00594 }
00595
00596
00597
00598 ArgParameter::operator num::SafeBool() const
00599 {
00600 return isSet_ ? num::safeTrue : num::safeFalse;
00601 }
00602
00603
00604
00605 ArgParameter::ArgParameter(ArgParser& iParser,
00606 const std::string& iShortName,
00607 const std::string& iLongName,
00608 int iArgMode):
00609 parser_(iParser),
00610 shortName_(iShortName),
00611 longName_(iLongName),
00612 mode_(iArgMode),
00613 isSet_(false)
00614 {
00615 parser_.subscribe(*this);
00616 }
00617
00618
00619
00620 const std::string ArgParameter::names() const
00621 {
00622 std::ostringstream result;
00623
00624 if (!shortName_.empty())
00625 {
00626 result << "-" << shortName_;
00627 }
00628 if (!longName_.empty())
00629 {
00630 if (!shortName_.empty())
00631 {
00632 result << "|";
00633 }
00634 result << "--" << longName_;
00635 }
00636
00637 return result.str();
00638 }
00639
00640
00641
00642 const bool ArgParameter::parserIsQuiet() const
00643 {
00644 return parser_.isQuiet_;
00645 }
00646
00647
00648
00649 void ArgParameter::set()
00650 {
00651 isSet_ = true;
00652 }
00653
00654
00655
00656 const std::string ArgParameter::format() const
00657 {
00658 return doFormat();
00659 }
00660
00661
00662
00663 const bool ArgParameter::setValue(const std::string& iValue)
00664 {
00665 return doSetValue(iValue);
00666 }
00667
00668
00669
00670 const std::string ArgParameter::doFormat() const
00671 {
00672 std::ostringstream result;
00673 result << "[" << names() << "]";
00674 return result.str();
00675 }
00676
00677
00678
00679 const bool ArgParameter::doSetValue(const std::string& )
00680 {
00681
00682 set();
00683 return true;
00684 }
00685
00686
00687
00688
00689
00690 ArgFlag::ArgFlag(ArgParser& iParser,
00691 const std::string& iShortName,
00692 const std::string& iLongName):
00693 ArgParameter(iParser, iShortName, iLongName, amNoValue)
00694 {
00695 }
00696
00697
00698
00699 ArgFlag::ArgFlag(ArgParser& iParser,
00700 const ArgFormat& iFormat):
00701 ArgParameter(iParser, iFormat.shortName, iFormat.longName, amNoValue)
00702 {
00703 }
00704
00705
00706
00707 }
00708
00709 }
00710
00711