00001 /*!@file Component/CmdlineOptionManager.C OptionManager implementation for command-line parsing */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Component/CmdlineOptionManager.C $ 00035 // $Id: CmdlineOptionManager.C 14376 2011-01-11 02:44:34Z pez $ 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" // for levenshteinDistance() 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> // for TIOCGWINSZ 00062 #include <unistd.h> // for isatty() 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 // types and typedefs 00075 00076 struct OptionInfo; 00077 00078 typedef std::list<OptionedModelParam*> ParamList; 00079 typedef std::map<const ModelOptionDef*, OptionInfo> OptMapType; 00080 00081 // ############################################################## 00082 //! OptionInfo holds mutable information related to a ModelOptionDef 00083 /*! We associate one OptionInfo with each ModelOptionDef that 00084 ModelManager hears about. Each OptionInfo keeps track of the 00085 current value of that option in the current program run, as well 00086 as the list of params who are listening for that option's 00087 value. */ 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; //!< next available value for sortid 00098 00099 int sortid; //!< used to sort OptionInfo's in creation order 00100 string val; //!< current value in string representation 00101 ParamList params; //!< the components that requested this option 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 // let's start with a bunch of spaces: 00113 string ret = string(spc1, ' '); 00114 string::size_type pos = 0; 00115 string::size_type spc = spc1; 00116 00117 // get a chunk of the incoming string: 00118 while(pos < h.length()) 00119 { 00120 // if the rest of h fits in our available space, AND doesn't 00121 // have any hard newlines, then just add it and we're done: 00122 if (h.length() - pos + spc < ncols 00123 && h.find_first_of("\n", pos) == h.npos) 00124 { ret += h.substr(pos); break; } 00125 00126 // here is the max we could fit in the current line of output: 00127 string::size_type pos2 = pos + ncols - spc - 1; 00128 00129 // do we have an LF? 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++; // skip any LF 00146 } 00147 else 00148 { 00149 // cannot cut -- abort this mess 00150 ret += h.substr(pos); break; 00151 } 00152 } 00153 return ret; 00154 } 00155 00156 // ############################################################## 00157 // make a human-readable string out of a short-option name (e.g., 00158 // replacing the null value '\0' with the two literal characters 00159 // {backslash, '0'} 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 // else ... 00169 return sformat("%c", c); 00170 } 00171 00172 // ############################################################## 00173 void assertNoCollision(const ModelOptionDef* opt1, 00174 const ModelOptionDef* opt2) 00175 { 00176 // if they're the exact same option object, then it's not 00177 // considered a collision: 00178 if (opt1 == opt2) return; 00179 00180 // otherwise, make sure that neither the param name, the long 00181 // options name, nor the short option name are the same 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 // if we have an error message, let's start with that: 00252 if (errormsg.length() > 0 && err2.length() > 0) 00253 { 00254 cerr << endl << "ERROR: " << context << errormsg << err2; 00255 00256 // is it a "--something" option that we want to spell-check? 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 // figure out number of columns in our terminal: 00336 unsigned int ncols = 80; // default width 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 // we'll pile up the relevant help messages for each category; so 00346 // here we need a vector (one entry per category) of maps (one entry 00347 // per help message) of strings (each help message) -- note that we 00348 // use a std::map keyed on each OptionInfo's sortid, which in turn 00349 // is determined by the order of construction of the OptionInfo's; 00350 // the bottom line is that OptionInfo's that were registered first 00351 // will be sorted first, and will thus show up first in the help 00352 // listing 00353 CategMap bycateg; 00354 00355 // Go over all possible options and add some help to the correct 00356 // category if we have at least one ModelComponent that has 00357 // requested the option: 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 // does this option have any params? 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 // now loop over all categories and print their options if any: 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 // let's print out the category heading: 00432 out << '\n' << sortedcategs[c].first->description << ":\n\n"; 00433 00434 // now loop over all options in that category (see note above 00435 // explaining how the std::map will contain the options listed 00436 // in their order of registration): 00437 for (std::map<int, string>::iterator 00438 itr = sortedcategs[c].second.begin(), 00439 stop = sortedcategs[c].second.end(); 00440 itr != stop; ++itr) { 00441 // let's format the help message to terminal width: 00442 string h = (*itr).second; 00443 // we have 2 lines, separated by a \n; let's split them: 00444 unsigned int pos = h.find('\n'); 00445 string h1 = h.substr(0, pos), h2 = h.substr(pos + 1); 00446 // let's print this out: 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 // Try to find a pager program (such as /usr/bin/less or 00461 // /usr/bin/more) 00462 00463 // (1) by default, start with the user's PAGER environment 00464 // variable, if it exists: 00465 const char* pager = getenv("PAGER"); 00466 00467 // (2) next, see if we picked up a PAGER_PROG from the 00468 // configure script: 00469 #ifdef PAGER_PROG 00470 if (pager == 0 || access(pager, X_OK) != 0) 00471 { 00472 pager = PAGER_PROG; 00473 } 00474 #endif 00475 00476 // (3) finally, just try /usr/bin/less 00477 if (pager == 0 || access(pager, X_OK) != 0) 00478 pager = "/usr/bin/less"; 00479 00480 // now check if the pager program actually exists and is 00481 // executable 00482 if (pager != 0) 00483 { 00484 if (access(pager, X_OK) != 0) 00485 // ok, we wouldn't be able to execute it, so forget 00486 // about it 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 // ok, the pager failed, so just dump the help to stdout: 00507 printFullHelp(opts, cout); 00508 } 00509 else 00510 { 00511 printFullHelp(opts, cout); 00512 } 00513 } 00514 00515 // ############################################################## 00516 /* Find the OptionInfo for a given ModelOptionDef*. This finds our 00517 OptionInfo object associated with the given ModelOptionDef*; if 00518 there is no such OptionInfo yet, then we create it on the fly 00519 here. */ 00520 OptionInfo& findOptionInfo(OptMapType& opts, 00521 const ModelOptionDef* def) 00522 { 00523 OptMapType::iterator mitr = opts.find(def); 00524 if (mitr == opts.end()) 00525 { 00526 // Make sure the new option doesn't collide with any existing 00527 // options 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; //!< link OptionInfo's to ModelOptionDef's 00553 int exportMask; //!< bit mask for allowed ModelOptionDef::exportFlag 00554 vector<string> extraArgs; //!< non-option command-line args 00555 vector<string> allArgs; //!< all commad-line args including options and non-options 00556 }; 00557 00558 // ############################################################## 00559 CmdlineOptionManager::CmdlineOptionManager() 00560 : 00561 rep(new Impl) 00562 {} 00563 00564 // ############################################################## 00565 CmdlineOptionManager::~CmdlineOptionManager() 00566 { 00567 delete rep; 00568 00569 // ugly const_cast to set rep=0 here, but the ugliness is justified 00570 // the fact that it allows us to ASSERT(rep!=0) in our other 00571 // functions to make sure that people don't try to use us after we 00572 // have already been destroyed 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 // if the option doesn't match our exportMask, then do nothing 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 // find the option based on the name of the model param: 00601 OptionInfo& opt = findOptionInfo(rep->opts, p.getOptionDef()); 00602 00603 // ok, found it. See if we already have this component for this 00604 // option: 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 // Add this component to our client list for that option: 00613 if (gotit == false) opt.params.push_back(&p); 00614 00615 // Do we want to use its val as our new default? Otherwise, reset 00616 // its val to our default: 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 // find the option based on the name of the model param: 00636 OptionInfo& opt = findOptionInfo(rep->opts, p.getOptionDef()); 00637 00638 // ok, found it. See if we already have this component for this 00639 // option, and if so, let's remove it from the list of params for 00640 // the option: 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 // if the option alias doesn't match our exportMask, then do nothing 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 // find the option based on the name (and force the OptionInfo to be 00663 // created if it doesn't yet exist): 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 // find the option by name: 00677 OptionInfo& opt = findOptionInfo(rep->opts, def); 00678 00679 // keep the new value so that we can return it via 00680 // getOptionValString: 00681 opt.val = val; 00682 00683 LDEBUG("setting option %s=%s", def->name, val.c_str()); 00684 00685 // tell the new value to all our params: 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 // find the option by name: 00703 OptionInfo& opt = findOptionInfo(rep->opts, def); 00704 00705 // return the value: 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 /* can't happen */ 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 // FIXME do we even want to treat MODOPT_ALIAS options as "used"? 00778 // The only place isOptionDefUsed() is called is in the qt 00779 // ModelManagerWizard, which maybe doesn't even want to deal with 00780 // aliases? 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 // do a few resets: 00795 rep->extraArgs.clear(); 00796 00797 // do the core of the parsing: 00798 bool ret = this->parseCommandLineCore(argc, argv); 00799 if (ret == false) return false; 00800 00801 // let's finally check that the number of leftover args is within 00802 // what we want: 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 // all went well 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 // loop over all the options and trigger the appropriate parsing. 00836 // There are several special cases in here. First, the 00837 // ModelParamBase objects take no option arguments and are processed 00838 // as special cases. 00839 int nextarg = 1; // position of next argument for short options 00840 00841 // Let's loop over the args and parse each one: 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 // if it's not an option, let's add it as an extra arg: 00853 if (argv[i][0] != '-' || ignoreOptions) { 00854 LDEBUG("argv[%d]=\"%s\" is an extra arg", i, argv[i]); 00855 if (i < nextarg) 00856 continue; // already processed as a short opt arg 00857 else { 00858 rep->extraArgs.push_back(string(argv[i])); 00859 nextarg = i + 1; 00860 continue; 00861 } 00862 } 00863 00864 // is it a single '-'? 00865 if (strlen(argv[i]) < 2 && !ignoreOptions) 00866 { showErrMsg(rep->opts, context, "Unknown option: -"); return false; } 00867 00868 // is it a short option (or a collection of short options)? 00869 if (argv[i][1] != '-' && !ignoreOptions) { 00870 LDEBUG("argv[%d]=\"%s\" is a short option", i, argv[i]); 00871 00872 // loop over all short options specified here: 00873 for (int j = 1; j < int(strlen(argv[i])); j ++) { 00874 // let's look it up: 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 // if not found, let's give an error: 00880 if (mitr == rep->opts.end()) 00881 { showErrMsg(rep->opts, context, "Unknown option: ", argv[i]); return false; } 00882 00883 // if it's an obsolete option, let's print a message and 00884 // bail out: 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 // if it's special option number 0, let's print the help: 00894 if ((*mitr).first == &OPT_ShowHelpMessage) 00895 { printFullHelp(rep->opts); return false; } 00896 00897 // if it's a MODOPT_ALIAS, let's recursively parse its value 00898 // and we'll be done with that option: 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 // did anybody request this option? 00910 if ((*mitr).second.params.empty()) 00911 { showErrMsg(rep->opts, context, "Unimplemented option: ",argv[i]); return false; } 00912 00913 // if it's a MODOPT_FLAG, let's turn it on: 00914 if ((*mitr).first->type.kind == MOK_FLAG) 00915 setOptionValString((*mitr).first, "true"); 00916 else { 00917 // otherwise, we need an argument for the option: 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 // ok, let's set the value: 00927 setOptionValString((*mitr).first, argv[nextarg]); 00928 00929 // ready for the next short option in our current arg 00930 nextarg ++; 00931 } 00932 } 00933 } else { 00934 LDEBUG("argv[%d]=\"%s\" is a long option", i, argv[i]); 00935 // it's a long option 00936 00937 // is it just '--'? 00938 if (strlen(argv[i]) < 3) 00939 { LDEBUG("Ignore options: --"); ignoreOptions=true; } 00940 00941 if (ignoreOptions) continue; 00942 00943 // do we have an '=' symbol in the string? 00944 const char* eq = strchr(argv[i], '='); 00945 if (eq == NULL) { 00946 // is it a MODOPT_FLAG or MODOPT_ALIAS? let's look it up: 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 // if not found, since there is no '=', it could still be 00954 // --noXXX 00955 if (mitr == rep->opts.end()) { 00956 // check that we have 'no' as prefix, plus something else: 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 // let's look up that --noXXX: 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 // if not found, let's give up: 00970 if (mitr == rep->opts.end()) 00971 { showErrMsg(rep->opts, context, "Unknown option: ", argv[i], true); return false; } 00972 00973 // we'll continue, knowing that we want to set the value 00974 // to false: 00975 cval = "false"; 00976 } 00977 00978 // if it's an obsolete option, let's print a message and 00979 // bail out: 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 // if it's special option number 0, let's print the help: 00989 if ((*mitr).first == &OPT_ShowHelpMessage) 00990 { printFullHelp(rep->opts); return false; } 00991 00992 // if it's a MODOPT_ALIAS, let's recursively parse its value 00993 // and we'll be done for that option: 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 // did anybody request this option? 01005 if ((*mitr).second.params.empty()) 01006 { showErrMsg(rep->opts, context, "Unimplemented option: ", argv[i]); return false; } 01007 01008 // if it's a MODOPT_FLAG, let's turn it on/off, otherwise 01009 // trouble: 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 // ok, we have an '='; let's isolate the option name: 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 // ok. let's look it up: 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 // if not found, it's a bogus option name: 01034 if (mitr == rep->opts.end()) 01035 { showErrMsg(rep->opts, context, "Unknown option: ", std::string("--") + oname, true); return false; } 01036 01037 // if it's an obsolete option, let's print a message and 01038 // bail out: 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 // if it's a MODOPT_ALIAS, user is goofing off: 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 // did anybody request this option? 01056 if ((*mitr).second.params.empty()) 01057 { 01058 showErrMsg(rep->opts, context, "Unimplemented option: --", oname); 01059 return false; 01060 } 01061 01062 // is there anything after the '='? 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 // all right, let's set the value: 01073 setOptionValString((*mitr).first, &(eq[1])); 01074 } 01075 } 01076 } 01077 01078 // all went well: 01079 return true; 01080 } 01081 01082 // ############################################################## 01083 bool CmdlineOptionManager::parseCommandLineCore(const char* args, 01084 const std::string& context) 01085 { 01086 // let's just split our single string into an argv-like array and 01087 // call the other version of parseCommandLineCore: 01088 char* a = new char[strlen(args) + 1]; 01089 strcpy(a, args); 01090 01091 const int maxnum = 100; // max number of options in the alias 01092 typedef const char* const_char_ptr; 01093 const char** argv = new const_char_ptr[maxnum]; 01094 argv[0] = NULL; // this usually is the exec name and will be ignored 01095 01096 int argc = 1; // current argv number 01097 char* ptr = a; // pointer to next option 01098 char* end = a + strlen(a); // pointer to end 01099 01100 while(ptr < end) 01101 { 01102 argv[argc++] = ptr; // store address of option 01103 if (argc >= maxnum) 01104 LFATAL("Wooooo, your alias is too long! ('%s')", 01105 args); 01106 while (!isspace(*ptr) && ptr != end) // find next white space 01107 ++ptr; 01108 *ptr++ = '\0'; // blank it out and get ready for next one 01109 } 01110 01111 // ok, let's parse them all! 01112 bool ret = parseCommandLineCore(argc, argv, context); 01113 01114 // free our local memory and return: 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 /* So things look consistent in everyone's emacs... */ 01166 /* Local Variables: */ 01167 /* mode: c++ */ 01168 /* indent-tabs-mode: nil */ 01169 /* End: */ 01170 01171 #endif // COMPONENT_CMDLINEOPTIONMANAGER_C_DEFINED