diff --git a/src/Arg.cpp b/src/Arg.cpp index 4070bfe..45e6ecd 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -43,10 +43,12 @@ Arg::Arg( const string& flag, _name(name), _description(desc), _required(req), + _requireLabel("required"), _valueRequired(valreq), _alreadySet(false), _visitor( v ), - _ignoreable(true) + _ignoreable(true), + _xorSet(false) { if ( _flag.length() > 1 ) throw(ArgException("Argument flag can only be one character long", @@ -59,8 +61,12 @@ Arg::Arg() _name(""), _description(""), _required(false), + _requireLabel("required"), _valueRequired(false), - _alreadySet(false) + _alreadySet(false), + _visitor( NULL ), + _ignoreable(false), + _xorSet(false) { }; Arg::Arg(const Arg& a) @@ -69,8 +75,12 @@ Arg::Arg(const Arg& a) _name(a._name), _description(a._description), _required(a._required), + _requireLabel(a._requireLabel), _valueRequired(a._valueRequired), - _alreadySet(a._alreadySet) + _alreadySet(a._alreadySet), + _visitor( a._visitor ), + _ignoreable(a._ignoreable), + _xorSet(a._xorSet) { }; Arg::~Arg() { }; @@ -83,8 +93,12 @@ Arg& Arg::operator=(const Arg& a) _name = a._name; _description = a._description; _required = a._required; + _requireLabel = a._requireLabel; _valueRequired = a._valueRequired; _alreadySet = a._alreadySet; + _visitor = a._visitor; + _ignoreable = a._ignoreable; + _xorSet = a._xorSet; } return *this; }; @@ -149,7 +163,7 @@ string Arg::getDescription() const { string desc = ""; if ( _required ) - desc = "(required) "; + desc = "(" + _requireLabel + ") "; if ( _valueRequired ) desc += "(value required) "; @@ -161,9 +175,22 @@ string Arg::getDescription() const const string& Arg::getFlag() const { return _flag; }; bool Arg::isRequired() const { return _required; } bool Arg::isValueRequired() const { return _valueRequired; } -bool Arg::isAlreadySet() const { return _alreadySet; } + +bool Arg::isSet() const +{ + if ( _alreadySet && !_xorSet ) + return true; + else + return false; +} + bool Arg::isIgnoreable() const { return _ignoreable; } +void Arg::setRequireLabel( const string& s) +{ + _requireLabel = s; +} + bool Arg::argMatches( const string& argFlag ) const { if ( argFlag == Arg::flagStartString + _flag || @@ -218,4 +245,15 @@ bool Arg::_hasBlanks( const string& s ) const return false; } +void Arg::forceRequired() +{ + _required = true; +} + +void Arg::xorSet() +{ + _alreadySet = true; + _xorSet = true; +} + } diff --git a/src/CmdLine.cpp b/src/CmdLine.cpp index a72a3ed..1fe1391 100644 --- a/src/CmdLine.cpp +++ b/src/CmdLine.cpp @@ -20,7 +20,7 @@ *****************************************************************************/ -#include +#include namespace TCLAP { @@ -62,23 +62,50 @@ void CmdLine::_constructor() "Ignores the rest of the labeled arguments following this flag.", false, new IgnoreRestVisitor() ); add( *ignore ); + +} + +void CmdLine::xorAdd( vector& ors ) +{ + _xorHandler.add( ors ); + + for (ArgVectorIterator it = ors.begin(); it != ors.end(); it++) + { + (*it)->forceRequired(); + (*it)->setRequireLabel( "OR required" ); + + add( *it ); + } +} + +void CmdLine::xorAdd( Arg& a, Arg& b ) +{ + vector ors; + ors.push_back( &a ); + ors.push_back( &b ); + xorAdd( ors ); } void CmdLine::add( Arg& a ) { - if ( find(_argList.begin(),_argList.end(), &a) != _argList.end() ) + add( &a ); +} + +void CmdLine::add( Arg* a ) +{ + if ( find(_argList.begin(),_argList.end(), a) != _argList.end() ) { cerr << "ADD ERROR: Argument with same flag/name already exists: " - << a.toString() << " Ignoring!" << endl; + << a->toString() << " Ignoring!" << endl; return; } - if ( a.getFlag() == "" ) - _argList.push_back( &a ); + if ( a->getFlag() == "" ) + _argList.push_back( a ); else - _argList.push_front( &a ); + _argList.push_front( a ); - if ( a.isRequired() ) _numRequired++; + if ( a->isRequired() ) _numRequired++; } @@ -93,14 +120,20 @@ void CmdLine::usage( int exitVal ) { cout << endl << "USAGE: " << endl << endl << " " << _progName ; + _xorHandler.shortUsage(); + for (ArgIterator it = _argList.begin(); it != _argList.end(); it++) - cout << " " << (*it)->shortID(); + if ( !_xorHandler.contains( (*it) ) ) + cout << " " << (*it)->shortID(); cout << endl << endl << "Where: " << endl << endl; + _xorHandler.longUsage(); + for (ArgIterator it = _argList.begin(); it != _argList.end(); it++) - cout << " " << (*it)->longID() << endl << " " - << (*it)->getDescription() << endl << endl; + if ( !_xorHandler.contains( (*it) ) ) + cout << " " << (*it)->longID() << endl << " " + << (*it)->getDescription() << endl << endl; cout << endl << endl << _message << endl << endl; exit( exitVal ); @@ -126,8 +159,7 @@ void CmdLine::parse(int argc, char** argv) { if ( (*it)->processArg( &i, args ) ) { - if ( (*it)->isRequired() ) - requiredCount++; + requiredCount += _xorHandler.check( *it ); matched = true; break; } diff --git a/src/Makefile.am b/src/Makefile.am index f3c87bb..a7a64a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,7 @@ lib_LIBRARIES = libtclap.a -libtclap_a_SOURCES = Arg.cpp CmdLine.cpp SwitchArg.cpp +libtclap_a_SOURCES = Arg.cpp CmdLine.cpp SwitchArg.cpp XorHandler.cpp INCLUDES = -I$(top_srcdir)/include diff --git a/src/XorHandler.cpp b/src/XorHandler.cpp new file mode 100644 index 0000000..e3ee77d --- /dev/null +++ b/src/XorHandler.cpp @@ -0,0 +1,107 @@ + +/****************************************************************************** + * + * file: XorHandler.cpp + * + * Copyright (c) 2003, Michael E. Smoot . + * All rights reverved. + * + * See the file COPYING in the top directory of this distribution for + * more information. + * + * THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + *****************************************************************************/ + + +#include + +namespace TCLAP { + +XorHandler::XorHandler( ) +{ } + +void XorHandler::add( vector& ors ) +{ + _orList.push_back( ors ); +} + +void XorHandler::shortUsage() +{ + string out = ""; + for ( int i = 0; (unsigned int)i < _orList.size(); i++ ) + { + out += " {"; + for ( ArgVectorIterator it = _orList[i].begin(); + it != _orList[i].end(); it++ ) + out += (*it)->shortID() + "|"; + + out[out.length()-1] = '}'; + } + + cout << out; +} + +void XorHandler::longUsage() +{ + for ( int i = 0; (unsigned int)i < _orList.size(); i++ ) + { + int orCount = 0; + for ( ArgVectorIterator it = _orList[i].begin(); + it != _orList[i].end(); it++, orCount++ ) + { + cout << " " << (*it)->longID() << endl + << " " << (*it)->getDescription() << endl; + if ( (orCount % 2) == 0 ) + cout << " -- OR -- " << endl; + } + cout << endl << endl; + } +} + +int XorHandler::check( const Arg* a ) +{ + // iterate over each XOR list + for ( int i = 0; (unsigned int)i < _orList.size(); i++ ) + { + // if the XOR list contains the arg.. + if ( find( _orList[i].begin(), _orList[i].end(), a ) != + _orList[i].end() ) + { + // go through and set each arg that is not a + for ( ArgVectorIterator it = _orList[i].begin(); + it != _orList[i].end(); + it++ ) + if ( a != (*it) ) + (*it)->xorSet(); + + // return the number of required args that have now been set + return (int)_orList[i].size(); + } + } + + if ( a->isRequired() ) + return 1; + else + return 0; +} + +bool XorHandler::contains( const Arg* a ) +{ + for ( int i = 0; (unsigned int)i < _orList.size(); i++ ) + for ( ArgVectorIterator it = _orList[i].begin(); + it != _orList[i].end(); + it++ ) + if ( a == (*it) ) + return true; + + return false; +} + +}