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 #ifndef COMPONENT_CMDLINEOPTIONMANAGER_C_DEFINED
00039 #define COMPONENT_CMDLINEOPTIONMANAGER_C_DEFINED
00040
00041 #include "Component/CmdlineOptionManager.H"
00042
00043 #include "Component/GlobalOpts.H"
00044 #include "Component/ModelOptionDef.H"
00045 #include "Component/ModelParamBase.H"
00046 #include "Util/Assert.H"
00047 #include "Util/StringConversions.H"
00048 #include "Util/StringUtil.H"
00049 #include "Util/log.H"
00050 #include "Util/sformat.H"
00051 #include "rutz/demangle.h"
00052 #include "rutz/error_context.h"
00053 #include "rutz/pipe.h"
00054 #include "rutz/sfmt.h"
00055
00056 #include <iostream>
00057 #include <list>
00058 #include <map>
00059 #include <algorithm>
00060 #include <string>
00061 #include <sys/ioctl.h>
00062 #include <unistd.h>
00063 #include <vector>
00064
00065 using std::cerr;
00066 using std::cout;
00067 using std::endl;
00068 using std::string;
00069 using std::vector;
00070
00071 namespace dummy_namespace_to_avoid_gcc411_bug_CmdlineOptionManager_C
00072 {
00073
00074
00075
00076 struct OptionInfo;
00077
00078 typedef std::list<OptionedModelParam*> ParamList;
00079 typedef std::map<const ModelOptionDef*, OptionInfo> OptMapType;
00080
00081
00082
00083
00084
00085
00086
00087
00088 struct OptionInfo
00089 {
00090 OptionInfo(const ModelOptionDef* d)
00091 :
00092 sortid(nextid++),
00093 val(d->defval),
00094 params()
00095 {}
00096
00097 static int nextid;
00098
00099 int sortid;
00100 string val;
00101 ParamList params;
00102 };
00103
00104 int OptionInfo::nextid = 0;
00105
00106
00107 string formatHelpString(const string& h,
00108 const string::size_type spc1,
00109 const string::size_type spc2,
00110 const string::size_type ncols)
00111 {
00112
00113 string ret = string(spc1, ' ');
00114 string::size_type pos = 0;
00115 string::size_type spc = spc1;
00116
00117
00118 while(pos < h.length())
00119 {
00120
00121
00122 if (h.length() - pos + spc < ncols
00123 && h.find_first_of("\n", pos) == h.npos)
00124 { ret += h.substr(pos); break; }
00125
00126
00127 string::size_type pos2 = pos + ncols - spc - 1;
00128
00129
00130 string::size_type poslf = h.find('\n', pos);
00131 if (poslf != h.npos && poslf < pos2) pos2 = poslf;
00132
00133 string::size_type pos3 = h.find_last_of(" \t\n", pos2);
00134 if (pos3 != h.npos && pos3 >= pos)
00135 {
00136 if (h[pos3] != '\n')
00137 ret += (h.substr(pos, pos3 - pos + 1)
00138 + "\n"
00139 + string(spc2, ' '));
00140 else
00141 ret += (h.substr(pos, pos3 - pos + 1)
00142 + string(spc2, ' '));
00143
00144 pos = pos3 + 1; spc = spc2;
00145 if (h[pos] == '\n') pos++;
00146 }
00147 else
00148 {
00149
00150 ret += h.substr(pos); break;
00151 }
00152 }
00153 return ret;
00154 }
00155
00156
00157
00158
00159
00160 std::string makeReadableShortopt(const char c)
00161 {
00162 if (c == 0)
00163 return "\\0";
00164
00165 else if (!isprint(c))
00166 return sformat("0x%02x", int(c));
00167
00168
00169 return sformat("%c", c);
00170 }
00171
00172
00173 void assertNoCollision(const ModelOptionDef* opt1,
00174 const ModelOptionDef* opt2)
00175 {
00176
00177
00178 if (opt1 == opt2) return;
00179
00180
00181
00182
00183 if (strcmp(opt1->name, opt2->name) == 0)
00184 LFATAL("ModelOptionDef collision:\n"
00185 "ERROR: two options have the same name \"%s\"\n"
00186 "\twith &opt1 = %p\n"
00187 "\t and &opt2 = %p\n"
00188 "\t and opt1->longoptname = \"%s\"\n"
00189 "\t and opt2->longoptname = \"%s\"\n"
00190 "\t and opt1->shortoptname = '%s'\n"
00191 "\t and opt2->shortoptname = '%s'\n",
00192 opt1->name,
00193 opt1, opt2,
00194 opt1->longoptname, opt2->longoptname,
00195 makeReadableShortopt(opt1->shortoptname).c_str(),
00196 makeReadableShortopt(opt2->shortoptname).c_str());
00197
00198 if (opt1->longoptname != 0
00199 && opt2->longoptname != 0
00200 && opt1->longoptname[0] != '\0'
00201 && strcmp(opt1->longoptname, opt2->longoptname) == 0)
00202 LFATAL("ModelOptionDef collision:\n"
00203 "ERROR: two options have the same longoptname \"%s\"\n"
00204 "\twith &opt1 = %p\n"
00205 "\t and &opt2 = %p\n"
00206 "\t and opt1->name = \"%s\"\n"
00207 "\t and opt2->name = \"%s\"\n"
00208 "\t and opt1->shortoptname = '%s'\n"
00209 "\t and opt2->shortoptname = '%s'\n",
00210 opt1->longoptname,
00211 opt1, opt2,
00212 opt1->name, opt2->name,
00213 makeReadableShortopt(opt1->shortoptname).c_str(),
00214 makeReadableShortopt(opt2->shortoptname).c_str());
00215
00216 if (opt1->shortoptname != '\0'
00217 && opt1->shortoptname == opt2->shortoptname)
00218 LFATAL("ModelOptionDef collision:\n"
00219 "ERROR: two options have the same shortoptname '%s'\n"
00220 "\twith &opt1 = %p\n"
00221 "\t and &opt2 = %p\n"
00222 "\t and opt1->name = \"%s\"\n"
00223 "\t and opt2->name = \"%s\"\n"
00224 "\t and opt1->longoptname = \"%s\"\n"
00225 "\t and opt2->longoptname = \"%s\"\n",
00226 makeReadableShortopt(opt1->shortoptname).c_str(),
00227 opt1, opt2,
00228 opt1->name, opt2->name,
00229 opt1->longoptname, opt2->longoptname);
00230 }
00231
00232
00233 void assertNoOptionCollisions(const OptMapType& opts,
00234 const ModelOptionDef* newopt)
00235 {
00236 for (OptMapType::const_iterator
00237 mitr = opts.begin(),
00238 mstop = opts.end();
00239 mitr != mstop;
00240 ++mitr)
00241 assertNoCollision((*mitr).first, newopt);
00242 }
00243
00244
00245 void showErrMsg(const OptMapType& opts,
00246 const std::string& context,
00247 const std::string& errormsg,
00248 const std::string& err2 = std::string(),
00249 const bool spellcheck = false)
00250 {
00251
00252 if (errormsg.length() > 0 && err2.length() > 0)
00253 {
00254 cerr << endl << "ERROR: " << context << errormsg << err2;
00255
00256
00257 if (spellcheck &&
00258 err2.length() > 2 && err2[0] == '-' && err2[1] == '-')
00259 {
00260 const std::string option = err2.substr(2);
00261
00262 const unsigned int levthresh =
00263 std::min(6u, (unsigned int)(option.length() / 2));
00264
00265 typedef std::multimap<unsigned int, std::string>
00266 match_map_type;
00267
00268 match_map_type matches;
00269
00270 for (OptMapType::const_iterator
00271 itr = opts.begin(), stop = opts.end();
00272 itr != stop; ++itr)
00273 {
00274 const ModelOptionDef* odef = (*itr).first;
00275
00276 const unsigned int dist1 =
00277 damerauLevenshteinDistance(option, odef->longoptname);
00278
00279 if (dist1 <= levthresh)
00280 {
00281 matches.insert(match_map_type::value_type
00282 (dist1, odef->longoptname));
00283 }
00284 else if (odef->type.kind == MOK_FLAG)
00285 {
00286 const std::string othername =
00287 std::string("no") + odef->longoptname;
00288
00289 const unsigned int dist2 =
00290 damerauLevenshteinDistance(option, othername);
00291
00292 if (dist2 <= levthresh)
00293 {
00294 matches.insert(match_map_type::value_type
00295 (dist2, othername));
00296 }
00297 }
00298 }
00299
00300 if (matches.size() > 0)
00301 {
00302 cerr << " (did you mean";
00303 for (match_map_type::const_iterator
00304 itr = matches.begin(), stop = matches.end();
00305 itr != stop; ++itr)
00306 cerr << " --" << (*itr).second << '?';
00307 cerr << ')';
00308 }
00309 }
00310 }
00311 else if (errormsg.length() > 0)
00312 cerr << endl << "ERROR: " << errormsg;
00313
00314 cerr << endl << endl;
00315
00316 cerr << "Try re-running with --help to see a "
00317 << "full list of available options"
00318 << endl;
00319 }
00320
00321
00322 typedef std::map< const ModelOptionCateg*, std::map<int, string> > CategMap;
00323
00324 typedef std::pair<const ModelOptionCateg*, std::map<int, string> > CategoryInfo;
00325
00326 bool cmpcateg(const CategoryInfo& v1,
00327 const CategoryInfo& v2)
00328 {
00329 return v1.first->sortpriority < v2.first->sortpriority;
00330 }
00331
00332
00333 void printFullHelp(const OptMapType& opts, std::ostream& out)
00334 {
00335
00336 unsigned int ncols = 80;
00337 #ifdef TIOCGWINSZ
00338 if (isatty(STDIN_FILENO)) {
00339 struct winsize ws;
00340 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) && ws.ws_col > 0)
00341 ncols = ws.ws_col;
00342 }
00343 #endif
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353 CategMap bycateg;
00354
00355
00356
00357
00358 OptMapType::const_iterator mitr = opts.begin();
00359 for ( ; mitr != opts.end(); ++mitr) {
00360 const ModelOptionCateg* categ = (*mitr).first->categ;
00361 const ModelOptionDef* mo = (*mitr).first;
00362 string help = "";
00363
00364
00365 if ((*mitr).second.params.empty() == false
00366 || mo->type.kind == MOK_ALIAS)
00367 {
00368 switch (mo->type.kind)
00369 {
00370 case MOK_OBSOLETE:
00371 {
00372 if (mo->shortoptname != '\0')
00373 help += string("-") + mo->shortoptname+string(", ");
00374 help += mo->longoptname + string(" [OBSOLETE]");
00375 help += string("\n") + mo->descr;
00376 if (MYLOGVERB >= LOG_DEBUG)
00377 help += string(" [") + mo->name + string("]");
00378 }
00379 break;
00380 case MOK_ALIAS:
00381 if (mo->shortoptname != '\0')
00382 help += string("-") + mo->shortoptname + string(", ");
00383 help += string("--") + mo->longoptname +
00384 string("\n") + mo->descr +
00385 string(". EQUIVALENT TO: ") + mo->defval;
00386 if (MYLOGVERB >= LOG_DEBUG)
00387 help += string(" [") + mo->name + string("]");
00388 break;
00389 case MOK_FLAG:
00390 {
00391 if (mo->shortoptname != '\0')
00392 help += string("-") + mo->shortoptname+string(", ");
00393 help += string("--[no]") + mo->longoptname;
00394 bool on; convertFromString((*mitr).second.val, on);
00395 if (on) help += " [yes]"; else help += " [no]";
00396 help += string("\n") + mo->descr;
00397 if (MYLOGVERB >= LOG_DEBUG)
00398 help += string(" [") + mo->name + string("]");
00399 }
00400 break;
00401 case MOK_ARG:
00402 {
00403 if (mo->shortoptname != '\0')
00404 help += string("-") + mo->shortoptname+string(", ");
00405 ASSERT(mo->type.argtype != 0);
00406 help += string("--") + mo->longoptname + string("=")
00407 + mo->validvals + string(" [") + (*mitr).second.val
00408 + string("] (")
00409 + string(rutz::demangled_name(*(mo->type.argtype)))
00410 + string(")\n") + mo->descr;
00411 if (MYLOGVERB >= LOG_DEBUG)
00412 help += string(" [") + mo->name + string("]");
00413 }
00414 break;
00415 }
00416 bycateg[categ][(*mitr).second.sortid] = help;
00417 }
00418 }
00419
00420
00421 out << "\nCOMMAND-LINE OPTION SYNTAX:\n";
00422 vector<CategoryInfo>
00423 sortedcategs(bycateg.begin(), bycateg.end());
00424
00425 std::sort(sortedcategs.begin(), sortedcategs.end(),
00426 cmpcateg);
00427
00428 for (unsigned int c = 0; c < sortedcategs.size(); c ++)
00429 if (sortedcategs[c].second.empty() == false)
00430 {
00431
00432 out << '\n' << sortedcategs[c].first->description << ":\n\n";
00433
00434
00435
00436
00437 for (std::map<int, string>::iterator
00438 itr = sortedcategs[c].second.begin(),
00439 stop = sortedcategs[c].second.end();
00440 itr != stop; ++itr) {
00441
00442 string h = (*itr).second;
00443
00444 unsigned int pos = h.find('\n');
00445 string h1 = h.substr(0, pos), h2 = h.substr(pos + 1);
00446
00447 out << formatHelpString(h1, 2, 4, ncols) << '\n';
00448 out << formatHelpString(h2, 6, 6, ncols) << "\n\n";
00449 }
00450 }
00451
00452 out << std::flush;
00453 }
00454
00455
00456 void printFullHelp(const OptMapType& opts)
00457 {
00458 if (isatty(STDOUT_FILENO))
00459 {
00460
00461
00462
00463
00464
00465 const char* pager = getenv("PAGER");
00466
00467
00468
00469 #ifdef PAGER_PROG
00470 if (pager == 0 || access(pager, X_OK) != 0)
00471 {
00472 pager = PAGER_PROG;
00473 }
00474 #endif
00475
00476
00477 if (pager == 0 || access(pager, X_OK) != 0)
00478 pager = "/usr/bin/less";
00479
00480
00481
00482 if (pager != 0)
00483 {
00484 if (access(pager, X_OK) != 0)
00485
00486
00487 pager = 0;
00488 }
00489
00490 if (pager != 0)
00491 {
00492 rutz::exec_pipe p("w", pager, (const char*) 0);
00493
00494 printFullHelp(opts, p.stream());
00495
00496 p.close();
00497
00498 const int result = p.exit_status();
00499
00500 if (result != 0)
00501 LERROR("error in child process (%s)", pager);
00502 else
00503 return;
00504 }
00505
00506
00507 printFullHelp(opts, cout);
00508 }
00509 else
00510 {
00511 printFullHelp(opts, cout);
00512 }
00513 }
00514
00515
00516
00517
00518
00519
00520 OptionInfo& findOptionInfo(OptMapType& opts,
00521 const ModelOptionDef* def)
00522 {
00523 OptMapType::iterator mitr = opts.find(def);
00524 if (mitr == opts.end())
00525 {
00526
00527
00528 assertNoOptionCollisions(opts, def);
00529
00530 std::pair<OptMapType::iterator, bool> result =
00531 opts.insert(OptMapType::value_type
00532 (def, OptionInfo(def)));
00533 return (*result.first).second;
00534 }
00535 else
00536 return (*mitr).second;
00537 }
00538 }
00539
00540 using namespace dummy_namespace_to_avoid_gcc411_bug_CmdlineOptionManager_C;
00541
00542
00543 struct CmdlineOptionManager::Impl
00544 {
00545 Impl()
00546 :
00547 opts(),
00548 exportMask(OPTEXP_ALL),
00549 extraArgs()
00550 {}
00551
00552 OptMapType opts;
00553 int exportMask;
00554 vector<string> extraArgs;
00555 vector<string> allArgs;
00556 };
00557
00558
00559 CmdlineOptionManager::CmdlineOptionManager()
00560 :
00561 rep(new Impl)
00562 {}
00563
00564
00565 CmdlineOptionManager::~CmdlineOptionManager()
00566 {
00567 delete rep;
00568
00569
00570
00571
00572
00573 *(const_cast<Impl**>(&rep)) = 0;
00574 }
00575
00576
00577 void CmdlineOptionManager::allowOptions(const int mask)
00578 {
00579 ASSERT(rep != 0);
00580 rep->exportMask = mask;
00581 }
00582
00583
00584 void CmdlineOptionManager::requestOption(OptionedModelParam& p,
00585 const bool useMyVal)
00586 {
00587 ASSERT(rep != 0);
00588
00589
00590 if ((p.getOptionDef()->exportFlag & rep->exportMask) == false)
00591 {
00592 LDEBUG("option %s (--%s) IGNORED (does not match export mask)",
00593 p.getOptionDef()->name, p.getOptionDef()->longoptname);
00594 return;
00595 }
00596
00597 LDEBUG("requesting option %s (--%s)",
00598 p.getOptionDef()->name, p.getOptionDef()->longoptname);
00599
00600
00601 OptionInfo& opt = findOptionInfo(rep->opts, p.getOptionDef());
00602
00603
00604
00605 bool gotit = false;
00606 ParamList::iterator itr = opt.params.begin();
00607 while(itr != opt.params.end()) {
00608 if (*itr == &p) { gotit = true; break; }
00609 itr ++;
00610 }
00611
00612
00613 if (gotit == false) opt.params.push_back(&p);
00614
00615
00616
00617 if (useMyVal) setOptionValString(p.getOptionDef(), p.getValString());
00618 else
00619 {
00620 const bool ok = p.setValString(opt.val);
00621 if (!ok)
00622 LFATAL("rejected setting option %s=%s",
00623 p.getOptionDef()->name, opt.val.c_str());
00624 }
00625 }
00626
00627
00628 void CmdlineOptionManager::unRequestOption(OptionedModelParam& p)
00629 {
00630 ASSERT(rep != 0);
00631
00632 LDEBUG("unrequesting option %s (--%s)",
00633 p.getOptionDef()->name, p.getOptionDef()->longoptname);
00634
00635
00636 OptionInfo& opt = findOptionInfo(rep->opts, p.getOptionDef());
00637
00638
00639
00640
00641 ParamList::iterator itr = opt.params.begin();
00642 while (itr != opt.params.end())
00643 if (&p == *itr) itr = opt.params.erase(itr); else itr ++;
00644 }
00645
00646
00647 void CmdlineOptionManager::requestOptionAlias(const ModelOptionDef* def)
00648 {
00649 ASSERT(rep != 0);
00650
00651
00652 if ((def->exportFlag & rep->exportMask) == 0)
00653 {
00654 LDEBUG("option alias %s (--%s) IGNORED (does not match export mask)",
00655 def->name, def->longoptname);
00656 return;
00657 }
00658
00659 LDEBUG("requesting option alias %s (--%s)",
00660 def->name, def->longoptname);
00661
00662
00663
00664 OptionInfo& opt = findOptionInfo(rep->opts, def);
00665
00666 if (!opt.params.empty())
00667 LFATAL("model params can't be associated with option aliases");
00668 }
00669
00670
00671 void CmdlineOptionManager::setOptionValString(const ModelOptionDef* def,
00672 const string& val)
00673 {
00674 ASSERT(rep != 0);
00675
00676
00677 OptionInfo& opt = findOptionInfo(rep->opts, def);
00678
00679
00680
00681 opt.val = val;
00682
00683 LDEBUG("setting option %s=%s", def->name, val.c_str());
00684
00685
00686 for (ParamList::iterator itr = opt.params.begin();
00687 itr != opt.params.end(); ++itr)
00688 {
00689 ASSERT(*itr != 0);
00690 const bool ok = (*itr)->setValString(val);
00691 if (!ok)
00692 LFATAL("rejected setting option %s=%s",
00693 def->name, val.c_str());
00694 }
00695 }
00696
00697
00698 string CmdlineOptionManager::getOptionValString(const ModelOptionDef* def)
00699 {
00700 ASSERT(rep != 0);
00701
00702
00703 OptionInfo& opt = findOptionInfo(rep->opts, def);
00704
00705
00706 return opt.val;
00707 }
00708
00709
00710 bool CmdlineOptionManager::isOptionRequested(const ModelOptionDef* def) const
00711 {
00712 ASSERT(rep != 0);
00713
00714 return (rep->opts.find(def) != rep->opts.end());
00715 }
00716
00717
00718 uint CmdlineOptionManager::numOptionDefs() const
00719 {
00720 ASSERT(rep != 0);
00721
00722 return rep->opts.size();
00723 }
00724
00725
00726 uint CmdlineOptionManager::getOptionDefs(const ModelOptionDef** arr,
00727 uint narr)
00728 {
00729 ASSERT(rep != 0);
00730
00731 uint nfilled = 0;
00732
00733 OptMapType::const_iterator
00734 mitr = rep->opts.begin(),
00735 stop = rep->opts.end();
00736
00737 for (uint i = 0; i < narr; ++i)
00738 {
00739 if (mitr == stop)
00740 break;
00741
00742 arr[i] = (*mitr).first;
00743
00744 ++mitr;
00745 ++nfilled;
00746 }
00747
00748 return nfilled;
00749 }
00750
00751
00752 const ModelOptionDef* CmdlineOptionManager::findOptionDef(const char* name) const
00753 {
00754 ASSERT(rep != 0);
00755
00756 for (OptMapType::const_iterator mitr = rep->opts.begin();
00757 mitr != rep->opts.end();
00758 ++mitr)
00759 if (!strcmp((*mitr).first->name, name))
00760 return (*mitr).first;
00761
00762 LFATAL("Unknown option named '%s'", name);
00763
00764 return (ModelOptionDef*) 0;
00765 }
00766
00767
00768 bool CmdlineOptionManager::isOptionDefUsed(const ModelOptionDef* def) const
00769 {
00770 ASSERT(rep != 0);
00771
00772 OptMapType::const_iterator mitr = rep->opts.find(def);
00773
00774 if (mitr == rep->opts.end())
00775 return false;
00776
00777
00778
00779
00780
00781 return ((*mitr).second.params.empty() == false
00782 || (*mitr).first->type.kind == MOK_ALIAS);
00783 }
00784
00785
00786 bool CmdlineOptionManager::parseCommandLine(const int argc,
00787 const char** argv,
00788 const char* usage,
00789 const int minarg,
00790 const int maxarg)
00791 {
00792 ASSERT(rep != 0);
00793
00794
00795 rep->extraArgs.clear();
00796
00797
00798 bool ret = this->parseCommandLineCore(argc, argv);
00799 if (ret == false) return false;
00800
00801
00802
00803 if (int(rep->extraArgs.size()) < minarg ||
00804 (maxarg >= 0 && int(rep->extraArgs.size()) > maxarg))
00805 {
00806 if (maxarg == -1)
00807 LERROR("Incorrect number of (non-opt) arg: %"ZU" [%d..Inf]",
00808 rep->extraArgs.size(), minarg);
00809 else
00810 LERROR("Incorrect number of (non-opt) arg: %"ZU" [%d..%d]",
00811 rep->extraArgs.size(), minarg, maxarg);
00812
00813 cerr << "USAGE: " << argv[0] << " " << usage << endl;
00814 cerr << "Try '" << argv[0]
00815 << " --help' for additional information." << endl;
00816 return false;
00817 }
00818
00819
00820 return true;
00821 }
00822
00823
00824 bool CmdlineOptionManager::parseCommandLineCore(const int argc,
00825 const char** argv,
00826 const std::string& context)
00827 {
00828 ASSERT(rep != 0);
00829
00830 GVX_ERR_CONTEXT("parsing the command-line arguments");
00831
00832 rep->allArgs.clear();
00833 rep->allArgs.push_back(argv[0] ? argv[0] : "");
00834
00835
00836
00837
00838
00839 int nextarg = 1;
00840
00841
00842 bool ignoreOptions = false;
00843 for (int i = 1; i < argc; i ++)
00844 {
00845 GVX_ERR_CONTEXT(rutz::sfmt("considering argv[%d]=\"%s\"",
00846 i, argv[i]));
00847
00848 LDEBUG("considering argv[%d]=\"%s\"", i, argv[i]);
00849
00850 rep->allArgs.push_back(argv[i]);
00851
00852
00853 if (argv[i][0] != '-' || ignoreOptions) {
00854 LDEBUG("argv[%d]=\"%s\" is an extra arg", i, argv[i]);
00855 if (i < nextarg)
00856 continue;
00857 else {
00858 rep->extraArgs.push_back(string(argv[i]));
00859 nextarg = i + 1;
00860 continue;
00861 }
00862 }
00863
00864
00865 if (strlen(argv[i]) < 2 && !ignoreOptions)
00866 { showErrMsg(rep->opts, context, "Unknown option: -"); return false; }
00867
00868
00869 if (argv[i][1] != '-' && !ignoreOptions) {
00870 LDEBUG("argv[%d]=\"%s\" is a short option", i, argv[i]);
00871
00872
00873 for (int j = 1; j < int(strlen(argv[i])); j ++) {
00874
00875 OptMapType::iterator mitr = rep->opts.begin();
00876 for ( ; mitr != rep->opts.end(); ++mitr)
00877 if ((*mitr).first->shortoptname == argv[i][j]) break;
00878
00879
00880 if (mitr == rep->opts.end())
00881 { showErrMsg(rep->opts, context, "Unknown option: ", argv[i]); return false; }
00882
00883
00884
00885 if ((*mitr).first->type.kind == MOK_OBSOLETE)
00886 {
00887 showErrMsg(rep->opts, context,
00888 sformat("Obsolete option: -%c:\n\t%s",
00889 argv[i][j], (*mitr).first->descr));
00890 return false;
00891 }
00892
00893
00894 if ((*mitr).first == &OPT_ShowHelpMessage)
00895 { printFullHelp(rep->opts); return false; }
00896
00897
00898
00899 if ((*mitr).first->type.kind == MOK_ALIAS)
00900 {
00901 if (!parseCommandLineCore
00902 ((*mitr).first->defval,
00903 sformat("While parsing option alias %s: %s",
00904 argv[i], context.c_str())))
00905 return false;
00906 continue;
00907 }
00908
00909
00910 if ((*mitr).second.params.empty())
00911 { showErrMsg(rep->opts, context, "Unimplemented option: ",argv[i]); return false; }
00912
00913
00914 if ((*mitr).first->type.kind == MOK_FLAG)
00915 setOptionValString((*mitr).first, "true");
00916 else {
00917
00918 while (nextarg < argc && argv[nextarg][0] == '-')
00919 nextarg ++;
00920 if (nextarg >= argc)
00921 {
00922 showErrMsg(rep->opts, context, "Missing argument for option: ", argv[i]);
00923 return false;
00924 }
00925
00926
00927 setOptionValString((*mitr).first, argv[nextarg]);
00928
00929
00930 nextarg ++;
00931 }
00932 }
00933 } else {
00934 LDEBUG("argv[%d]=\"%s\" is a long option", i, argv[i]);
00935
00936
00937
00938 if (strlen(argv[i]) < 3)
00939 { LDEBUG("Ignore options: --"); ignoreOptions=true; }
00940
00941 if (ignoreOptions) continue;
00942
00943
00944 const char* eq = strchr(argv[i], '=');
00945 if (eq == NULL) {
00946
00947 OptMapType::iterator mitr = rep->opts.begin();
00948 const char* cval = "true";
00949 for ( ; mitr != rep->opts.end(); ++mitr)
00950 if (strcmp((*mitr).first->longoptname,
00951 &(argv[i][2]))==0) break;
00952
00953
00954
00955 if (mitr == rep->opts.end()) {
00956
00957 if (argv[i][2] != 'n'
00958 || argv[i][3] != 'o'
00959 || strlen(argv[i]) == 4)
00960 { showErrMsg(rep->opts, context, "Unknown option: ", argv[i], true); return false; }
00961
00962
00963 for (mitr = rep->opts.begin();
00964 mitr != rep->opts.end();
00965 ++mitr)
00966 if (strcmp((*mitr).first->longoptname,
00967 &(argv[i][4])) == 0) break;
00968
00969
00970 if (mitr == rep->opts.end())
00971 { showErrMsg(rep->opts, context, "Unknown option: ", argv[i], true); return false; }
00972
00973
00974
00975 cval = "false";
00976 }
00977
00978
00979
00980 if ((*mitr).first->type.kind == MOK_OBSOLETE)
00981 {
00982 showErrMsg(rep->opts, context,
00983 sformat("Obsolete option: %s:\n\t%s",
00984 argv[i], (*mitr).first->descr));
00985 return false;
00986 }
00987
00988
00989 if ((*mitr).first == &OPT_ShowHelpMessage)
00990 { printFullHelp(rep->opts); return false; }
00991
00992
00993
00994 if ((*mitr).first->type.kind == MOK_ALIAS)
00995 {
00996 if (!parseCommandLineCore
00997 ((*mitr).first->defval,
00998 sformat("While parsing option alias %s: %s",
00999 argv[i], context.c_str())))
01000 return false;
01001 continue;
01002 }
01003
01004
01005 if ((*mitr).second.params.empty())
01006 { showErrMsg(rep->opts, context, "Unimplemented option: ", argv[i]); return false; }
01007
01008
01009
01010 if ((*mitr).first->type.kind == MOK_FLAG)
01011 setOptionValString((*mitr).first, cval);
01012 else
01013 {
01014 showErrMsg(rep->opts, context, "Missing required argument for option: ",
01015 argv[i]);
01016 return false;
01017 }
01018 } else {
01019
01020 char oname[eq - argv[i] - 1];
01021 strncpy(oname, &(argv[i][2]), eq - argv[i] - 2);
01022 oname[eq - argv[i] - 2] = '\0';
01023
01024 LDEBUG("argv[%d]=\"%s\" has long option name \"%s\"",
01025 i, argv[i], oname);
01026
01027
01028 OptMapType::iterator mitr = rep->opts.begin();
01029 for ( ; mitr != rep->opts.end(); ++mitr)
01030 if (strcmp((*mitr).first->longoptname, oname) == 0)
01031 break;
01032
01033
01034 if (mitr == rep->opts.end())
01035 { showErrMsg(rep->opts, context, "Unknown option: ", std::string("--") + oname, true); return false; }
01036
01037
01038
01039 if ((*mitr).first->type.kind == MOK_OBSOLETE)
01040 {
01041 showErrMsg(rep->opts, context,
01042 sformat("Obsolete option: --%s:\n\t%s",
01043 oname, (*mitr).first->descr));
01044 return false;
01045 }
01046
01047
01048 if ((*mitr).first->type.kind == MOK_ALIAS)
01049 {
01050 showErrMsg(rep->opts, context, "No argument permitted "
01051 "for option alias --", oname);
01052 return false;
01053 }
01054
01055
01056 if ((*mitr).second.params.empty())
01057 {
01058 showErrMsg(rep->opts, context, "Unimplemented option: --", oname);
01059 return false;
01060 }
01061
01062
01063 if (eq - argv[i] == int(strlen(argv[i])) - 1)
01064 {
01065 showErrMsg(rep->opts, context, "Missing value for option: --", oname);
01066 return false;
01067 }
01068
01069 LDEBUG("argv[%d]=\"%s\" has long option value \"%s\"",
01070 i, argv[i], &(eq[1]));
01071
01072
01073 setOptionValString((*mitr).first, &(eq[1]));
01074 }
01075 }
01076 }
01077
01078
01079 return true;
01080 }
01081
01082
01083 bool CmdlineOptionManager::parseCommandLineCore(const char* args,
01084 const std::string& context)
01085 {
01086
01087
01088 char* a = new char[strlen(args) + 1];
01089 strcpy(a, args);
01090
01091 const int maxnum = 100;
01092 typedef const char* const_char_ptr;
01093 const char** argv = new const_char_ptr[maxnum];
01094 argv[0] = NULL;
01095
01096 int argc = 1;
01097 char* ptr = a;
01098 char* end = a + strlen(a);
01099
01100 while(ptr < end)
01101 {
01102 argv[argc++] = ptr;
01103 if (argc >= maxnum)
01104 LFATAL("Wooooo, your alias is too long! ('%s')",
01105 args);
01106 while (!isspace(*ptr) && ptr != end)
01107 ++ptr;
01108 *ptr++ = '\0';
01109 }
01110
01111
01112 bool ret = parseCommandLineCore(argc, argv, context);
01113
01114
01115 delete [] argv; delete [] a;
01116 return ret;
01117 }
01118
01119
01120 uint CmdlineOptionManager::numArgs() const
01121 {
01122 ASSERT(rep != 0);
01123 return rep->allArgs.size();
01124 }
01125
01126
01127 string CmdlineOptionManager::getArg(const uint num) const
01128 {
01129 ASSERT(rep != 0);
01130 if (num >= rep->allArgs.size()) {
01131 LERROR("Invalid arg number %d (0..%"ZU") -- IGNORED",
01132 num, rep->allArgs.size());
01133 return string("");
01134 }
01135 return rep->allArgs[num];
01136 }
01137
01138
01139 uint CmdlineOptionManager::numExtraArgs() const
01140 {
01141 ASSERT(rep != 0);
01142 return rep->extraArgs.size();
01143 }
01144
01145
01146 string CmdlineOptionManager::getExtraArg(const uint num) const
01147 {
01148 ASSERT(rep != 0);
01149 if (num >= rep->extraArgs.size()) {
01150 LERROR("Invalid extra arg number %d (0..%"ZU") -- IGNORED",
01151 num, rep->extraArgs.size());
01152 return string("");
01153 }
01154 return rep->extraArgs[num];
01155 }
01156
01157
01158 void CmdlineOptionManager::clearExtraArgs()
01159 {
01160 ASSERT(rep != 0);
01161 rep->extraArgs.clear();
01162 }
01163
01164
01165
01166
01167
01168
01169
01170
01171 #endif // COMPONENT_CMDLINEOPTIONMANAGER_C_DEFINED