CmdLineParser.cxx

Go to the documentation of this file.
00001 // -*-Mode: C++;-*-
00002 // $Header: /Volumes/cvsrep/developer/OpenADFortTk/src/lib/support/CmdLineParser.cxx,v 1.6 2004/04/28 17:46:19 eraxxon Exp $
00003 
00004 // * BeginCopyright *********************************************************
00005 // *********************************************************** EndCopyright *
00006 
00007 //***************************************************************************
00008 //
00009 // File:
00010 //   $Source: /Volumes/cvsrep/developer/OpenADFortTk/src/lib/support/CmdLineParser.cxx,v $
00011 //
00012 //   Nathan Tallent
00013 //
00014 // Purpose:
00015 //    [The purpose of this file]
00016 //
00017 // Description:
00018 //    [The set of functions, macros, etc. defined in the file]
00019 //
00020 //***************************************************************************
00021 
00022 //************************* System Include Files ****************************
00023 
00024 #include <stdlib.h> // <cstdlib> // for strtol
00025 #include <string.h> // <cstring>
00026 #include <errno.h>  // <cerrno>
00027 
00028 #include <algorithm> // for sort
00029 
00030 //************************** Open64 Include Files ***************************
00031 
00032 //*************************** User Include Files ****************************
00033 
00034 #include "CmdLineParser.h"
00035 
00036 //*************************** Forward Declarations ***************************
00037 
00038 using std::string;
00039 
00040 static string MISSING_SWITCH = "Missing switch after -";
00041 static string UNKNOWN_SWITCH = "Unknown option switch: ";
00042 static string MISSING_ARG = "Missing argument for switch: ";
00043 
00044 //*************************** Forward Declarations ***************************
00045 
00046 // lt_OptArgDesc: Used to sort CmdLineParser::OptArgDesc
00047 struct lt_OptArgDesc
00048 {
00049   // return true if x1 < x2; false otherwise
00050   bool operator()(const CmdLineParser::OptArgDesc& x1, 
00051                   const CmdLineParser::OptArgDesc& x2) const
00052   {
00053     // There are three possibilities, listed in order of preference:
00054     //   - both long switches are present 
00055     //   - both short switches are present
00056     //   - one short and one long switch
00057     if (x1.swLong && x2.swLong) {
00058       return (strcmp(x1.swLong, x2.swLong) < 0);
00059     }
00060     else if (x1.swShort != 0 && x2.swShort != 0) {
00061       return (x1.swShort < x2.swShort);
00062     } 
00063     else {
00064       if (x1.swLong && x2.swShort != 0) {
00065         return (x1.swLong[0] < x2.swShort);
00066       } 
00067       else {
00068         return (x1.swShort < x2.swLong[0]);
00069       }
00070     }
00071   }
00072 
00073 private:
00074 
00075 };
00076 
00077 //****************************************************************************
00078 
00079 // IsDashDash
00080 static inline bool
00081 IsDashDash(const char* str) { return (strcmp(str, "--") == 0); }
00082 
00083 
00084 // IsSwitch, IsLongSwitch, IsShortSwitch: Assumes str is non-NULL!  Note also
00085 // that the test for short switch is not quite complete and depends on
00086 // testing for a long switch first!
00087 static inline bool
00088 IsLongSwitch(const char* str) { return (strncmp(str, "--", 2) == 0); }
00089 
00090 static inline bool
00091 IsShortSwitch(const char* str) { return (*str == '-'); }
00092 
00093 static inline bool
00094 IsSwitch(const char* str) { return (IsLongSwitch(str) || IsShortSwitch(str)); }
00095 
00096 
00097 // IsArg: Verifies that we should interpret 'str' as an argument.
00098 // Should be non-NULL;
00099 static inline bool
00100 IsArg(const char* str) { return (!IsSwitch(str) && !IsDashDash(str)); }
00101 
00102 
00103 //****************************************************************************
00104 
00105 //****************************************************************************
00106 // CmdLineParser
00107 //****************************************************************************
00108 
00109 CmdLineParser::OptArgDesc CmdLineParser::OptArgDesc_NULL = 
00110   CmdLineParser_OptArgDesc_NULL_MACRO;
00111 
00112 
00113 CmdLineParser::CmdLineParser() 
00114 {
00115   Ctor();
00116 }
00117 
00118 CmdLineParser::CmdLineParser(const OptArgDesc* optArgDescs, 
00119                              int argc, const char* const argv[])
00120 {
00121   Ctor();
00122   Parse(optArgDescs, argc, argv);
00123 }
00124 
00125 void
00126 CmdLineParser::Ctor() 
00127 {
00128   // nothing to do
00129 }
00130 
00131 
00132 
00133 CmdLineParser::~CmdLineParser() 
00134 { 
00135   Reset();
00136 }
00137 
00138 
00139 void
00140 CmdLineParser::Parse(const OptArgDesc* optArgDescsOrig, 
00141                      int argc, const char* const argv[])
00142 { 
00143   Reset();
00144   command = argv[0]; // always do first so it will be available after errors
00145   
00146   CheckForErrors(optArgDescsOrig);  
00147   const OptArgDesc* optArgDescs = CreateSortedCopy(optArgDescsOrig);
00148   
00149   bool endOfOpts = false;  // are we at end of optional args?
00150   
00151   for (int i = 1; i < argc; ++i) {
00152     const char* str = argv[i];
00153     
00154     // -------------------------------------------------------
00155     // Bypass special option values
00156     // -------------------------------------------------------
00157     if (str == NULL || *str == '\0') {
00158       continue; // should never happen, but we ignore
00159     }
00160     
00161     // A '--' signifies end of optional arguments
00162     if (IsDashDash(str)) {
00163       endOfOpts = true;
00164       continue;
00165     }
00166     
00167     if (!endOfOpts && IsSwitch(str)) {
00168       // -------------------------------------------------------
00169       // An option switch (possibly needing an argument)
00170       // -------------------------------------------------------
00171       // Note: The argument may be appended to the switch or it may be
00172       // the next element of argv.
00173       
00174       // 1. Separate switch from any argument embedded within
00175       SwDesc swdesc = MakeSwitchDesc(str);
00176       if (swdesc.sw.empty()) {
00177         throw ParseError(MISSING_SWITCH); // must have been '-'
00178       }
00179       
00180       // 2. Find option descriptor from switch (checks for duplicate matches)
00181       const OptArgDesc* d = FindOptDesc(optArgDescs, swdesc);
00182       if (!d) {
00183         throw ParseError(UNKNOWN_SWITCH + swdesc.sw);
00184       }
00185       
00186       // 3. Find argument for switch (if any) [N.B. may advance iteration!]
00187       if (d->kind == ARG_NONE) {
00188         if (!swdesc.arg.empty()) {
00189           string msg = "Invalid argument `" + swdesc.arg + "' to switch `" 
00190             + swdesc.sw + "'";
00191           throw ParseError(msg);
00192         }
00193       } else if (d->kind == ARG_REQ || d->kind == ARG_OPT) {
00194         if (swdesc.arg.empty()) {
00195           int nexti = i + 1;
00196           if (nexti < argc && argv[nexti] && IsArg(argv[nexti])) {
00197             swdesc.arg = argv[nexti];
00198             i = nexti; // increment iteration
00199           }
00200         } 
00201         if (swdesc.arg.empty() && d->kind == ARG_REQ) {
00202           throw ParseError(MISSING_ARG + swdesc.sw);
00203         }
00204       }
00205       
00206       // 4. Add option switch and any argument to map
00207       AddOption(*d, swdesc);
00208     }
00209     else { 
00210       // -------------------------------------------------------
00211       // A regular argument
00212       // -------------------------------------------------------
00213       arguments.push_back(string(str));
00214     } 
00215   } 
00216   
00217   delete[] optArgDescs;
00218 }
00219 
00220 
00221 //****************************************************************************
00222 
00223 const string& 
00224 CmdLineParser::GetCmd() const
00225 {
00226   return command;
00227 }
00228 
00229 
00230 // IsOpt:
00231 bool 
00232 CmdLineParser::IsOpt(const char swShort) const
00233 {
00234   string sw(1, swShort);
00235   return IsOpt(sw);
00236 }
00237 
00238 bool 
00239 CmdLineParser::IsOpt(const char* swLong) const
00240 {
00241   string sw(swLong);
00242   return IsOpt(sw);
00243 }
00244 
00245 bool 
00246 CmdLineParser::IsOpt(const string& sw) const
00247 {
00248   SwitchToArgMap::const_iterator it = switchToArgMap.find(sw);
00249   return (it != switchToArgMap.end());
00250 }
00251 
00252 
00253 // IsOptArg:
00254 bool 
00255 CmdLineParser::IsOptArg(const char swShort) const
00256 {
00257   string sw(1, swShort);
00258   return IsOptArg(sw);
00259 }
00260 
00261 bool 
00262 CmdLineParser::IsOptArg(const char* swLong) const
00263 {
00264   string sw(swLong);
00265   return IsOptArg(sw);
00266 }
00267 
00268 bool 
00269 CmdLineParser::IsOptArg(const string& sw) const
00270 {
00271   SwitchToArgMap::const_iterator it = switchToArgMap.find(sw);
00272   if ((it != switchToArgMap.end()) && ((*it).second != NULL)) {
00273     return true;
00274   }
00275   return false;
00276 }
00277 
00278 
00279 // GetOptArg:
00280 const string&
00281 CmdLineParser::GetOptArg(const char swShort) const
00282 {
00283   string sw(1, swShort);
00284   return GetOptArg(sw);
00285 }
00286 
00287 const string&
00288 CmdLineParser::GetOptArg(const char* swLong) const
00289 {
00290   string sw(swLong);
00291   return GetOptArg(sw);
00292 }
00293 
00294 const string&
00295 CmdLineParser::GetOptArg(const string& sw) const
00296 {
00297   SwitchToArgMap::const_iterator it = switchToArgMap.find(sw);
00298   if (it == switchToArgMap.end()) {
00299     // FIXME: ERROR
00300   }
00301   string* arg = (*it).second;
00302   if (!arg) {
00303     // FIXME: ERROR
00304   }
00305   return *arg;
00306 }
00307 
00308 
00309 unsigned int 
00310 CmdLineParser::GetNumArgs() const
00311 { 
00312   return arguments.size(); 
00313 }
00314 
00315 const string& 
00316 CmdLineParser::GetArg(unsigned int i) const
00317 {
00318   return arguments[i];
00319 }
00320 
00321 
00322 //****************************************************************************
00323 
00324 long
00325 CmdLineParser::ToLong(const string& str)
00326 {
00327   long value = 0;
00328   if (str.empty()) { throw InternalError("ToLong"); }
00329   
00330   errno = 0;
00331   char* endptr = NULL;
00332   value = strtol(str.c_str(), &endptr, 0);
00333   if (errno || (endptr && strlen(endptr) > 0)) {
00334     string msg = "Argument `" + str 
00335       + "' cannot be converted to integral value.";
00336     if (errno) { // not always set
00337       msg += " ";
00338       msg += strerror(errno);
00339     }
00340     throw ParseError(msg);
00341   } 
00342   return value;
00343 }
00344 
00345 
00346 uint64_t
00347 CmdLineParser::ToUInt64(const string& str)
00348 {
00349   uint64_t value = 0;
00350   if (str.empty()) { throw InternalError("ToUInt64"); }
00351   
00352   errno = 0;
00353   char* endptr = NULL;
00354   value = strtoul(str.c_str(), &endptr, 0);
00355   if (errno || (endptr && strlen(endptr) > 0)) {
00356     string msg = "Argument `" + str 
00357       + " cannot be converted to integral value.";
00358     if (errno) { // not always set
00359       msg += " ";
00360       msg += strerror(errno);
00361     }
00362     throw ParseError(msg);
00363   } 
00364   return value;
00365 }
00366 
00367 
00368 double   
00369 CmdLineParser::ToDbl(const string& str)
00370 {
00371   double value = 0;
00372   if (str.empty()) { throw InternalError("ToDbl"); }
00373   
00374   errno = 0;
00375   char* endptr = NULL;
00376   value = strtod(str.c_str(), &endptr);
00377   if (errno || (endptr && strlen(endptr) > 0)) {
00378     string msg = "Argument `" + str + "' cannot be converted to real value.";
00379     if (errno) { // not always set
00380       msg += " ";
00381       msg += strerror(errno);
00382     }
00383     throw ParseError(msg);
00384   } 
00385   return value;
00386 }
00387 
00388 
00389 //****************************************************************************
00390 
00391 void 
00392 CmdLineParser::Dump(std::ostream& os) const
00393 {
00394   os << "Command: `" << GetCmd() << "'" << std::endl;
00395   
00396   os << "Switch to Argument map:" << std::endl;
00397   for (SwitchToArgMap::const_iterator it = switchToArgMap.begin();
00398        it != switchToArgMap.end(); ++it) {
00399     const string& sw = (*it).first;
00400     const string* arg = (*it).second;
00401     os << "  " << sw << " --> " << ((arg) ? *arg : "<>") << std::endl;
00402   }
00403   
00404   os << "Regular arguments:" << std::endl;
00405   for (unsigned int i = 0; i < arguments.size(); ++i) {
00406     os << "  " << arguments[i] << std::endl;
00407   }
00408 }
00409 
00410 
00411 void 
00412 CmdLineParser::DDump() const
00413 {
00414   Dump(std::cerr);
00415 }
00416 
00417 
00418 //****************************************************************************
00419 
00420 // Reset: Clear data to prepare for parsing
00421 void
00422 CmdLineParser::Reset()
00423 {
00424   for (SwitchToArgMap::iterator it = switchToArgMap.begin();
00425        it != switchToArgMap.end(); ++it) {
00426     string* arg = (*it).second;
00427     delete arg;
00428   }
00429   switchToArgMap.clear();
00430   arguments.clear();
00431 }
00432 
00433 
00434 // CreateSortedCopy: create a sorted NULL-terminated copy of
00435 // 'optArgDescs'.  WARNING: the OptArgDesc objects are bitwise-copied.
00436 const CmdLineParser::OptArgDesc* 
00437 CmdLineParser::CreateSortedCopy(const OptArgDesc* optArgDescs)
00438 {
00439   // Find the size, not including the NULL-terminator
00440   unsigned int sz = 0; 
00441   for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p) { ++sz; }
00442   
00443   // Make a copy of 'optArgDescs'
00444   OptArgDesc* copy = new OptArgDesc[sz+1];
00445   unsigned int i = 0; 
00446   for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p, ++i) {
00447     copy[i] = *p; // bitwise copy is ok
00448   }
00449   copy[sz] = OptArgDesc_NULL; // add the NULL-terminator
00450   
00451   // Sort
00452   if (sz > 1) {
00453     std::sort(&copy[0], &copy[sz-1], lt_OptArgDesc());
00454   }
00455   
00456   return copy;
00457 }
00458 
00459 
00460 // CheckForErrors: Checks argument descriptor for errors
00461 void
00462 CmdLineParser::CheckForErrors(const OptArgDesc* optArgDescs)
00463 {
00464   // FIXME
00465   //   - detect duplicate option entries.  Not pressing because
00466   //   FindOptDesc() will effectively do this.
00467   
00468   // Check individual descriptors
00469   string msg;
00470   string sw;
00471   for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p) {
00472     // Verify that at least one switch is present
00473     if (p->swShort == 0 && !p->swLong) {
00474       throw InternalError("Arg descriptor is missing a switch!");
00475     }
00476 
00477     if (p->swLong) {
00478       sw = p->swLong; 
00479     } else {
00480       sw = p->swShort;
00481     }
00482     
00483     // Verify that the kind is valid
00484     if (p->kind == ARG_NULL) {
00485       msg = "OptArgDesc.kind is invalid for: " + sw;
00486       throw InternalError(msg);
00487     }
00488     
00489     // Verify that dupKind is valid
00490     if (p->dupKind == DUPOPT_NULL) {
00491       msg = "OptArgDesc.dupKind is invalid for: " + sw;
00492       throw InternalError(msg);
00493     }
00494     
00495     // Verify that if dupKind == DUPOPT_CAT, dupArgSep is valid
00496     if (p->dupKind == DUPOPT_CAT && !p->dupArgSep) {
00497       msg = "OptArgDesc.dupArgSep is invalid for: " + sw;
00498       throw InternalError(msg);
00499     }
00500   }
00501 }
00502 
00503 
00504 // MakeSwitchDesc: Given an option string from argv (potentially
00505 // containing both an option and an argument), create a SwDesc,
00506 // separating switch text from any argument text.
00507 CmdLineParser::SwDesc
00508 CmdLineParser::MakeSwitchDesc(const char* str)
00509 {
00510   // 1. Find pointers for begin/end of switch and argument
00511   unsigned int len = strlen(str);
00512   const char* strEnd = str + len;
00513   const char* begSw = NULL, *endSw = NULL;   // end pointers are inclusive!
00514   const char* begArg = NULL, *endArg = NULL;
00515   bool isLong = false;
00516   if (IsLongSwitch(str)) {
00517     // test for --foo=arg
00518     begArg = strchr(str, '=');
00519     if (begArg) {
00520       begArg++;            // starts after the '='
00521       endArg = strEnd - 1; // ends right before '\0'
00522     }
00523     begSw = str + 2;       // bump past '--'
00524     endSw = (begArg) ? (begArg - 2) : (strEnd - 1);
00525     isLong = true;
00526   } 
00527   else if (IsShortSwitch(str)) {
00528     // test for -f[arg]
00529     begArg = (len > 2) ? (str + 2) : NULL;   // starts after '-f'
00530     endArg = (begArg) ? (strEnd - 1) : NULL; // ends right before '\0'
00531     begSw  = (len > 1) ? (str + 1) : NULL;   // starts after '-'
00532     endSw  = begSw;                               // single character
00533   } 
00534   else {
00535     throw InternalError("Programming Error!");
00536   }
00537   
00538   // 2. Copy switch and argument substrings
00539   SwDesc swdesc;
00540   swdesc.isLong = isLong;
00541   for (const char* p = begSw; p && p <= endSw; ++p) { swdesc.sw += *p; }
00542   for (const char* p = begArg; p && p <= endArg; ++p) { swdesc.arg += *p; }
00543   
00544   return swdesc;
00545 }
00546 
00547 
00548 // FindOptDesc: Given a *sorted* NULL-terminated array of OptArgDesc and
00549 // an option switch, return a reference to the appropriate OptArgDesc.
00550 // If 'errOnMultipleMatches' is true, checks to make sure we don't
00551 // match more than one descriptor (useful for testing long argument
00552 // abbreviation).
00553 const CmdLineParser::OptArgDesc*
00554 CmdLineParser::FindOptDesc(const OptArgDesc* optArgDescs, const SwDesc& swdesc,
00555                            bool errOnMultipleMatches)
00556 {
00557   // Note: Because there will never be very many options, we simply
00558   //   use a linear search.
00559   // Note: A long option may be a substring of another long option!
00560   //   Because 'optArgDescs' will be sorted, any options that are
00561   //   substrings of other options will be ordered so that they appear
00562   //   before the option that contains them, e.g. 'xx', 'xxx', 'xxxx',
00563   //   'xxxxx'.
00564   
00565   // Try to find a matching descriptor
00566   unsigned int swLen = swdesc.sw.length();
00567   const OptArgDesc* odesc = NULL;
00568   for (const OptArgDesc* p = optArgDescs; *p != OptArgDesc_NULL; ++p) {
00569     if (swdesc.isLong) {
00570       if (p->swLong && strncmp(p->swLong, swdesc.sw.c_str(), swLen) == 0) {
00571         odesc = p;
00572         break;
00573       }
00574     } else {
00575       if (p->swShort != 0 && p->swShort == swdesc.sw[0]) {
00576         odesc = p;
00577         break;
00578       }
00579     }
00580   }
00581   if (!odesc) { return NULL; }
00582   
00583   // We have a match. Check for more matches ==> ambiguity.
00584   const OptArgDesc* m = NULL;
00585   if (errOnMultipleMatches && (m = FindOptDesc((odesc+1), swdesc, false))) {
00586     // Special case to handle a long option that is a substring of
00587     // another. If the long option switch exactly matches 'odesc' and
00588     // it is different than 'm' then we do not want to generate an
00589     // ambiguous option error.
00590     bool ok = (swdesc.isLong && (strcmp(odesc->swLong, swdesc.sw.c_str()) == 0)
00591                && (strcmp(odesc->swLong, m->swLong) != 0));
00592     if (!ok) {
00593       string msg = "Switch `"; 
00594       msg += swdesc.sw; msg += "' matches two different options: ";
00595       if (swdesc.isLong) {
00596         msg += odesc->swLong; msg += ", "; msg += m->swLong;
00597       } else {
00598         msg += odesc->swShort; msg += ", "; msg += m->swShort;
00599       }
00600       throw ParseError(msg);
00601     } 
00602   }
00603   
00604   return odesc;
00605 }
00606 
00607 
00608 // AddOption: Records the option switch and its (possibly optional)
00609 // argument in the switch->argument map.  In order to support easy
00610 // lookup, both the *canonical* long and short form of the switches
00611 // are entered in the map.
00612 void
00613 CmdLineParser::AddOption(const OptArgDesc& odesc, const SwDesc& swdesc)
00614 {
00615   if (odesc.swShort != 0) {
00616     string swShort(1, odesc.swShort);
00617     AddOption(odesc, swShort, swdesc.arg);
00618   }
00619   if (odesc.swLong) {
00620     string swLong(odesc.swLong);
00621     AddOption(odesc, swLong, swdesc.arg);
00622   }
00623 }
00624 
00625 
00626 // AddOption: Records the option switch and its (possibly optional)
00627 // argument in the switch->argument map.  If the switch is not in the
00628 // map, it is inserted with the available argument or NULL.  If it is
00629 // already the map, the option descriptor defines how to handle
00630 // duplicates.
00631 void
00632 CmdLineParser::AddOption(const OptArgDesc& odesc,
00633                          const string& sw, const string& arg)
00634 {
00635   SwitchToArgMap::iterator it = switchToArgMap.find(sw);
00636   if (it == switchToArgMap.end()) {
00637     // Insert in map
00638     string* theArg = (arg.empty()) ? NULL : new string(arg);
00639     switchToArgMap.insert(SwitchToArgMap::value_type(sw, theArg));
00640   } else {
00641     // Handle duplicates
00642     string* theArg = (*it).second;
00643     
00644     if (odesc.dupKind == DUPOPT_ERR) {
00645       throw ParseError("Duplicate switch: " + sw);
00646     }
00647     
00648     if (!arg.empty()) {
00649       if (!theArg) {
00650         theArg = new string(arg);
00651       } else {
00652         if (odesc.dupKind == DUPOPT_CLOB) {
00653           *theArg = arg;
00654         } else if (odesc.dupKind == DUPOPT_CAT) {
00655           *theArg += odesc.dupArgSep + arg;
00656         } 
00657       }
00658     }
00659   }
00660 }
00661 
00662 //****************************************************************************
00663 

Generated on Fri Jul 24 04:29:02 2009 for OpenADFortTk (extended to Open64) by  doxygen 1.5.7.1