/* Copyright (C) 2004 Xavier Décoret * * argsteam is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Foobar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef ARGSTREAM_H #define ARGSTREAM_H #include #include #include #include #include #include #include namespace { class argstream; template class ValueHolder; template argstream& operator>> (argstream&, const ValueHolder&); //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Interface of ValueHolder //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ template class ValueHolder { public: ValueHolder(char s, const char* l, T& b, const char* desc, bool mandatory); ValueHolder(const char* l, T& b, const char* desc, bool mandatory); ValueHolder(char s, T& b, const char* desc, bool mandatory); friend argstream& operator>><>(argstream& s,const ValueHolder& v); std::string name() const; std::string description() const; private: std::string shortName_; std::string longName_; T* value_; T initialValue_; std::string description_; bool mandatory_; }; template inline ValueHolder parameter(char s, const char* l, T& b, const char* desc="", bool mandatory = true) { return ValueHolder(s,l,b,desc,mandatory); } template inline ValueHolder parameter(char s, T& b, const char* desc="", bool mandatory = true) { return ValueHolder(s,b,desc,mandatory); } template inline ValueHolder parameter(const char* l, T& b, const char* desc="", bool mandatory = true) { return ValueHolder(l,b,desc,mandatory); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Interface of OptionHolder //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class OptionHolder { public: inline OptionHolder(char s, const char* l, bool& b, const char* desc); inline OptionHolder(const char* l, bool& b, const char* desc); inline OptionHolder(char s, bool& b, const char* desc); friend argstream& operator>>(argstream& s,const OptionHolder& v); inline std::string name() const; inline std::string description() const; protected: inline OptionHolder(char s, const char* l, const char* desc); friend OptionHolder help(char s='h', const char* l="help", const char* desc="Display this help"); private: std::string shortName_; std::string longName_; bool* value_; std::string description_; }; inline OptionHolder option(char s, const char* l, bool& b, const char* desc="") { return OptionHolder(s,l,b,desc); } inline OptionHolder option(char s, bool& b, const char* desc="") { return OptionHolder(s,b,desc); } inline OptionHolder option(const char* l, bool& b, const char* desc="") { return OptionHolder(l,b,desc); } inline OptionHolder help(char s, const char* l, const char* desc) { return OptionHolder(s,l,desc); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Interface of ValuesHolder //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ template class ValuesHolder { public: ValuesHolder(const O& o, const char* desc, int len); friend argstream& operator>><>(argstream& s,const ValuesHolder& v); std::string name() const; std::string description() const; typedef T value_type; private: mutable O value_; std::string description_; int len_; char letter_; }; template inline ValuesHolder values(const O& o, const char* desc="", int len=-1) { return ValuesHolder(o,desc,len); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Interface of ValueParser //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ template class ValueParser { public: inline T operator()(const std::string& s) const { std::istringstream is(s); T t; is>>t; return t; } }; // We need to specialize for string otherwise parsing of a value that // contains space (for example a string with space passed in quotes on the // command line) would parse only the first element of the value!!! template <> class ValueParser { public: inline std::string operator()(const std::string& s) const { return s; } }; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Interface of argstream //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class argstream { public: inline argstream(int argc,char** argv); inline argstream(const char* c); template friend argstream& operator>>(argstream& s,const ValueHolder& v); friend inline argstream& operator>>(argstream& s,const OptionHolder& v); template friend argstream& operator>>(argstream& s,const ValuesHolder& v); inline bool helpRequested() const; inline bool isOk() const; inline std::string errorLog() const; inline std::string usage() const; inline void defaultErrorHandling(bool ignoreUnused=false) const; static inline char uniqueLetter(); protected: void parse(int argc,char** argv); private: typedef std::list::iterator value_iterator; typedef std::pair help_entry; std::string progName_; std::map options_; std::list values_; bool minusActive_; bool isOk_; std::deque argHelps_; std::string cmdLine_; std::deque errors_; bool helpRequested_; }; //************************************************************ // Implementation of ValueHolder //************************************************************ template ValueHolder::ValueHolder(char s, const char* l, T& v, const char* desc, bool mandatory) : shortName_(1,s), longName_(l), value_(&v), initialValue_(v), description_(desc), mandatory_(mandatory) { } template ValueHolder::ValueHolder(const char* l, T& v, const char* desc, bool mandatory) : longName_(l), value_(&v), initialValue_(v), description_(desc), mandatory_(mandatory) { } template ValueHolder::ValueHolder(char s, T& v, const char* desc, bool mandatory) : shortName_(1,s), value_(&v), initialValue_(v), description_(desc), mandatory_(mandatory) { } template std::string ValueHolder::name() const { std::ostringstream os; if (!shortName_.empty()) os<<'-'< std::string ValueHolder::description() const { std::ostringstream os; os< //************************************************************ template ValuesHolder::ValuesHolder(const O& o, const char* desc, int len) : value_(o), description_(desc), len_(len) { letter_ = argstream::uniqueLetter(); } template std::string ValuesHolder::name() const { std::ostringstream os; os< std::string ValuesHolder::description() const { return description_; } //************************************************************ // Implementation of argstream //************************************************************ inline argstream::argstream(int argc,char** argv) : progName_(argv[0]), minusActive_(true), isOk_(true) { parse(argc,argv); } inline argstream::argstream(const char* c) : progName_(""), minusActive_(true), isOk_(true) { std::string s(c); // Build argc, argv from s. We must add a dummy first element for // progName because parse() expects it!! std::deque args; args.push_back(""); std::istringstream is(s); while (is.good()) { std::string t; is>>t; args.push_back(t); } char* pargs[args.size()]; char** p = pargs; for (std::deque::const_iterator iter = args.begin(); iter != args.end();++iter) { *p++ = const_cast(iter->c_str()); } parse(args.size(),pargs); } inline void argstream::parse(int argc,char** argv) { // Run thru all arguments. // * it has -- in front : it is a long name option, if remainder is empty, // it is an error // * it has - in front : it is a sequence of short name options, if // remainder is empty, deactivates option (- will // now be considered a char). // * if any other char, or if option was deactivated // : it is a value. Values are split in parameters // (immediately follow an option) and pure values. // Each time a value is parsed, if the previously parsed argument was an // option, then the option is linked to the value in case of it is a // option with parameter. The subtle point is that when several options // are given with short names (ex: -abc equivalent to -a -b -c), the last // parsed option is -c). // Since we use map for option, any successive call overides the previous // one: foo -a -b -a hello is equivalent to foo -b -a hello // For values it is not true since we might have several times the same // value. value_iterator* lastOption = NULL; for (char** a = argv,**astop=a+argc;++a!=astop;) { std::string s(*a); if (minusActive_ && s[0] == '-') { if (s.size() > 1 && s[1] == '-') { if (s.size() == 2) { minusActive_ = false; continue; } lastOption = &(options_[s.substr(2)] = values_.end()); } else { if (s.size() > 1) { // Parse all chars, if it is a minus we have an error for (std::string::const_iterator cter = s.begin(); ++cter != s.end();) { if (*cter == '-') { isOk_ = false; std::ostringstream os; os<<"- in the middle of a switch "<::const_iterator iter = options_.begin();iter != options_.end();++iter) { std::cout<<"DEBUG: option "<first; if (iter->second != values_.end()) { std::cout<<" -> "<<*(iter->second); } std::cout<::const_iterator iter = values_.begin();iter != values_.end();++iter) { std::cout<<"DEBUG: value "<<*iter<::const_iterator iter = argHelps_.begin();iter != argHelps_.end();++iter) { if (lmaxfirst.size()) lmax = iter->first.size(); } for (std::deque::const_iterator iter = argHelps_.begin();iter != argHelps_.end();++iter) { os<<'\t'<first<first.size(),' ') <<" : "<second<<'\n'; } return os.str(); } inline std::string argstream::errorLog() const { std::string s; for(std::deque::const_iterator iter = errors_.begin(); iter != errors_.end();++iter) { s += *iter; s += '\n'; } return s; } inline char argstream::uniqueLetter() { static unsigned int c = 'a'; return c++; } template argstream& operator>>(argstream& s,const ValueHolder& v) { // Search in the options if there is any such option defined either with a // short name or a long name. If both are found, only the last one is // used. #ifdef ARGSTREAM_DEBUG std::cout<<"DEBUG: searching "<::iterator iter = s.options_.find(v.shortName_); if (iter == s.options_.end()) { iter = s.options_.find(v.longName_); } if (iter != s.options_.end()) { // If we find counterpart for value holder on command line, either it // has an associated value in which case we assign it, or it has not, in // which case we have an error. if (iter->second != s.values_.end()) { #ifdef ARGSTREAM_DEBUG std::cout<<"DEBUG: found value "<<*(iter->second)< p; *(v.value_) = p(*(iter->second)); // The option and its associated value are removed, the subtle thing // is that someother options might have this associated value too, // which we must invalidate. s.values_.erase(iter->second); for (std::map::iterator jter = s.options_.begin();jter != s.options_.end();++jter) { if (jter->second == iter->second) { jter->second = s.values_.end(); } } s.options_.erase(iter); } else { s.isOk_ = false; std::ostringstream os; os<<"No value following switch "<first <<" on command line"; s.errors_.push_back(os.str()); } } else { if (v.mandatory_) { s.isOk_ = false; std::ostringstream os; os<<"Mandatory parameter "; if (!v.shortName_.empty()) os<<'-'<>(argstream& s,const OptionHolder& v) { // Search in the options if there is any such option defined either with a // short name or a long name. If both are found, only the last one is // used. #ifdef ARGSTREAM_DEBUG std::cout<<"DEBUG: searching "<::iterator iter = s.options_.find(v.shortName_); if (iter == s.options_.end()) { iter = s.options_.find(v.longName_); } if (iter != s.options_.end()) { // If we find counterpart for value holder on command line then the // option is true and if an associated value was found, it is ignored if (v.value_ != NULL) { *(v.value_) = true; } else { s.helpRequested_ = true; } // The option only is removed s.options_.erase(iter); } else { if (v.value_ != NULL) { *(v.value_) = false; } else { s.helpRequested_ = false; } } return s; } template argstream& operator>>(argstream& s,const ValuesHolder& v) { s.argHelps_.push_back(argstream::help_entry(v.name(),v.description())); { std::ostringstream os; os<<' '<::iterator first = s.values_.begin(); // We add to the iterator as much values as we can, limited to the length // specified (if different of -1) int n = v.len_ != -1?v.len_:s.values_.size(); while (first != s.values_.end() && n-->0) { // Read the value from the string *first ValueParser p; *(v.value_++) = p(*first ); s.argHelps_.push_back(argstream::help_entry(v.name(),v.description())); // The value we just removed was maybe "remembered" by an option so we // remove it now. for (std::map::iterator jter = s.options_.begin();jter != s.options_.end();++jter) { if (jter->second == first) { jter->second = s.values_.end(); } } ++first; } // Check if we have enough values if (n != 0) { s.isOk_ = false; std::ostringstream os; os<<"Expecting "<