Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-02-14 12:44:56

0001 //=========================================================================
0002 // CmdLine.hh
0003 //
0004 // Simple command line parser for the C++ "main" program. It provides
0005 // functionality of "getopt" and "getopt_long" with a convenient interface.
0006 // Typical usage is as follows:
0007 //
0008 // #include <necessary standard headers>
0009 //
0010 // #include "CmdLine.hh"
0011 //
0012 // int main(int argc, char *argv[])
0013 // {
0014 //     CmdLine cmdline(argc, argv);
0015 //
0016 //     int i = 0;
0017 //     double d = 0;
0018 //     bool b = false;
0019 //     std::string requiredOption;
0020 //     std::vector<std::string> positionalArgs;
0021 //
0022 //     try {
0023 //         /* Arguments are short and long versions of the option name. */
0024 //         /* Long version can be omitted. If you want to use the long  */
0025 //         /* version only, call the two-argument method with the short */
0026 //         /* version set to NULL.                                      */
0027 //         cmdline.option("-i", "--integer") >> i;
0028 //         cmdline.option("-d") >> d;
0029 //
0030 //         /* Options that must be present on the command line  */
0031 //         cmdline.require("-r", "--required") >> requiredOption;
0032 //
0033 //         /* Switches that do not require additional arguments */
0034 //         b = cmdline.has("-b");
0035 //
0036 //         /* Declare the end of option processing. Unconsumed options  */
0037 //         /* will cause an exception to be thrown.                     */
0038 //         cmdline.optend();
0039 //
0040 //         /* Process all remaining arguments */
0041 //         while (cmdline)
0042 //         {
0043 //             std::string s;
0044 //             cmdline >> s;
0045 //             positionalArgs.push_back(s);
0046 //         }
0047 //     }
0048 //     catch (const CmdLineError& e) {
0049 //         std::cerr << "Error in " << cmdline.progname() << ": "
0050 //                   << e.str() << std::endl;
0051 //         return 1;
0052 //     }
0053 //
0054 //     /* ..... Do main processing here ..... */
0055 //
0056 //     return 0;
0057 // }
0058 //
0059 // Short version options must use a single character. It is possible
0060 // to combine several short options together on the command line,
0061 // for exampe, "-xzvf" is equivalent to "-x -z -v -f".
0062 //
0063 // Use standalone "-" to indicate that the next argument is either
0064 // an option value or the program argument (but not a switch), even
0065 // if it starts with "-". This is useful if the option value has to
0066 // be set to a negative number.
0067 //
0068 // Use standalone "--" (not preceded by standalone "-") to indicate
0069 // the end of options. All remaining arguments will be treated as
0070 // program arguments, even if they start with "-".
0071 //
0072 // Note that each of the parsing methods of the CmdLine class ("option",
0073 // "has", and "require") is greedy. These methods will consume all
0074 // corresponding options or switches and will set the result to the last
0075 // option value seen. It is therefore impossible to provide a collection
0076 // of values by using an option more than once. This is done in order to
0077 // avoid difficulties with deciding what to do when multiple option values
0078 // were consumed by the user code only partially.
0079 //
0080 // After the "optend()" call, the "argc()" method of the CmdLine
0081 // class can be used to determine the number of remaining program
0082 // arguments. If the expected number of arguments is known in advance,
0083 // the simplest way to get the arguments out is like this (should be
0084 // inside the "try" block):
0085 //
0086 // if (cmdline.argc() != n_expected)
0087 //     throw CmdLineError("wrong number of command line arguments");
0088 // cmdline >> arg0 >> arg1 >> ...;
0089 //
0090 //
0091 // I. Volobouev
0092 // March 2013
0093 //=========================================================================
0094 
0095 #ifndef CMDLINE_HH_
0096 #define CMDLINE_HH_
0097 
0098 #include <cstdio>
0099 #include <cstring>
0100 #include <memory>
0101 #include <list>
0102 #include <sstream>
0103 #include <utility>
0104 
0105 // Subsequent classes will throw exceptions of the following class
0106 class CmdLineError {
0107 public:
0108   inline CmdLineError(const char *msg = 0) : os_(new std::ostringstream()) {
0109     if (msg)
0110       *os_ << msg;
0111   }
0112 
0113   template <typename T>
0114   inline CmdLineError &operator<<(const T &obj) {
0115     *os_ << obj;
0116     return *this;
0117   }
0118 
0119   inline std::string str() const { return os_->str(); }
0120 
0121 private:
0122   std::shared_ptr<std::ostringstream> os_;
0123 };
0124 
0125 template <typename T>
0126 inline void OneShotExtract(std::istringstream &is, T &obj) {
0127   is >> obj;
0128 }
0129 
0130 template <>
0131 inline void OneShotExtract<std::string>(std::istringstream &is, std::string &obj) {
0132   obj = is.str();
0133   is.seekg(0, std::ios_base::end);
0134 }
0135 
0136 class OneShotIStream {
0137 public:
0138   inline OneShotIStream() : valid_(false), readout_(false) {}
0139 
0140   inline OneShotIStream(const std::string &s) : str_(s), valid_(true), readout_(false) {}
0141 
0142   inline operator void *() const { return valid_ && !readout_ ? (void *)this : (void *)0; }
0143 
0144   template <typename T>
0145   inline bool operator>>(T &obj) {
0146     if (readout_)
0147       throw CmdLineError() << "can't reuse command line argument \"" << str_ << '"';
0148     readout_ = true;
0149     if (valid_) {
0150       std::istringstream is(str_);
0151       OneShotExtract(is, obj);
0152       if (is.bad() || is.fail())
0153         throw CmdLineError() << "failed to parse command line argument \"" << str_ << '"';
0154       if (is.peek() != EOF)
0155         throw CmdLineError() << "extra characters in command line argument \"" << str_ << '"';
0156     }
0157     return valid_;
0158   }
0159 
0160   inline bool isValid() const { return valid_; }
0161 
0162 private:
0163   std::string str_;
0164   bool valid_;
0165   bool readout_;
0166 };
0167 
0168 class CmdLine {
0169   // Argument codes (second member of the pair):
0170   //   0 -- possible option value (or program argument, not yet known which)
0171   //   1 -- short option switch
0172   //   2 -- long option switch
0173   //   3 -- program argument
0174   typedef std::pair<std::string, int> Pair;
0175   typedef std::list<Pair> Optlist;
0176 
0177   inline Optlist::iterator find(const char *shortOpt, const char *longOpt) {
0178     Optlist::iterator iend = args_.end();
0179     for (Optlist::iterator it = args_.begin(); it != iend; ++it) {
0180       if (shortOpt && it->second == 1 && it->first == shortOpt)
0181         return it;
0182       if (longOpt && it->second == 2 && it->first == longOpt)
0183         return it;
0184     }
0185     return iend;
0186   }
0187 
0188 public:
0189   inline CmdLine(const unsigned argc, const char *const argv[]) : nprogargs_(0) {
0190     // Parse the program name
0191     const char *progname = std::strrchr(argv[0], '/');
0192     if (progname)
0193       ++progname;
0194     else
0195       progname = argv[0];
0196 
0197     // Take into account program name mangling by GNU autotools
0198     if (strncmp(progname, "lt-", 3) == 0)
0199       progname += 3;
0200     progname_ = progname;
0201 
0202     // Make a list of arguments noting on the way if this is
0203     // a short option, long option, or possible option argument
0204     bool previousIsOpt = false;
0205     bool nextIsArg = false;
0206     for (unsigned i = 1; i < argc; ++i) {
0207       if (nextIsArg) {
0208         args_.push_back(Pair(argv[i], previousIsOpt ? 0 : 3));
0209         previousIsOpt = false;
0210         ++nprogargs_;
0211         nextIsArg = false;
0212       } else if (strcmp(argv[i], "-") == 0)
0213         nextIsArg = true;
0214       else if (strcmp(argv[i], "--") == 0) {
0215         // End of options
0216         for (unsigned k = i + 1; k < argc; ++k) {
0217           args_.push_back(Pair(argv[k], 3));
0218           ++nprogargs_;
0219         }
0220         return;
0221       } else if (strncmp(argv[i], "--", 2) == 0) {
0222         args_.push_back(Pair(argv[i], 2));
0223         previousIsOpt = true;
0224       } else if (argv[i][0] == '-') {
0225         const unsigned len = strlen(argv[i]);
0226         for (unsigned k = 1; k < len; ++k) {
0227           std::string dummy("-");
0228           dummy += argv[i][k];
0229           args_.push_back(Pair(dummy, 1));
0230           previousIsOpt = true;
0231         }
0232       } else {
0233         args_.push_back(Pair(argv[i], previousIsOpt ? 0 : 3));
0234         previousIsOpt = false;
0235         ++nprogargs_;
0236       }
0237     }
0238   }
0239 
0240   inline const char *progname() const { return progname_.c_str(); }
0241 
0242   inline bool has(const char *shortOpt, const char *longOpt = 0) {
0243     bool found = false;
0244     for (Optlist::iterator it = find(shortOpt, longOpt); it != args_.end(); it = find(shortOpt, longOpt)) {
0245       found = true;
0246       Optlist::iterator it0(it);
0247       if (++it != args_.end())
0248         if (it->second == 0)
0249           it->second = 3;
0250       args_.erase(it0);
0251     }
0252     return found;
0253   }
0254 
0255   inline OneShotIStream option(const char *shortOpt, const char *longOpt = 0) {
0256     OneShotIStream result;
0257     for (Optlist::iterator it = find(shortOpt, longOpt); it != args_.end(); it = find(shortOpt, longOpt)) {
0258       Optlist::iterator it0(it);
0259       if (++it != args_.end())
0260         if (it->second == 0) {
0261           result = OneShotIStream(it->first);
0262           args_.erase(it0, ++it);
0263           --nprogargs_;
0264           continue;
0265         }
0266       throw CmdLineError() << "missing command line argument for option \"" << it0->first << '"';
0267     }
0268     return result;
0269   }
0270 
0271   inline OneShotIStream require(const char *shortOpt, const char *longOpt = 0) {
0272     const OneShotIStream &is(option(shortOpt, longOpt));
0273     if (!is.isValid()) {
0274       const char empty[] = "";
0275       const char *s = shortOpt ? shortOpt : (longOpt ? longOpt : empty);
0276       throw CmdLineError() << "required command line option \"" << s << "\" is missing";
0277     }
0278     return is;
0279   }
0280 
0281   inline void optend() const {
0282     for (Optlist::const_iterator it = args_.begin(); it != args_.end(); ++it)
0283       if (it->second == 1 || it->second == 2)
0284         throw CmdLineError("invalid command line option \"") << it->first << '"';
0285   }
0286 
0287   inline operator void *() const { return (void *)(static_cast<unsigned long>(nprogargs_)); }
0288 
0289   inline unsigned argc() const { return nprogargs_; }
0290 
0291   template <typename T>
0292   inline CmdLine &operator>>(T &obj) {
0293     if (!nprogargs_)
0294       throw CmdLineError("no more input available on the command line");
0295     Optlist::iterator it = args_.begin();
0296     for (; it != args_.end(); ++it)
0297       if (it->second == 0 || it->second == 3)
0298         break;
0299     OneShotIStream is(it->first);
0300     args_.erase(it);
0301     --nprogargs_;
0302     is >> obj;
0303     return *this;
0304   }
0305 
0306 private:
0307   CmdLine();
0308 
0309   std::string progname_;
0310   Optlist args_;
0311   unsigned nprogargs_;
0312 };
0313 
0314 #endif  // CMDLINE_HH_