CmdlineOptionManager.C

Go to the documentation of this file.
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
Generated on Sun May 8 08:04:42 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3