text: // -*- Mode: c++; c-basic-offset: 4; tab-width: 4; -*-
  
  /******************************************************************************
   *
   *  file:  CmdLine.h
   *
   *  Copyright (c) 2003, Michael E. Smoot .
   *  Copyright (c) 2004, Michael E. Smoot, Daniel Aarno.
   *  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 State OF CONTRACT, TORT OR OTHERWISE, ARISING
   *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   *  DEALINGS IN THE SOFTWARE.
   *
   *****************************************************************************/
  
  #ifndef TCLAP_CMDLINE_H
  #define TCLAP_CMDLINE_H
  
  #include <tclap/SwitchArg.h>
  #include <tclap/MultiSwitchArg.h>
  #include <tclap/UnlabeledValueArg.h>
  #include <tclap/UnlabeledMultiArg.h>
  
  #include <tclap/XorHandler.h>
  #include <tclap/HelpVisitor.h>
  #include <tclap/VersionVisitor.h>
  #include <tclap/IgnoreRestVisitor.h>
  
  #include <tclap/CmdLineOutput.h>
  #include <tclap/StdOutput.h>
  
  #include <tclap/Constraint.h>
  #include <tclap/ValuesConstraint.h>
  
  #include <string>
  #include <vector>
  #include <list>
  #include <iostream>
  #include <iomanip>
  #include <algorithm>
  #include <stdlib.h> // Needed for exit(), which isn't defined in some envs.
  
  namespace TCLAP {
  
  template<typename T> void DelPtr(T ptr)
  {
  	delete ptr;
  }
  
  template<typename C> void ClearContainer(C &c)
  {
  	typedef typename C::value_type value_type;
  	std::for_each(c.begin(), c.end(), DelPtr<value_type>);
  	c.clear();
  }
  
  
  /**
   * The base class that manages the command line definition and passes
   * along the parsing to the appropriate Arg classes.
   */
  class CmdLine : public CmdLineInterface
  {
  	protected:
  
  		/**
  		 * The list of arguments that will be tested against the
  		 * command line.
  		 */
  		std::list<Arg*> _argList;
  
  		/**
  		 * The name of the program.  Set to argv[0].
  		 */
  		std::string _progName;
  
  		/**
  		 * A message used to describe the program.  Used in the usage output.
  		 */
  		std::string _message;
  
  		/**
  		 * The version to be displayed with the --version switch.
  		 */
  		std::string _version;
  
  		/**
  		 * The number of arguments that are required to be present on
  		 * the command line. This is set dynamically, based on the
  		 * Args added to the CmdLine object.
  		 */
  		int _numRequired;
  
  		/**
  		 * The character that is used to separate the argument flag/name
  		 * from the value.  Defaults to ' ' (space).
  		 */
  		char _delimiter;
  
  		/**
  		 * The handler that manages xoring lists of args.
  		 */
  		XorHandler _xorHandler;
  
  		/**
  		 * A list of Args to be explicitly deleted when the destructor
  		 * is called.  At the moment, this only includes the three default
  		 * Args.
  		 */
  		std::list<Arg*> _argDeleteOnExitList;
  
  		/**
  		 * A list of Visitors to be explicitly deleted when the destructor
  		 * is called.  At the moment, these are the Vistors created for the
  		 * default Args.
  		 */
  		std::list<Visitor*> _visitorDeleteOnExitList;
  
  		/**
  		 * Object that handles all output for the CmdLine.
  		 */
  		CmdLineOutput* _output;
  
  		/**
  		 * Should CmdLine handle parsing exceptions internally?
  		 */
  		bool _handleExceptions;
  
  		/**
  		 * Throws an exception listing the missing args.
  		 */
  		void missingArgsException();
  
  		/**
  		 * Checks whether a name/flag string matches entirely matches
  		 * the Arg::blankChar.  Used when multiple switches are combined
  		 * into a single argument.
  		 * \param s - The message to be used in the usage.
  		 */
  		bool _emptyCombined(const std::string& s);
  
  		/**
  		 * Perform a delete ptr; operation on ptr when this object is deleted.
  		 */
  		void deleteOnExit(Arg* ptr);
  
  		/**
  		 * Perform a delete ptr; operation on ptr when this object is deleted.
  		 */
  		void deleteOnExit(Visitor* ptr);
  
  private:
  
  		/**
  		 * Prevent accidental copying.
  		 */
  		CmdLine(const CmdLine& rhs);
  		CmdLine& operator=(const CmdLine& rhs);
  
  		/**
  		 * Encapsulates the code common to the constructors
  		 * (which is all of it).
  		 */
  		void _constructor();
  
  
  		/**
  		 * Is set to true when a user sets the output object. We use this so
  		 * that we don't delete objects that are created outside of this lib.
  		 */
  		bool _userSetOutput;
  
  		/**
  		 * Whether or not to automatically create help and version switches.
  		 */
  		bool _helpAndVersion;
  
  	public:
  
  		/**
  		 * Command line constructor. Defines how the arguments will be
  		 * parsed.
  		 * \param message - The message to be used in the usage
  		 * output.
  		 * \param delimiter - The character that is used to separate
  		 * the argument flag/name from the value.  Defaults to ' ' (space).
  		 * \param version - The version number to be used in the
  		 * --version switch.
  		 * \param helpAndVersion - Whether or not to create the Help and
  		 * Version switches. Defaults to true.
  		 */
  		CmdLine(const std::string& message,
  				const char delimiter = ' ',
  				const std::string& version = "none",
  				bool helpAndVersion = true);
  
  		/**
  		 * Deletes any resources allocated by a CmdLine object.
  		 */
  		virtual ~CmdLine();
  
  		/**
  		 * Adds an argument to the list of arguments to be parsed.
  		 * \param a - Argument to be added.
  		 */
  		void add( Arg& a );
  
  		/**
  		 * An alternative add.  Functionally identical.
  		 * \param a - Argument to be added.
  		 */
  		void add( Arg* a );
  
  		/**
  		 * Add two Args that will be xor'd.  If this method is used, add does
  		 * not need to be called.
  		 * \param a - Argument to be added and xor'd.
  		 * \param b - Argument to be added and xor'd.
  		 */
  		void xorAdd( Arg& a, Arg& b );
  
  		/**
  		 * Add a list of Args that will be xor'd.  If this method is used,
  		 * add does not need to be called.
  		 * \param xors - List of Args to be added and xor'd.
  		 */
  		void xorAdd( std::vector<Arg*>& xors );
  
  		/**
  		 * Parses the command line.
  		 * \param argc - Number of arguments.
  		 * \param argv - Array of arguments.
  		 */
  		void parse(int argc, const char * const * argv);
  
  		/**
  		 * Parses the command line.
  		 * \param args - A vector of strings representing the args.
  		 * args[0] is still the program name.
  		 */
  		void parse(std::vector<std::string>& args);
  
  		/**
  		 *
  		 */
  		CmdLineOutput* getOutput();
  
  		/**
  		 *
  		 */
  		void setOutput(CmdLineOutput* co);
  
  		/**
  		 *
  		 */
  		std::string& getVersion();
  
  		/**
  		 *
  		 */
  		std::string& getProgramName();
  
  		/**
  		 *
  		 */
  		std::list<Arg*>& getArgList();
  
  		/**
  		 *
  		 */
  		XorHandler& getXorHandler();
  
  		/**
  		 *
  		 */
  		char getDelimiter();
  
  		/**
  		 *
  		 */
  		std::string& getMessage();
  
  		/**
  		 *
  		 */
  		bool hasHelpAndVersion();
  
  		/**
  		 * Disables or enables CmdLine's internal parsing exception handling.
  		 *
  		 * @param state Should CmdLine handle parsing exceptions internally?
  		 */
  		void setExceptionHandling(const bool state);
  
  		/**
  		 * Returns the current state of the internal exception handling.
  		 *
  		 * @retval true Parsing exceptions are handled internally.
  		 * @retval false Parsing exceptions are propagated to the caller.
  		 */
  		bool getExceptionHandling() const;
  
  		/**
  		 * Allows the CmdLine object to be reused.
  		 */
  		void reset();
  
  };
  
  
  ///////////////////////////////////////////////////////////////////////////////
  //Begin CmdLine.cpp
  ///////////////////////////////////////////////////////////////////////////////
  
  inline CmdLine::CmdLine(const std::string& m,
                          char delim,
                          const std::string& v,
                          bool help )
      :
    _argList(std::list<Arg*>()),
    _progName("not_set_yet"),
    _message(m),
    _version(v),
    _numRequired(0),
    _delimiter(delim),
    _xorHandler(XorHandler()),
    _argDeleteOnExitList(std::list<Arg*>()),
    _visitorDeleteOnExitList(std::list<Visitor*>()),
    _output(0),
    _handleExceptions(true),
    _userSetOutput(false),
    _helpAndVersion(help)
  {
  	_constructor();
  }
  
  inline CmdLine::~CmdLine()
  {
  	ClearContainer(_argDeleteOnExitList);
  	ClearContainer(_visitorDeleteOnExitList);
  
  	if ( !_userSetOutput ) {
  		delete _output;
  		_output = 0;
  	}
  }
  
  inline void CmdLine::_constructor()
  {
  	_output = new StdOutput;
  
  	Arg::setDelimiter( _delimiter );
  
  	Visitor* v;
  
  	if ( _helpAndVersion )
  	{
  		v = new HelpVisitor( this, &_output );
  		SwitchArg* help = new SwitchArg("h","help",
  		                      "Displays usage information and exits.",
  		                      false, v);
  		add( help );
  		deleteOnExit(help);
  		deleteOnExit(v);
  
  		v = new VersionVisitor( this, &_output );
  		SwitchArg* vers = new SwitchArg("","version",
  		                      "Displays version information and exits.",
  		                      false, v);
  		add( vers );
  		deleteOnExit(vers);
  		deleteOnExit(v);
  	}
  
  	v = new IgnoreRestVisitor();
  	SwitchArg* ignore  = new SwitchArg(Arg::flagStartString(),
  	          Arg::ignoreNameString(),
  	          "Ignores the rest of the labeled arguments following this flag.",
  	          false, v);
  	add( ignore );
  	deleteOnExit(ignore);
  	deleteOnExit(v);
  }
  
  inline void CmdLine::xorAdd( std::vector<Arg*>& ors )
  {
  	_xorHandler.add( ors );
  
  	for (ArgVectorIterator it = ors.begin(); it != ors.end(); it++)
  	{
  		(*it)->forceRequired();
  		(*it)->setRequireLabel( "OR required" );
  		add( *it );
  	}
  }
  
  inline void CmdLine::xorAdd( Arg& a, Arg& b )
  {
  	std::vector<Arg*> ors;
  	ors.push_back( &a );
  	ors.push_back( &b );
  	xorAdd( ors );
  }
  
  inline void CmdLine::add( Arg& a )
  {
  	add( &a );
  }
  
  inline void CmdLine::add( Arg* a )
  {
  	for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ )
  		if ( *a == *(*it) )
  			throw( SpecificationException(
  			        "Argument with same flag/name already exists!",
  			        a->longID() ) );
  
  	a->addToList( _argList );
  
  	if ( a->isRequired() )
  		_numRequired++;
  }
  
  
  inline void CmdLine::parse(int argc, const char * const * argv)
  {
  		// this step is necessary so that we have easy access to
  		// mutable strings.
  		std::vector<std::string> args;
  		for (int i = 0; i < argc; i++)
  			args.push_back(argv[i]);
  
  		parse(args);
  }
  
  inline void CmdLine::parse(std::vector<std::string>& args)
  {
  	bool shouldExit = false;
  	int estat = 0;
  
  	try {
  		_progName = args.front();
  		args.erase(args.begin());
  
  		int requiredCount = 0;
  
  		for (int i = 0; static_cast<unsigned int>(i) < args.size(); i++) 
  		{
  			bool matched = false;
  			for (ArgListIterator it = _argList.begin();
  			     it != _argList.end(); it++) {
  				if ( (*it)->processArg( &i, args ) )
  				{
  					requiredCount += _xorHandler.check( *it );
  					matched = true;
  					break;
  				}
  			}
  
  			// checks to see if the argument is an empty combined
  			// switch and if so, then we've actually matched it
  			if ( !matched && _emptyCombined( args[i] ) )
  				matched = true;
  
  			if ( !matched && !Arg::ignoreRest() )
  				throw(CmdLineParseException("Couldn't find match "
  				                            "for argument",
  				                            args[i]));
  		}
  
  		if ( requiredCount < _numRequired )
  			missingArgsException();
  
  		if ( requiredCount > _numRequired )
  			throw(CmdLineParseException("Too many arguments!"));
  
  	} catch ( ArgException& e ) {
  		// If we're not handling the exceptions, rethrow.
  		if ( !_handleExceptions) {
  			throw;
  		}
  
  		try {
  			_output->failure(*this,e);
  		} catch ( ExitException &ee ) {
  			estat = ee.getExitStatus();
  			shouldExit = true;
  		}
  	} catch (ExitException &ee) {
  		// If we're not handling the exceptions, rethrow.
  		if ( !_handleExceptions) {
  			throw;
  		}
  
  		estat = ee.getExitStatus();
  		shouldExit = true;
  	}
  
  	if (shouldExit)
  		exit(estat);
  }
  
  inline bool CmdLine::_emptyCombined(const std::string& s)
  {
  	if ( s.length() > 0 && s[0] != Arg::flagStartChar() )
  		return false;
  
  	for ( int i = 1; static_cast<unsigned int>(i) < s.length(); i++ )
  		if ( s[i] != Arg::blankChar() )
  			return false;
  
  	return true;
  }
  
  inline void CmdLine::missingArgsException()
  {
  		int count = 0;
  
  		std::string missingArgList;
  		for (ArgListIterator it = _argList.begin(); it != _argList.end(); it++)
  		{
  			if ( (*it)->isRequired() && !(*it)->isSet() )
  			{
  				missingArgList += (*it)->getName();
  				missingArgList += ", ";
  				count++;
  			}
  		}
  		missingArgList = missingArgList.substr(0,missingArgList.length()-2);
  
  		std::string msg;
  		if ( count > 1 )
  			msg = "Required arguments missing: ";
  		else
  			msg = "Required argument missing: ";
  
  		msg += missingArgList;
  
  		throw(CmdLineParseException(msg));
  }
  
  inline void CmdLine::deleteOnExit(Arg* ptr)
  {
  	_argDeleteOnExitList.push_back(ptr);
  }
  
  inline void CmdLine::deleteOnExit(Visitor* ptr)
  {
  	_visitorDeleteOnExitList.push_back(ptr);
  }
  
  inline CmdLineOutput* CmdLine::getOutput()
  {
  	return _output;
  }
  
  inline void CmdLine::setOutput(CmdLineOutput* co)
  {
  	if ( !_userSetOutput )
  		delete _output;
  	_userSetOutput = true;
  	_output = co;
  }
  
  inline std::string& CmdLine::getVersion()
  {
  	return _version;
  }
  
  inline std::string& CmdLine::getProgramName()
  {
  	return _progName;
  }
  
  inline std::list<Arg*>& CmdLine::getArgList()
  {
  	return _argList;
  }
  
  inline XorHandler& CmdLine::getXorHandler()
  {
  	return _xorHandler;
  }
  
  inline char CmdLine::getDelimiter()
  {
  	return _delimiter;
  }
  
  inline std::string& CmdLine::getMessage()
  {
  	return _message;
  }
  
  inline bool CmdLine::hasHelpAndVersion()
  {
  	return _helpAndVersion;
  }
  
  inline void CmdLine::setExceptionHandling(const bool state)
  {
  	_handleExceptions = state;
  }
  
  inline bool CmdLine::getExceptionHandling() const
  {
  	return _handleExceptions;
  }
  
  inline void CmdLine::reset()
  {
  	for( ArgListIterator it = _argList.begin(); it != _argList.end(); it++ )
  		(*it)->reset();
  	
  	_progName.clear();
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  //End CmdLine.cpp
  ///////////////////////////////////////////////////////////////////////////////
  
  
  
  } //namespace TCLAP
  #endif
  