diff --git a/library/argstream/argstream.h b/library/argstream/argstream.h new file mode 100644 index 000000000..8b55e294a --- /dev/null +++ b/library/argstream/argstream.h @@ -0,0 +1,813 @@ +/* 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 "<