00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <errno.h>
00027
00028 #include <algorithm>
00029
00030
00031
00032
00033
00034 #include "CmdLineParser.h"
00035
00036
00037
00038 using std::string;
00039
00040 static string MISSING_SWITCH = "Missing switch after -";
00041 static string UNKNOWN_SWITCH = "Unknown option switch: ";
00042 static string MISSING_ARG = "Missing argument for switch: ";
00043
00044
00045
00046
00047 struct lt_OptArgDesc
00048 {
00049
00050 bool operator()(const CmdLineParser::OptArgDesc& x1,
00051 const CmdLineParser::OptArgDesc& x2) const
00052 {
00053
00054
00055
00056
00057 if (x1.swLong && x2.swLong) {
00058 return (strcmp(x1.swLong, x2.swLong) < 0);
00059 }
00060 else if (x1.swShort != 0 && x2.swShort != 0) {
00061 return (x1.swShort < x2.swShort);
00062 }
00063 else {
00064 if (x1.swLong && x2.swShort != 0) {
00065 return (x1.swLong[0] < x2.swShort);
00066 }
00067 else {
00068 return (x1.swShort < x2.swLong[0]);
00069 }
00070 }
00071 }
00072
00073 private:
00074
00075 };
00076
00077
00078
00079
00080 static inline bool
00081 IsDashDash(const char* str) { return (strcmp(str, "--") == 0); }
00082
00083
00084
00085
00086
00087 static inline bool
00088 IsLongSwitch(const char* str) { return (strncmp(str, "--", 2) == 0); }
00089
00090 static inline bool
00091 IsShortSwitch(const char* str) { return (*str == '-'); }
00092
00093 static inline bool
00094 IsSwitch(const char* str) { return (IsLongSwitch(str) || IsShortSwitch(str)); }
00095
00096
00097
00098
00099 static inline bool
00100 IsArg(const char* str) { return (!IsSwitch(str) && !IsDashDash(str)); }
00101
00102
00103
00104
00105
00106
00107
00108
00109 CmdLineParser::OptArgDesc CmdLineParser::OptArgDesc_NULL =
00110 CmdLineParser_OptArgDesc_NULL_MACRO;
00111
00112
00113 CmdLineParser::CmdLineParser()
00114 {
00115 Ctor();
00116 }
00117
00118 CmdLineParser::CmdLineParser(const OptArgDesc* optArgDescs,
00119 int argc, const char* const argv[])
00120 {
00121 Ctor();
00122 Parse(optArgDescs, argc, argv);
00123 }
00124
00125 void
00126 CmdLineParser::Ctor()
00127 {
00128
00129 }
00130
00131
00132
00133 CmdLineParser::~CmdLineParser()
00134 {
00135 Reset();
00136 }
00137
00138
00139 void
00140 CmdLineParser::Parse(const OptArgDesc* optArgDescsOrig,
00141 int argc, const char* const argv[])
00142 {
00143 Reset();
00144 command = argv[0];
00145
00146 CheckForErrors(optArgDescsOrig);
00147 const OptArgDesc* optArgDescs = CreateSortedCopy(optArgDescsOrig);
00148
00149 bool endOfOpts = false;
00150
00151 for (int i = 1; i < argc; ++i) {
00152 const char* str = argv[i];
00153
00154
00155
00156
00157 if (str == NULL || *str == '\0') {
00158 continue;
00159 }
00160
00161
00162 if (IsDashDash(str)) {
00163 endOfOpts = true;
00164 continue;
00165 }
00166
00167 if (!endOfOpts && IsSwitch(str)) {
00168
00169
00170
00171
00172
00173
00174
00175 SwDesc swdesc = MakeSwitchDesc(str);
00176 if (swdesc.sw.empty()) {
00177 throw ParseError(MISSING_SWITCH);
00178 }
00179
00180
00181 const OptArgDesc* d = FindOptDesc(optArgDescs, swdesc);
00182 if (!d) {
00183 throw ParseError(UNKNOWN_SWITCH + swdesc.sw);
00184 }
00185
00186
00187 if (d->kind == ARG_NONE) {
00188 if (!swdesc.arg.empty()) {
00189 string msg = "Invalid argument `" + swdesc.arg + "' to switch `"
00190 + swdesc.sw + "'";
00191 throw ParseError(msg);
00192 }
00193 } else if (d->kind == ARG_REQ || d->kind == ARG_OPT) {
00194 if (swdesc.arg.empty()) {
00195 int nexti = i + 1;
00196 if (nexti < argc && argv[nexti] && IsArg(argv[nexti])) {
00197 swdesc.arg = argv[nexti];
00198 i = nexti;
00199 }
00200 }
00201 if (swdesc.arg.empty() && d->kind == ARG_REQ) {
00202 throw ParseError(MISSING_ARG + swdesc.sw);
00203 }
00204 }
00205
00206
00207 AddOption(*d, swdesc);
00208 }
00209 else {
00210
00211
00212
00213 arguments.push_back(string(str));
00214 }
00215 }
00216
00217 delete[] optArgDescs;
00218 }
00219
00220
00221
00222
00223 const string&
00224 CmdLineParser::GetCmd() const
00225 {
00226 return command;
00227 }
00228
00229
00230
00231 bool
00232 CmdLineParser::IsOpt(const char swShort) const
00233 {
00234 string sw(1, swShort);
00235 return IsOpt(sw);
00236 }
00237
00238 bool
00239 CmdLineParser::IsOpt(const char* swLong) const
00240 {
00241 string sw(swLong);
00242 return IsOpt(sw);
00243 }
00244
00245 bool
00246 CmdLineParser::IsOpt(const string& sw) const
00247 {
00248 SwitchToArgMap::const_iterator it = switchToArgMap.find(sw);
00249 return (it != switchToArgMap.end());
00250 }
00251
00252
00253
00254 bool
00255 CmdLineParser::IsOptArg(const char swShort) const
00256 {
00257 string sw(1, swShort);
00258 return IsOptArg(sw);
00259 }
00260
00261 bool
00262 CmdLineParser::IsOptArg(const char* swLong) const
00263 {
00264 string sw(swLong);
00265 return IsOptArg(sw);
00266 }
00267
00268 bool
00269 CmdLineParser::IsOptArg(const string& sw) const
00270 {
00271 SwitchToArgMap::const_iterator it = switchToArgMap.find(sw);
00272 if ((it != switchToArgMap.end()) && ((*it).second != NULL)) {
00273 return true;
00274 }
00275 return false;
00276 }
00277
00278
00279
00280 const string&
00281 CmdLineParser::GetOptArg(const char swShort) const
00282 {
00283 string sw(1, swShort);
00284 return GetOptArg(sw);
00285 }
00286
00287 const string&
00288 CmdLineParser::GetOptArg(const char* swLong) const
00289 {
00290 string sw(swLong);
00291 return GetOptArg(sw);
00292 }
00293
00294 const string&
00295 CmdLineParser::GetOptArg(const string& sw) const
00296 {
00297 SwitchToArgMap::const_iterator it = switchToArgMap.find(sw);
00298 if (it == switchToArgMap.end()) {
00299
00300 }
00301 string* arg = (*it).second;
00302 if (!arg) {
00303
00304 }
00305 return *arg;
00306 }
00307
00308
00309 unsigned int
00310 CmdLineParser::GetNumArgs() const
00311 {
00312 return arguments.size();
00313 }
00314
00315 const string&
00316 CmdLineParser::GetArg(unsigned int i) const
00317 {
00318 return arguments[i];
00319 }
00320
00321
00322
00323
00324 long
00325 CmdLineParser::ToLong(const string& str)
00326 {
00327 long value = 0;
00328 if (str.empty()) { throw InternalError("ToLong"); }
00329
00330 errno = 0;
00331 char* endptr = NULL;
00332 value = strtol(str.c_str(), &endptr, 0);
00333 if (errno || (endptr && strlen(endptr) > 0)) {
00334 string msg = "Argument `" + str
00335 + "' cannot be converted to integral value.";
00336 if (errno) {
00337 msg += " ";
00338 msg += strerror(errno);
00339 }
00340 throw ParseError(msg);
00341 }
00342 return value;
00343 }
00344
00345
00346 uint64_t
00347 CmdLineParser::ToUInt64(const string& str)
00348 {
00349 uint64_t value = 0;
00350 if (str.empty()) { throw InternalError("ToUInt64"); }
00351
00352 errno = 0;
00353 char* endptr = NULL;
00354 value = strtoul(str.c_str(), &endptr, 0);
00355 if (errno || (endptr && strlen(endptr) > 0)) {
00356 string msg = "Argument `" + str
00357 + " cannot be converted to integral value.";
00358 if (errno) {
00359 msg += " ";
00360 msg += strerror(errno);
00361 }
00362 throw ParseError(msg);
00363 }
00364 return value;
00365 }
00366
00367
00368 double
00369 CmdLineParser::ToDbl(const string& str)
00370 {
00371 double value = 0;
00372 if (str.empty()) { throw InternalError("ToDbl"); }
00373
00374 errno = 0;
00375 char* endptr = NULL;
00376 value = strtod(str.c_str(), &endptr);
00377 if (errno || (endptr && strlen(endptr) > 0)) {
00378 string msg = "Argument `" + str + "' cannot be converted to real value.";
00379 if (errno) {
00380 msg += " ";
00381 msg += strerror(errno);
00382 }
00383 throw ParseError(msg);
00384 }
00385 return value;
00386 }
00387
00388
00389
00390
00391 void
00392 CmdLineParser::Dump(std::ostream& os) const
00393 {
00394 os << "Command: `" << GetCmd() << "'" << std::endl;
00395
00396 os << "Switch to Argument map:" << std::endl;
00397 for (SwitchToArgMap::const_iterator it = switchToArgMap.begin();
00398 it != switchToArgMap.end(); ++it) {
00399 const string& sw = (*it).first;
00400 const string* arg = (*it).second;
00401 os << " " << sw << " --> " << ((arg) ? *arg : "<>") << std::endl;
00402 }
00403
00404 os << "Regular arguments:" << std::endl;
00405 for (unsigned int i = 0; i < arguments.size(); ++i) {
00406 os << " " << arguments[i] << std::endl;
00407 }
00408 }
00409
00410
00411 void
00412 CmdLineParser::DDump() const
00413 {
00414 Dump(std::cerr);
00415 }
00416
00417
00418
00419
00420
00421 void
00422 CmdLineParser::Reset()
00423 {
00424 for (SwitchToArgMap::iterator it = switchToArgMap.begin();
00425 it != switchToArgMap.end(); ++it) {
00426 string* arg = (*it).second;
00427 delete arg;
00428 }
00429 switchToArgMap.clear();
00430 arguments.clear();
00431 }
00432
00433
00434
00435
00436 const CmdLineParser::OptArgDesc*
00437 CmdLineParser::CreateSortedCopy(const OptArgDesc* optArgDescs)
00438 {
00439
00440 unsigned int sz = 0;
00441 for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p) { ++sz; }
00442
00443
00444 OptArgDesc* copy = new OptArgDesc[sz+1];
00445 unsigned int i = 0;
00446 for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p, ++i) {
00447 copy[i] = *p;
00448 }
00449 copy[sz] = OptArgDesc_NULL;
00450
00451
00452 if (sz > 1) {
00453 std::sort(©[0], ©[sz-1], lt_OptArgDesc());
00454 }
00455
00456 return copy;
00457 }
00458
00459
00460
00461 void
00462 CmdLineParser::CheckForErrors(const OptArgDesc* optArgDescs)
00463 {
00464
00465
00466
00467
00468
00469 string msg;
00470 string sw;
00471 for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p) {
00472
00473 if (p->swShort == 0 && !p->swLong) {
00474 throw InternalError("Arg descriptor is missing a switch!");
00475 }
00476
00477 if (p->swLong) {
00478 sw = p->swLong;
00479 } else {
00480 sw = p->swShort;
00481 }
00482
00483
00484 if (p->kind == ARG_NULL) {
00485 msg = "OptArgDesc.kind is invalid for: " + sw;
00486 throw InternalError(msg);
00487 }
00488
00489
00490 if (p->dupKind == DUPOPT_NULL) {
00491 msg = "OptArgDesc.dupKind is invalid for: " + sw;
00492 throw InternalError(msg);
00493 }
00494
00495
00496 if (p->dupKind == DUPOPT_CAT && !p->dupArgSep) {
00497 msg = "OptArgDesc.dupArgSep is invalid for: " + sw;
00498 throw InternalError(msg);
00499 }
00500 }
00501 }
00502
00503
00504
00505
00506
00507 CmdLineParser::SwDesc
00508 CmdLineParser::MakeSwitchDesc(const char* str)
00509 {
00510
00511 unsigned int len = strlen(str);
00512 const char* strEnd = str + len;
00513 const char* begSw = NULL, *endSw = NULL;
00514 const char* begArg = NULL, *endArg = NULL;
00515 bool isLong = false;
00516 if (IsLongSwitch(str)) {
00517
00518 begArg = strchr(str, '=');
00519 if (begArg) {
00520 begArg++;
00521 endArg = strEnd - 1;
00522 }
00523 begSw = str + 2;
00524 endSw = (begArg) ? (begArg - 2) : (strEnd - 1);
00525 isLong = true;
00526 }
00527 else if (IsShortSwitch(str)) {
00528
00529 begArg = (len > 2) ? (str + 2) : NULL;
00530 endArg = (begArg) ? (strEnd - 1) : NULL;
00531 begSw = (len > 1) ? (str + 1) : NULL;
00532 endSw = begSw;
00533 }
00534 else {
00535 throw InternalError("Programming Error!");
00536 }
00537
00538
00539 SwDesc swdesc;
00540 swdesc.isLong = isLong;
00541 for (const char* p = begSw; p && p <= endSw; ++p) { swdesc.sw += *p; }
00542 for (const char* p = begArg; p && p <= endArg; ++p) { swdesc.arg += *p; }
00543
00544 return swdesc;
00545 }
00546
00547
00548
00549
00550
00551
00552
00553 const CmdLineParser::OptArgDesc*
00554 CmdLineParser::FindOptDesc(const OptArgDesc* optArgDescs, const SwDesc& swdesc,
00555 bool errOnMultipleMatches)
00556 {
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566 unsigned int swLen = swdesc.sw.length();
00567 const OptArgDesc* odesc = NULL;
00568 for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p) {
00569 if (swdesc.isLong) {
00570 if (p->swLong && strncmp(p->swLong, swdesc.sw.c_str(), swLen) == 0) {
00571 odesc = p;
00572 break;
00573 }
00574 } else {
00575 if (p->swShort != 0 && p->swShort == swdesc.sw[0]) {
00576 odesc = p;
00577 break;
00578 }
00579 }
00580 }
00581 if (!odesc) { return NULL; }
00582
00583
00584 const OptArgDesc* m = NULL;
00585 if (errOnMultipleMatches && (m = FindOptDesc((odesc+1), swdesc, false))) {
00586
00587
00588
00589
00590 bool ok = (swdesc.isLong && (strcmp(odesc->swLong, swdesc.sw.c_str()) == 0)
00591 && (strcmp(odesc->swLong, m->swLong) != 0));
00592 if (!ok) {
00593 string msg = "Switch `";
00594 msg += swdesc.sw; msg += "' matches two different options: ";
00595 if (swdesc.isLong) {
00596 msg += odesc->swLong; msg += ", "; msg += m->swLong;
00597 } else {
00598 msg += odesc->swShort; msg += ", "; msg += m->swShort;
00599 }
00600 throw ParseError(msg);
00601 }
00602 }
00603
00604 return odesc;
00605 }
00606
00607
00608
00609
00610
00611
00612 void
00613 CmdLineParser::AddOption(const OptArgDesc& odesc, const SwDesc& swdesc)
00614 {
00615 if (odesc.swShort != 0) {
00616 string swShort(1, odesc.swShort);
00617 AddOption(odesc, swShort, swdesc.arg);
00618 }
00619 if (odesc.swLong) {
00620 string swLong(odesc.swLong);
00621 AddOption(odesc, swLong, swdesc.arg);
00622 }
00623 }
00624
00625
00626
00627
00628
00629
00630
00631 void
00632 CmdLineParser::AddOption(const OptArgDesc& odesc,
00633 const string& sw, const string& arg)
00634 {
00635 SwitchToArgMap::iterator it = switchToArgMap.find(sw);
00636 if (it == switchToArgMap.end()) {
00637
00638 string* theArg = (arg.empty()) ? NULL : new string(arg);
00639 switchToArgMap.insert(SwitchToArgMap::value_type(sw, theArg));
00640 } else {
00641
00642 string* theArg = (*it).second;
00643
00644 if (odesc.dupKind == DUPOPT_ERR) {
00645 throw ParseError("Duplicate switch: " + sw);
00646 }
00647
00648 if (!arg.empty()) {
00649 if (!theArg) {
00650 theArg = new string(arg);
00651 } else {
00652 if (odesc.dupKind == DUPOPT_CLOB) {
00653 *theArg = arg;
00654 } else if (odesc.dupKind == DUPOPT_CAT) {
00655 *theArg += odesc.dupArgSep + arg;
00656 }
00657 }
00658 }
00659 }
00660 }
00661
00662
00663