Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:03:07

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