- added argstream to handle commandline arguments

- switched tests to use the common value of argstream in libretroshare, rather than the one in tests/common
- reworked command line arguments in rsinit and retroshare-nogui.
- improved passwd hash explanations
- improved command-line help.



git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@6559 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
csoler 2013-08-06 17:01:38 +00:00
parent 9ffb625f9a
commit aba5033604
13 changed files with 1031 additions and 1094 deletions

View File

@ -92,7 +92,7 @@ std::string pgp_pwd_callback(void * /*hook*/, const char *uid_hint, const char *
fprintf(stderr, "pgp_pwd_callback() called.\n");
#endif
std::string password;
rsicontrol->getNotify().askForPassword(uid_hint, prev_was_bad, password) ;
rsicontrol->getNotify().askForPassword(std::string("Please enter your PGP password for key ")+uid_hint+" :", prev_was_bad, password) ;
return password ;
}

View File

@ -35,6 +35,7 @@
#include "util/rswin.h"
#endif
#include "util/argstream.h"
#include "util/rsdebug.h"
#include "util/rsdir.h"
#include "util/rsrandom.h"
@ -122,12 +123,11 @@ class RsInitConfig
static bool forceExtPort;
static bool forceLocalAddr;
static unsigned short port;
static char inet[256];
static std::string inet ;
/* Logging */
static bool haveLogFile;
static bool outStderr;
static bool haveDebugLevel;
static int debugLevel;
static std::string logfname;
@ -183,12 +183,11 @@ bool RsInitConfig::isWindowsXP = false;
bool RsInitConfig::forceExtPort;
bool RsInitConfig::forceLocalAddr;
unsigned short RsInitConfig::port;
char RsInitConfig::inet[256];
std::string RsInitConfig::inet;
/* Logging */
bool RsInitConfig::haveLogFile;
bool RsInitConfig::outStderr;
bool RsInitConfig::haveDebugLevel;
int RsInitConfig::debugLevel;
std::string RsInitConfig::logfname;
@ -220,12 +219,11 @@ void RsInit::InitRsConfig()
RsInitConfig::outStderr = false;
RsInitConfig::forceExtPort = false;
strcpy(RsInitConfig::inet, "127.0.0.1");
RsInitConfig::inet = std::string("127.0.0.1");
RsInitConfig::autoLogin = false; // .
RsInitConfig::startMinimised = false;
RsInitConfig::passwd = "";
RsInitConfig::haveDebugLevel = false;
RsInitConfig::debugLevel = PQL_WARNING;
RsInitConfig::udpListenerOnly = false;
@ -330,19 +328,12 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck
{
/* THIS IS A HACK TO ALLOW WINDOWS TO ACCEPT COMMANDLINE ARGUMENTS */
int argc;
int i;
#ifdef USE_CMD_ARGS
char** argv = argvIgnored;
argc = argcIgnored;
#else
const int MAX_ARGS = 32;
int j;
char *argv[MAX_ARGS];
@ -368,24 +359,16 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck
}
}
argc = i;
#endif
for( i=0; i<argc; i++)
{
printf("%d: %s\n", i, argv[i]);
}
/* for static PThreads under windows... we need to init the library...
*/
#ifdef PTW32_STATIC_LIB
pthread_win32_process_attach_np();
#endif
#endif
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
@ -394,6 +377,41 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck
/* getopt info: every availiable option is listed here. if it is followed by a ':' it
needs an argument. If it is followed by a '::' the argument is optional.
*/
//RsInitConfig::logfname = "" ;
//RsInitConfig::inet = "" ;
argstream as(argc,argv) ;
as >> option('a',"auto-login" ,RsInitConfig::autoLogin ,"AutoLogin (Windows Only) + StartMinimised")
>> option('m',"minimized" ,RsInitConfig::startMinimised ,"Start minimized." )
>> option('s',"stderr" ,RsInitConfig::outStderr ,"output to stderr instead of log file." )
>> option('u',"udp" ,RsInitConfig::udpListenerOnly,"Only listen to UDP." )
>> option('e',"external-port" ,RsInitConfig::forceExtPort ,"Use a forwarded external port." )
>> parameter('l',"log-file" ,RsInitConfig::logfname ,"logfile" ,"Set Log filename." ,false)
>> parameter('d',"debug-level" ,RsInitConfig::debugLevel ,"level" ,"Set debug level." ,false)
>> parameter('w',"password" ,RsInitConfig::passwd ,"password" ,"Set Login Password." ,false)
>> parameter('i',"ip-address" ,RsInitConfig::inet ,"nnn.nnn.nnn.nnn", "Set IP address to use." ,false)
>> parameter('p',"port" ,RsInitConfig::port ,"port", "Set listenning port to use." ,false)
>> parameter('c',"base-dir" ,RsInitConfig::basedir ,"directory", "Set base directory." ,false)
>> parameter('U',"user-id" ,prefUserString ,"ID", "[User Name/GPG id/SSL id] Sets Account to Use, Useful when Autologin is enabled",false)
>> parameter('r',"link" ,RsInitConfig::RetroShareLink ,"retroshare://...", "Use a given Retroshare Link" ,false)
#ifdef LOCALNET_TESTING
>> parameter('R',"restrict-port" ,portRestrictions ,"port1-port2","Apply port restriction" ,false)
#endif
>> help() ;
as.defaultErrorHandling(true) ;
if(RsInitConfig::autoLogin) RsInitConfig::startMinimised = true ;
if(RsInitConfig::outStderr) RsInitConfig::haveLogFile = false ;
if(!RsInitConfig::logfname.empty()) RsInitConfig::haveLogFile = true;
if(!RsInitConfig::inet.empty()) RsInitConfig::forceLocalAddr = true;
#ifdef LOCALNET_TESTING
if(!portRestrictions.empty()) doPortRestrictions = true;
#endif
#ifdef SUSPENDED_CODE
#ifdef LOCALNET_TESTING
while((c = getopt(argc, argv,"hesamui:p:c:w:l:d:U:r:R:")) != -1)
#else
@ -402,85 +420,6 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck
{
switch (c)
{
case 'a':
RsInitConfig::autoLogin = true;
RsInitConfig::startMinimised = true;
std::cerr << "AutoLogin Allowed / Start Minimised On";
std::cerr << std::endl;
break;
case 'm':
RsInitConfig::startMinimised = true;
std::cerr << "Start Minimised On";
std::cerr << std::endl;
break;
case 'l':
RsInitConfig::logfname = optarg;
std::cerr << "LogFile (" << RsInitConfig::logfname;
std::cerr << ") Selected" << std::endl;
RsInitConfig::haveLogFile = true;
break;
case 'w':
RsInitConfig::passwd = optarg;
std::cerr << "Password Specified(********" ; //<< RsInitConfig::passwd;
std::cerr << ") Selected" << std::endl;
break;
case 'i':
strncpy(RsInitConfig::inet, optarg, 256);
std::cerr << "New Inet Addr(" << RsInitConfig::inet;
std::cerr << ") Selected" << std::endl;
RsInitConfig::forceLocalAddr = true;
break;
case 'p':
RsInitConfig::port = atoi(optarg);
std::cerr << "New Listening Port(" << RsInitConfig::port;
std::cerr << ") Selected" << std::endl;
break;
case 'c':
RsInitConfig::basedir = optarg;
std::cerr << "New Base Config Dir(";
std::cerr << RsInitConfig::basedir;
std::cerr << ") Selected" << std::endl;
break;
case 's':
RsInitConfig::outStderr = true;
RsInitConfig::haveLogFile = false;
std::cerr << "Output to Stderr";
std::cerr << std::endl;
break;
case 'd':
RsInitConfig::haveDebugLevel = true;
RsInitConfig::debugLevel = atoi(optarg);
std::cerr << "Opt for new Debug Level";
std::cerr << std::endl;
break;
case 'u':
RsInitConfig::udpListenerOnly = true;
std::cerr << "Opt for only udpListener";
std::cerr << std::endl;
break;
case 'e':
RsInitConfig::forceExtPort = true;
std::cerr << "Opt for External Port Mode";
std::cerr << std::endl;
break;
case 'U':
prefUserString = optarg;
std::cerr << "Opt for User Id ";
std::cerr << std::endl;
break;
case 'r':
RsInitConfig::RetroShareLink = optarg;
std::cerr << "Opt for RetroShare link";
std::cerr << std::endl;
break;
#ifdef LOCALNET_TESTING
case 'R':
portRestrictions = optarg;
doPortRestrictions = true;
std::cerr << "Opt for Port Restrictions";
std::cerr << std::endl;
break;
#endif
case 'h':
std::cerr << "Help: " << std::endl;
std::cerr << "The commandline options are for retroshare-nogui, a headless server in a shell, or systems without QT." << std::endl << std::endl;
@ -510,32 +449,31 @@ int RsInit::InitRetroShare(int argcIgnored, char **argvIgnored, bool strictCheck
}
}
}
#endif
setOutputLevel(RsInitConfig::debugLevel);
// set the default Debug Level...
if (RsInitConfig::haveDebugLevel)
{
if ((RsInitConfig::debugLevel > 0) &&
(RsInitConfig::debugLevel <= PQL_DEBUG_ALL))
{
std::cerr << "Setting Debug Level to: ";
std::cerr << RsInitConfig::debugLevel;
std::cerr << std::endl;
setOutputLevel(RsInitConfig::debugLevel);
}
else
{
std::cerr << "Ignoring Invalid Debug Level: ";
std::cerr << RsInitConfig::debugLevel;
std::cerr << std::endl;
}
}
// // set the default Debug Level...
// if (RsInitConfig::haveDebugLevel)
// {
// if ((RsInitConfig::debugLevel > 0) &&
// (RsInitConfig::debugLevel <= PQL_DEBUG_ALL))
// {
// std::cerr << "Setting Debug Level to: ";
// std::cerr << RsInitConfig::debugLevel;
// std::cerr << std::endl;
// }
// else
// {
// std::cerr << "Ignoring Invalid Debug Level: ";
// std::cerr << RsInitConfig::debugLevel;
// std::cerr << std::endl;
// }
// }
// set the debug file.
if (RsInitConfig::haveLogFile)
{
setDebugFile(RsInitConfig::logfname.c_str());
}
/******************************** WINDOWS/UNIX SPECIFIC PART ******************/
#ifndef WINDOWS_SYS
@ -2599,7 +2537,7 @@ int RsServer::StartupRetroShare()
laddr.sin_port = htons(RsInitConfig::port);
// universal
laddr.sin_addr.s_addr = inet_addr(RsInitConfig::inet);
laddr.sin_addr.s_addr = inet_addr(RsInitConfig::inet.c_str());
mPeerMgr->setLocalAddress(ownId, laddr);
}

View File

@ -1,814 +0,0 @@
/* Copyright (C) 2004 Xavier Décoret <Xavier.Decoret@imag.fr>
*
* 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 <stdlib.h>
#include <string>
#include <list>
#include <deque>
#include <map>
#include <stdexcept>
#include <sstream>
#include <iostream>
namespace
{
class argstream;
template<class T>
class ValueHolder;
template <typename T>
argstream& operator>> (argstream&, const ValueHolder<T>&);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of ValueHolder<T>
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
template<class T>
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<T>& 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 <class T>
inline ValueHolder<T>
parameter(char s,
const char* l,
T& b,
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(s,l,b,desc,mandatory);
}
template <class T>
inline ValueHolder<T>
parameter(char s,
T& b,
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(s,b,desc,mandatory);
}
template <class T>
inline ValueHolder<T>
parameter(const char* l,
T& b,
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(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 T,class O>
class ValuesHolder
{
public:
ValuesHolder(const O& o,
const char* desc,
int len);
template<class A,class B> friend argstream& operator>>(argstream& s,const ValuesHolder<A,B>& 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<class T,class O>
inline ValuesHolder<T,O>
values(const O& o,
const char* desc="",
int len=-1)
{
return ValuesHolder<T,O>(o,desc,len);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of ValueParser
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
template <class T>
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<std::string>
{
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<class T>
friend argstream& operator>>(argstream& s,const ValueHolder<T>& v);
friend inline argstream& operator>>(argstream& s,const OptionHolder& v);
template<class T,class O>
friend argstream& operator>>(argstream& s,const ValuesHolder<T,O>& 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<std::string>::iterator value_iterator;
typedef std::pair<std::string,std::string> help_entry;
std::string progName_;
std::map<std::string,value_iterator> options_;
std::list<std::string> values_;
bool minusActive_;
bool isOk_;
std::deque<help_entry> argHelps_;
std::string cmdLine_;
std::deque<std::string> errors_;
bool helpRequested_;
};
//************************************************************
// Implementation of ValueHolder<T>
//************************************************************
template<class T>
ValueHolder<T>::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<class T>
ValueHolder<T>::ValueHolder(const char* l,
T& v,
const char* desc,
bool mandatory)
: longName_(l),
value_(&v),
initialValue_(v),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
ValueHolder<T>::ValueHolder(char s,
T& v,
const char* desc,
bool mandatory)
: shortName_(1,s),
value_(&v),
initialValue_(v),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
std::string
ValueHolder<T>::name() const
{
std::ostringstream os;
if (!shortName_.empty()) os<<'-'<<shortName_;
if (!longName_.empty()) {
if (!shortName_.empty()) os<<'/';
os<<"--"<<longName_;
}
return os.str();
}
template<class T>
std::string
ValueHolder<T>::description() const
{
std::ostringstream os;
os<<description_;
if (mandatory_)
{
os<<"(mandatory)";
}
else
{
os<<"(default="<<initialValue_<<")";
}
return os.str();
}
//************************************************************
// Implementation of OptionHolder
//************************************************************
inline OptionHolder::OptionHolder(char s,
const char* l,
bool& b,
const char* desc)
: shortName_(1,s),
longName_(l),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(const char* l,
bool& b,
const char* desc)
: longName_(l),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(char s,
bool& b,
const char* desc)
: shortName_(1,s),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(char s,
const char* l,
const char* desc)
: shortName_(1,s),
longName_(l),
value_(NULL),
description_(desc)
{
}
inline std::string
OptionHolder::name() const
{
std::ostringstream os;
if (!shortName_.empty()) os<<'-'<<shortName_;
if (!longName_.empty())
{
if (!shortName_.empty()) os<<'/';
os<<"--"<<longName_;
}
return os.str();
}
inline std::string
OptionHolder::description() const
{
return description_;
}
//************************************************************
// Implementation of ValuesHolder<T,O>
//************************************************************
template<class T,class O>
ValuesHolder<T,O>::ValuesHolder(const O& o,
const char* desc,
int len)
: value_(o),
description_(desc),
len_(len)
{
letter_ = argstream::uniqueLetter();
}
template <class T,class O>
std::string
ValuesHolder<T,O>::name() const
{
std::ostringstream os;
os<<letter_<<"i";
return os.str();
}
template <class T,class O>
std::string
ValuesHolder<T,O>::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<std::string> 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<std::string>::const_iterator
iter = args.begin();
iter != args.end();++iter)
{
*p++ = const_cast<char*>(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 "<<a;
errors_.push_back(os.str());
break;
}
lastOption = &(options_[std::string(1,*cter)] = values_.end());
}
}
else
{
isOk_ = false;
errors_.push_back("Invalid argument -");
break;
}
}
}
else
{
values_.push_back(s);
if (lastOption != NULL)
{
*lastOption = --values_.end();
}
lastOption = NULL;
}
}
#ifdef ARGSTREAM_DEBUG
for (std::map<std::string,value_iterator>::const_iterator
iter = options_.begin();iter != options_.end();++iter)
{
std::cout<<"DEBUG: option "<<iter->first;
if (iter->second != values_.end())
{
std::cout<<" -> "<<*(iter->second);
}
std::cout<<std::endl;
}
for (std::list<std::string>::const_iterator
iter = values_.begin();iter != values_.end();++iter)
{
std::cout<<"DEBUG: value "<<*iter<<std::endl;
}
#endif // ARGSTREAM_DEBUG
}
inline bool
argstream::isOk() const
{
return isOk_;
}
inline bool
argstream::helpRequested() const
{
return helpRequested_;
}
inline std::string
argstream::usage() const
{
std::ostringstream os;
os<<"usage: "<<progName_<<cmdLine_<<'\n';
unsigned int lmax = 0;
for (std::deque<help_entry>::const_iterator
iter = argHelps_.begin();iter != argHelps_.end();++iter)
{
if (lmax<iter->first.size()) lmax = iter->first.size();
}
for (std::deque<help_entry>::const_iterator
iter = argHelps_.begin();iter != argHelps_.end();++iter)
{
os<<'\t'<<iter->first<<std::string(lmax-iter->first.size(),' ')
<<" : "<<iter->second<<'\n';
}
return os.str();
}
inline std::string
argstream::errorLog() const
{
std::string s;
for(std::deque<std::string>::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<class T>
argstream&
operator>>(argstream& s,const ValueHolder<T>& 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 "<<v.shortName_<<" "<<v.longName_<<std::endl;
#endif
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
if (v.mandatory_)
{
if (!v.shortName_.empty())
{
s.cmdLine_ += " -";
s.cmdLine_ += v.shortName_;
}
else
{
s.cmdLine_ += " --";
s.cmdLine_ += v.longName_;
}
s.cmdLine_ += " value";
}
else
{
if (!v.shortName_.empty())
{
s.cmdLine_ += " [-";
s.cmdLine_ += v.shortName_;
}
else
{
s.cmdLine_ += " [--";
s.cmdLine_ += v.longName_;
}
s.cmdLine_ += " value]";
}
std::map<std::string,argstream::value_iterator>::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)<<std::endl;
#endif
ValueParser<T> 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<std::string,argstream::value_iterator>::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 "<<iter->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<<'-'<<v.shortName_;
if (!v.longName_.empty())
{
if (!v.shortName_.empty()) os<<'/';
os<<"--"<<v.longName_;
}
os<<" missing";
s.errors_.push_back(os.str());
}
}
return s;
}
inline argstream&
operator>>(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 "<<v.shortName_<<" "<<v.longName_<<std::endl;
#endif
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
{
std::string c;
if (!v.shortName_.empty())
{
c += " [-";
c += v.shortName_;
}
else
{
c += " [--";
c += v.longName_;
}
c += "]";
s.cmdLine_ = c+s.cmdLine_;
}
std::map<std::string,argstream::value_iterator>::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<class T,class O>
argstream&
operator>>(argstream& s,const ValuesHolder<T,O>& v)
{
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
{
std::ostringstream os;
os<<' '<<v.letter_<<'1';
switch (v.len_)
{
case -1:
os<<"...";
break;
case 1:
break;
default:
os<<"..."<<v.letter_<<v.len_;
break;
}
s.cmdLine_ += os.str();
}
std::list<std::string>::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<T> 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<std::string,argstream::value_iterator>::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 "<<v.len_<<" values";
s.errors_.push_back(os.str());
}
// Erase the values parsed
s.values_.erase(s.values_.begin(),first);
return s;
}
inline void
argstream::defaultErrorHandling(bool ignoreUnused) const
{
if (helpRequested_)
{
std::cout<<usage();
exit(1);
}
if (!isOk_)
{
std::cerr<<errorLog();
exit(1);
}
if (!ignoreUnused &&
(!values_.empty() || !options_.empty()))
{
std::cerr<<"Unused arguments"<<std::endl;
exit(1);
}
}
};
#endif // ARGSTREAM_H

View File

@ -36,7 +36,7 @@
#include "util/rsdir.h"
#include "util/utest.h"
#include <common/testutils.h>
#include <common/argstream.h>
#include <util/argstream.h>
INITTEST() ;

View File

@ -2,7 +2,7 @@
#include <string.h>
#include <util/utest.h>
#include <common/argstream.h>
#include <util/argstream.h>
//#include <pqi/cleanupxpgp.h>
#include <retroshare/rspeers.h>
#include <pgp/rscertificate.h>

View File

@ -11,7 +11,7 @@ extern "C"
}
#include <util/utest.h>
#include <common/argstream.h>
#include <util/argstream.h>
INITTEST() ;

View File

@ -6,7 +6,7 @@
#include <pgp/pgphandler.h>
#include <util/utest.h>
#include <util/rsdir.h>
#include <common/argstream.h>
#include <util/argstream.h>
INITTEST() ;

View File

@ -28,7 +28,7 @@
#include "util/rsaes.h"
#include "util/utest.h"
#include <common/argstream.h>
#include <util/argstream.h>
#include <iostream>
#include <list>

View File

@ -28,7 +28,7 @@
#include "util/rsdir.h"
#include "util/utest.h"
#include <common/argstream.h>
#include <util/argstream.h>
#include <iostream>
#include <list>

View File

@ -0,0 +1,839 @@
/* Copyright (C) 2004 Xavier Décoret <Xavier.Decoret@imag.fr>
*
* 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 <stdlib.h>
#include <string>
#include <list>
#include <deque>
#include <map>
#include <stdexcept>
#include <sstream>
#include <iostream>
namespace
{
class argstream;
template<class T>
class ValueHolder;
template <typename T>
argstream& operator>> (argstream&, const ValueHolder<T>&);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of ValueHolder<T>
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
template<class T>
class ValueHolder
{
public:
ValueHolder(char s,
const char* l,
T& b,
const char *valueName,
const char* desc,
bool mandatory);
ValueHolder(const char* l,
T& b,
const char *valueName,
const char* desc,
bool mandatory);
ValueHolder(char s,
T& b,
const char *valueName,
const char* desc,
bool mandatory);
friend argstream& operator>><>(argstream& s,const ValueHolder<T>& v);
std::string name() const;
std::string description() const;
private:
std::string shortName_;
std::string longName_;
std::string valueName_;
T* value_;
T initialValue_;
std::string description_;
bool mandatory_;
};
template <class T>
inline ValueHolder<T>
parameter(char s,
const char* l,
T& b,
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(s,l,b,"value",desc,mandatory);
}
template <class T>
inline ValueHolder<T>
parameter(char s,
const char* l,
T& b,
const char* valueName="value",
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(s,l,b,valueName,desc,mandatory);
}
template <class T>
inline ValueHolder<T>
parameter(char s,
T& b,
const char* valueName="value",
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(s,b,valueName,desc,mandatory);
}
template <class T>
inline ValueHolder<T>
parameter(const char* l,
T& b,
const char* valueName="value",
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(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 T,class O>
class ValuesHolder
{
public:
ValuesHolder(const O& o,
const char* desc,
int len);
template<class A,class B> friend argstream& operator>>(argstream& s,const ValuesHolder<A,B>& 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<class T,class O>
inline ValuesHolder<T,O>
values(const O& o,
const char* desc="",
int len=-1)
{
return ValuesHolder<T,O>(o,desc,len);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of ValueParser
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
template <class T>
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<std::string>
{
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<class T>
friend argstream& operator>>(argstream& s,const ValueHolder<T>& v);
friend inline argstream& operator>>(argstream& s,const OptionHolder& v);
template<class T,class O>
friend argstream& operator>>(argstream& s,const ValuesHolder<T,O>& v);
inline bool helpRequested() const;
inline bool isOk() const;
inline std::string errorLog() const;
inline std::string usage() const;
inline bool defaultErrorHandling(bool ignoreUnused=false) const;
static inline char uniqueLetter();
protected:
void parse(int argc,char** argv);
private:
typedef std::list<std::string>::iterator value_iterator;
typedef std::pair<std::string,std::string> help_entry;
std::string progName_;
std::map<std::string,value_iterator> options_;
std::list<std::string> values_;
bool minusActive_;
bool isOk_;
std::deque<help_entry> argHelps_;
std::string cmdLine_;
std::deque<std::string> errors_;
bool helpRequested_;
};
//************************************************************
// Implementation of ValueHolder<T>
//************************************************************
template<class T>
ValueHolder<T>::ValueHolder(char s,
const char* l,
T& v,
const char* valueName,
const char* desc,
bool mandatory)
: shortName_(1,s),
longName_(l),
valueName_(valueName),
value_(&v),
initialValue_(v),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
ValueHolder<T>::ValueHolder(const char* l,
T& v,
const char* valueName,
const char* desc,
bool mandatory)
: longName_(l),
value_(&v),
valueName_(valueName),
initialValue_(v),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
ValueHolder<T>::ValueHolder(char s,
T& v,
const char* valueName,
const char* desc,
bool mandatory)
: shortName_(1,s),
value_(&v),
initialValue_(v),
valueName_(valueName),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
std::string
ValueHolder<T>::name() const
{
std::ostringstream os;
if (!shortName_.empty()) os<<'-'<<shortName_;
if (!longName_.empty()) {
if (!shortName_.empty()) os<<'/';
os<<"--"<<longName_;
}
return os.str();
}
template<class T>
std::string
ValueHolder<T>::description() const
{
std::ostringstream os;
os<<description_;
if (mandatory_)
{
os<<"(mandatory)";
}
else
{
os<<"(default="<<initialValue_<<")";
}
return os.str();
}
//************************************************************
// Implementation of OptionHolder
//************************************************************
inline OptionHolder::OptionHolder(char s,
const char* l,
bool& b,
const char* desc)
: shortName_(1,s),
longName_(l),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(const char* l,
bool& b,
const char* desc)
: longName_(l),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(char s,
bool& b,
const char* desc)
: shortName_(1,s),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(char s,
const char* l,
const char* desc)
: shortName_(1,s),
longName_(l),
value_(NULL),
description_(desc)
{
}
inline std::string
OptionHolder::name() const
{
std::ostringstream os;
if (!shortName_.empty()) os<<'-'<<shortName_;
if (!longName_.empty())
{
if (!shortName_.empty()) os<<'/';
os<<"--"<<longName_;
}
return os.str();
}
inline std::string
OptionHolder::description() const
{
return description_;
}
//************************************************************
// Implementation of ValuesHolder<T,O>
//************************************************************
template<class T,class O>
ValuesHolder<T,O>::ValuesHolder(const O& o,
const char* desc,
int len)
: value_(o),
description_(desc),
len_(len)
{
letter_ = argstream::uniqueLetter();
}
template <class T,class O>
std::string
ValuesHolder<T,O>::name() const
{
std::ostringstream os;
os<<letter_<<"i";
return os.str();
}
template <class T,class O>
std::string
ValuesHolder<T,O>::description() const
{
return description_;
}
//************************************************************
// Implementation of argstream
//************************************************************
inline
argstream::argstream(int argc,char** argv)
: progName_(argv[0]),
minusActive_(true),
isOk_(true),
helpRequested_(false)
{
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<std::string> 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<std::string>::const_iterator
iter = args.begin();
iter != args.end();++iter)
{
*p++ = const_cast<char*>(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 "<<a;
errors_.push_back(os.str());
break;
}
lastOption = &(options_[std::string(1,*cter)] = values_.end());
}
}
else
{
isOk_ = false;
errors_.push_back("Invalid argument -");
break;
}
}
}
else
{
values_.push_back(s);
if (lastOption != NULL)
{
*lastOption = --values_.end();
}
lastOption = NULL;
}
}
#ifdef ARGSTREAM_DEBUG
for (std::map<std::string,value_iterator>::const_iterator
iter = options_.begin();iter != options_.end();++iter)
{
std::cout<<"DEBUG: option "<<iter->first;
if (iter->second != values_.end())
{
std::cout<<" -> "<<*(iter->second);
}
std::cout<<std::endl;
}
for (std::list<std::string>::const_iterator
iter = values_.begin();iter != values_.end();++iter)
{
std::cout<<"DEBUG: value "<<*iter<<std::endl;
}
#endif // ARGSTREAM_DEBUG
}
inline bool
argstream::isOk() const
{
return isOk_;
}
inline bool
argstream::helpRequested() const
{
return helpRequested_;
}
inline std::string
argstream::usage() const
{
std::ostringstream os;
os<<"usage: "<<progName_<<cmdLine_<<'\n';
unsigned int lmax = 0;
for (std::deque<help_entry>::const_iterator
iter = argHelps_.begin();iter != argHelps_.end();++iter)
{
if (lmax<iter->first.size()) lmax = iter->first.size();
}
for (std::deque<help_entry>::const_iterator
iter = argHelps_.begin();iter != argHelps_.end();++iter)
{
os<<'\t'<<iter->first<<std::string(lmax-iter->first.size(),' ')
<<" : "<<iter->second<<'\n';
}
return os.str();
}
inline std::string
argstream::errorLog() const
{
std::string s;
for(std::deque<std::string>::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<class T>
argstream&
operator>>(argstream& s,const ValueHolder<T>& 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 "<<v.shortName_<<" "<<v.longName_<<std::endl;
#endif
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
if (v.mandatory_)
{
if (!v.shortName_.empty())
{
s.cmdLine_ += " -";
s.cmdLine_ += v.shortName_;
}
else
{
s.cmdLine_ += " --";
s.cmdLine_ += v.longName_;
}
s.cmdLine_ += " " + v.valueName_;
}
else
{
if (!v.shortName_.empty())
{
s.cmdLine_ += " [-";
s.cmdLine_ += v.shortName_;
}
else
{
s.cmdLine_ += " [--";
s.cmdLine_ += v.longName_;
}
s.cmdLine_ += " " + v.valueName_ +"]";
}
std::map<std::string,argstream::value_iterator>::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)<<std::endl;
#endif
ValueParser<T> 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<std::string,argstream::value_iterator>::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 "<<iter->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<<'-'<<v.shortName_;
if (!v.longName_.empty())
{
if (!v.shortName_.empty()) os<<'/';
os<<"--"<<v.longName_;
}
os<<" missing";
s.errors_.push_back(os.str());
}
}
return s;
}
inline argstream&
operator>>(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 "<<v.shortName_<<" "<<v.longName_<<std::endl;
#endif
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
{
std::string c;
if (!v.shortName_.empty())
{
c += " [-";
c += v.shortName_;
}
else
{
c += " [--";
c += v.longName_;
}
c += "]";
s.cmdLine_ = c+s.cmdLine_;
}
std::map<std::string,argstream::value_iterator>::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<class T,class O>
argstream&
operator>>(argstream& s,const ValuesHolder<T,O>& v)
{
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
{
std::ostringstream os;
os<<' '<<v.letter_<<'1';
switch (v.len_)
{
case -1:
os<<"...";
break;
case 1:
break;
default:
os<<"..."<<v.letter_<<v.len_;
break;
}
s.cmdLine_ += os.str();
}
std::list<std::string>::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<T> 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<std::string,argstream::value_iterator>::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 "<<v.len_<<" values";
s.errors_.push_back(os.str());
}
// Erase the values parsed
s.values_.erase(s.values_.begin(),first);
return s;
}
inline bool
argstream::defaultErrorHandling(bool ignoreUnused) const
{
if (helpRequested_)
{
std::cout<<usage();
exit(1);
}
if (!isOk_)
{
std::cerr<<errorLog();
exit(1);
}
if (!ignoreUnused &&
(!values_.empty() || !options_.empty()))
{
std::cerr<<"Unused arguments"<<std::endl;
exit(1);
}
return true ;
}
};
#endif // ARGSTREAM_H

View File

@ -101,9 +101,9 @@ bool NotifyTxt::askForPluginConfirmation(const std::string& plugin_file_name, co
return a == 'y' ;
}
bool NotifyTxt::askForPassword(const std::string& key_details, bool prev_is_bad, std::string& password)
bool NotifyTxt::askForPassword(const std::string& question, bool prev_is_bad, std::string& password)
{
char *passwd = getpass(("Please enter GPG password for key "+key_details+": ").c_str()) ;
char *passwd = getpass(question.c_str()) ;
password = passwd;
return !password.empty();

View File

@ -42,7 +42,7 @@ class NotifyTxt: public NotifyBase
virtual void notifyListChange(int list, int type);
virtual void notifyErrorMsg(int list, int sev, std::string msg);
virtual void notifyChat();
virtual bool askForPassword(const std::string& key_details, bool prev_is_bad, std::string& password);
virtual bool askForPassword(const std::string& question, bool prev_is_bad, std::string& password);
virtual bool askForPluginConfirmation(const std::string& plugin_file, const std::string& plugin_hash);
virtual void notifyTurtleSearchResult(uint32_t search_id,const std::list<TurtleFileInfo>& found_files);

View File

@ -31,6 +31,7 @@
#include "notifytxt.h"
#include <unistd.h>
#include <util/argstream.h>
#include <iostream>
#ifdef WINDOWS_SYS
#include <winsock2.h>
@ -53,6 +54,10 @@
#endif
#ifdef RS_SSH_SERVER
void generatePasswordHash() ;
#endif
/* Basic instructions for running libretroshare as background thread.
* ******************************************************************* *
* This allows your program to communicate with authenticated peers.
@ -102,7 +107,6 @@ int main(int argc, char **argv)
bool enableRpc = false;
bool enableSsh = false;
bool enableSshHtml = false;
bool enableSshPwd = false;
bool enableTerminal = false;
bool enableSshRsa = false;
bool genPwdHash = false;
@ -112,167 +116,75 @@ int main(int argc, char **argv)
std::string sshPortStr = "0";
uint16_t extPort = 0;
uint16_t sshPort = 7022;
bool extPortSet = false;
bool displayRPCInfo = false ;
while((c = getopt(argc, argv,"ChTL:P:K:GS:E:")) != -1)
{
switch(c)
{
case 'C':
enableRpc = true;
strictCheck = false;
break;
case 'S':
enableSsh = true;
sshPortStr = optarg; // no longer optional.
strictCheck = false;
break;
case 'E':
extPort = atoi(optarg);
extPortSet = true;
strictCheck = false;
break;
case 'H':
enableSshHtml = true;
strictCheck = false;
break;
case 'T':
enableTerminal = true;
strictCheck = false;
break;
case 'L':
sshUser = optarg;
strictCheck = false;
break;
case 'P':
enableSshPwd = true;
sshPwdHash = optarg;
strictCheck = false;
break;
#if 0 // NOT FINISHED YET.
case 'K':
enableSshRsa = true;
sshRsaFile = optarg;
strictCheck = false;
break;
argstream as(argc,argv) ;
as >> option('X',"enable-ssh" ,enableSsh ,"Enable SSH" )
>> option('T',"enable-terminal",enableTerminal ,"Enable terminal interface." )
>> option('C',"enable-rpc" ,enableRpc ,"Enable RPC protocol. To be used with e.g. -X (SSH).")
>> option('G',"gen-password" ,genPwdHash ,"Generate password hash (to supply to option -P)")
#if 0
>> option('H',"enable-ssh-html",enableSshHtml ,"Enable SSH html." )
#endif
case 'G':
genPwdHash = true;
break;
case 'h':
/* nogui help */
std::cerr << argv[0] << std::endl;
std::cerr << "Specific Help Options: " << std::endl;
std::cerr << "\t-G Generate a Password Hash for SSH Server" << std::endl;
std::cerr << "\t-T Enable Terminal Interface" << std::endl;
std::cerr << "\t-S <port> Enable SSH Server on specified port" << std::endl;
std::cerr << "\t-E <port> Specify Alternative External Port (provided to Clients)" << std::endl;
std::cerr << "\t-L <user> Specify SSH login user (default:user)" << std::endl;
std::cerr << "\t-P <pwdhash> Enable SSH login via Password" << std::endl;
std::cerr << "\t-C Enable RPC Protocol (requires -S too)" << std::endl;
//std::cerr << "\t-K [rsapubkeyfile] Enable SSH login via RSA key" << std::endl;
//std::cerr << "\t NB: Two Factor Auth, specify both -P & -K" << std::endl;
std::cerr << std::endl;
std::cerr << "\t To setup rs-nogui as a SSH Server is a three step process: " << std::endl;
std::cerr << "\t 1) \"ssh-keygen -t rsa -f rs_ssh_host_rsa_key\" " << std::endl;
std::cerr << "\t 2) \"./retroshare-nogui -G\" " << std::endl;
std::cerr << "\t 3) \"./retroshare-nogui -S [port] -L <user> -P <passwordhash>\" " << std::endl;
std::cerr << std::endl;
std::cerr << "Further Options ";
/* libretroshare will call exit(1) after printing its options */
break;
>> parameter('S',"ssh-port" ,sshPort ,"port" ,"SSH port to contact the interface.",false)
>> parameter('E',"ext-port" ,extPort ,"port" ,"Specify Alternative External Port (provided to Clients)",false)
>> parameter('L',"ssh-user" ,sshUser ,"name" ,"Ssh login user",false)
>> parameter('P',"ssh-p-hash" ,sshPwdHash ,"hash" ,"Ssh login password hash (Generated by retroshare-nogui -G)",false)
>> parameter('K',"ssh-key-file" ,sshRsaFile ,"RSA key file", "RSA key file for SSH login (not yet implemented).",false )// NOT FINISHED YET.
default:
/* let others through - for libretroshare */
break;
}
>> help() ;
// Normally argstream would handle this by itself, if we called
// as.defaultErrorHandling() ;
//
// but we have other parameter handling functions after, so we don't want to quit if help is requested.
//
if (as.helpRequested())
{
std::cerr << "\nSpecific Help Options:" << std::endl;
std::cerr << as.usage() << std::endl;
std::cerr << "\t To setup rs-nogui as a SSH Server is a three step process: " << std::endl;
std::cerr << "\t 1) Generate a RSA keypair in the current directory: \"ssh-keygen -t rsa -f rs_ssh_host_rsa_key\" " << std::endl;
std::cerr << "\t 2) Generate a password hash for the RPC login: \"./retroshare-nogui -G\" " << std::endl;
std::cerr << "\t 3) Launch the RS with remote control enabled: \"./retroshare-nogui -X/-T [-C] -S [port] -L <user> -P <passwordhash>\" " << std::endl;
std::cerr << "\nAdditional options: \n" << std::endl;
}
if (!as.isOk())
{
std::cerr << as.errorLog();
return 1;
}
// reset optind for Retroshare commandline arguments.
optind = 1;
if (genPwdHash)
{
std::string saltBin;
std::string pwdHashRadix64;
std::string sshPwdForHash = "";
std::cout << "Type in your Password:" << std::flush;
char pwd[1024];
if (!fgets(pwd, 1024, stdin))
{
std::cerr << "Error Reading Password";
std::cerr << std::endl;
exit(1);
}
// strip newline.
for(int i = 0; (i < 1024) && (pwd[i] != '\n') && (pwd[i] != '\0'); i++)
{
sshPwdForHash += pwd[i];
}
std::cerr << "Chosen Password : " << sshPwdForHash;
std::cerr << std::endl;
GenerateSalt(saltBin);
if (!GeneratePasswordHash(saltBin, sshPwdForHash, pwdHashRadix64))
{
std::cerr << "Error Generating Password Hash, password probably too short";
std::cerr << pwdHashRadix64;
std::cerr << std::endl;
exit(1);
}
std::cout << "Generated Password Hash for rs-nogui: ";
std::cout << pwdHashRadix64;
std::cout << std::endl;
std::cout << std::endl;
/* checking match */
if (CheckPasswordHash(pwdHashRadix64, sshPwdForHash))
{
std::cerr << "Passed Check Okay!";
std::cerr << std::endl;
}
else
{
std::cerr << "ERROR: Failed CheckPassword!";
std::cerr << std::endl;
exit(1);
}
std::cerr << "Usage:";
std::cerr << std::endl;
std::cerr << " - for SSH access: ./retroshare-nogui -S [port] -L <username> -P " << pwdHashRadix64;
std::cerr << std::endl;
std::cerr << " - for RPC access: ./retroshare-nogui -C -S [port] -L <username> -P " << pwdHashRadix64;
std::cerr << std::endl;
exit(0);
generatePasswordHash() ;
return 0 ;
}
/* enforce conditions */
if (((enableSshRsa) || (enableSshPwd)) && (!enableSsh))
if ((!sshRsaFile.empty() || !sshPwdHash.empty()) && (!enableSsh))
{
std::cerr << "ERROR: SSH Server (-S) must be enabled to specify SSH Pwd (-P) or SSH RSA (-K)";
std::cerr << "ERROR: SSH Server (-X) must be enabled to specify SSH Pwd (-P) or SSH RSA (-K)";
std::cerr << std::endl;
exit(1);
return 1 ;
}
if (enableSsh && (!enableSshRsa) && (!enableSshPwd))
if (enableSsh && (!enableSshRsa) && sshPwdHash.empty())
{
std::cerr << "ERROR: One of (or both) SSH Pwd (-P) and SSH RSA (-K) must be specified with SSH Server (-S)";
std::cerr << "ERROR: One of (or both) SSH Pwd (-P) and SSH RSA (-K) must be specified with SSH Server (-X)";
std::cerr << std::endl;
exit(1);
return 1 ;
}
if (enableRpc && (!enableSsh))
{
std::cerr << "ERROR: RPC Mode (-C) requires SSH Server (-S) enabled";
std::cerr << "ERROR: RPC Mode (-C) requires SSH Server (-X) enabled";
std::cerr << std::endl;
exit(1);
return 1 ;
}
@ -291,12 +203,8 @@ int main(int argc, char **argv)
}
if (enableSshPwd)
{
/* try parse it */
/* TODO */
}
#else
std::cerr << "\nRetroshare command line interface." << std::endl;
#endif
@ -372,7 +280,7 @@ int main(int argc, char **argv)
//ssh->adduser("anrsuser", "test");
}
if (enableSshPwd)
if (!sshPwdHash.empty())
{
ssh->adduserpwdhash(sshUser, sshPwdHash);
}
@ -385,8 +293,6 @@ int main(int argc, char **argv)
// NASTY GLOBAL VARIABLE HACK - NEED TO THINK OF A BETTER SYSTEM.
RpcProtoSystem::mExtPort = extPort;
}
#endif
/* Start-up libretroshare server threads */
@ -470,3 +376,71 @@ int main(int argc, char **argv)
}
return 1;
}
#ifdef RS_SSH_SERVER
void generatePasswordHash()
{
std::string saltBin;
std::string pwdHashRadix64;
std::string sshPwdForHash = "";
std::string passwd1,passwd2 ;
if(!NotifyTxt().askForPassword("Type your password (at least 8 chars) : ",false,passwd1)) exit(1) ;
if(passwd1.length() < 8)
{
std::cerr << "Password must be at least 8 characters long." << std::endl;
exit(1);
}
if(!NotifyTxt().askForPassword("Type your password (checking) : ",false,passwd2)) exit(1) ;
if(passwd1 != passwd2)
{
std::cerr << "Passwords differ. Please retry." << std::endl;
exit(1);
}
sshPwdForHash = passwd1 ;
//std::cerr << "Chosen Password : " << sshPwdForHash;
std::cerr << std::endl;
GenerateSalt(saltBin);
if (!GeneratePasswordHash(saltBin, sshPwdForHash, pwdHashRadix64))
{
std::cerr << "Error Generating Password Hash, password probably too short";
std::cerr << pwdHashRadix64;
std::cerr << std::endl;
exit(1);
}
std::cout << "Generated Password Hash for rs-nogui: ";
std::cout << pwdHashRadix64;
std::cout << std::endl;
std::cout << std::endl;
/* checking match */
if (CheckPasswordHash(pwdHashRadix64, sshPwdForHash))
{
std::cerr << "Passed Check Okay!";
std::cerr << std::endl;
}
else
{
std::cerr << "ERROR: Failed CheckPassword!";
std::cerr << std::endl;
exit(1);
}
std::cerr << "Usage:";
std::cerr << std::endl;
std::cerr << " - for SSH access: ./retroshare-nogui -X -S [port] -L <username> -P " << pwdHashRadix64;
std::cerr << std::endl;
std::cerr << " - for RPC access: ./retroshare-nogui -C -S [port] -L <username> -P " << pwdHashRadix64;
std::cerr << std::endl;
}
#endif