/**
 * @file cmdopt2.cc
 *
 * CommandOptions - Classes for evaluating command line options.
 *
 * Copyright: 2002-2005 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: cmdopt2.cc,v 1.1.2.1 2006/02/28 21:35:44 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     Common source files for c4b tools $
 * @date        06.05.2005
 * @author      "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

// System includes
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdexcept>
#include <sstream>
#include <iomanip>
#include <algorithm>

// Import includes

#define __CMDOPT2__

// Local includes
#ifndef CMDOPT2_H
#  include "cmdopt2.h"
#endif

namespace thwutil {





// === Private definitions ===============================================





/// The default characters introducing an option.
#ifdef WIN32
#define DEFAULT_SWITCH_CHARS    "-/"
#else // WIN32
#define DEFAULT_SWITCH_CHARS    "-"
#endif // WIN32

/// The default characters separating options from possible parameters.
#define DEFAULT_PARAM_SEP_CHARS ":="





// === Definitions for public declarations ===============================





// === Prototypes of private functions ===================================





// === Definitions for class members =====================================





// --- Class for evaluating a command line -------------------------------

/**
 * Constructor.
 */

CCmdLineEvaluator::CCmdLineEvaluator (void)
{
   m_strSwitchChars = DEFAULT_SWITCH_CHARS;
   m_strParamSepChars = DEFAULT_PARAM_SEP_CHARS;
   
} // CCmdLineEvaluator::CCmdLineEvaluator





/**
 * Destructor.
 */

CCmdLineEvaluator::~CCmdLineEvaluator (void)
{
   // nothing to do
   
} // CCmdLineEvaluator::~CCmdLineEvaluator





/**
 * Set the active switch characters for command line evaluation.
 *
 * The string specified will be used as the set of characters starting an
 * option. If more than one character is specified, each character may
 * introduce an option, even mixed use on the same command line is
 * allowed.
 *
 * It is not possible to specify a string to start an option, i.e. start
 * every option with "--". This may only be accomplished by defining the
 * options themselves to start with an additional "-" at the beginning.
 *
 * @param pszNewSwitchChars     I: A string of switch characters introducing an
 *                                 option. It must not be empty or the NULL
 *                                 pointer.
 *
 * @return None.
 *
 * @throws std::invalid_argument
 *                              The parameter was the NULL pointer or an empty
 *                              string.
 */

void CCmdLineEvaluator::SetSwitchCharacters
   (const char *pszNewSwitchChars)
{
   if (! pszNewSwitchChars || ! pszNewSwitchChars [0])
   {
      throw std::invalid_argument ("invalid switch character string");
   }

   (void) m_strSwitchChars.assign (pszNewSwitchChars);
   
} // CCmdLineEvaluator::SetSwitchCharacters





/**
 * Set the active parameter separator characters.
 *
 * The string specified will be used as the set of characters separating
 * options from their parameter if they are defined to have one. Each
 * character of the string may serve as the separation character.
 *
 * @param pszNewParamSepChars   I: A string of separator characters between
 *                                 options and their parameters. It must not be
 *                                 empty or the NULL pointer.
 *
 * @return None.
 *
 * @throws std::invalid_argument
 *                              The parameter was the NULL pointer or an empty
 *                              string.
 */

void CCmdLineEvaluator::SetParamSeparatorCharacters
   (const char *pszNewParamSepChars)
{
   if (! pszNewParamSepChars || ! pszNewParamSepChars [0])
   {
      throw std::invalid_argument ("invalid parameter separator string");
   }

   (void) m_strParamSepChars.assign (pszNewParamSepChars);
   
} // CCmdLineEvaluator::SetParamSeparatorCharacters





/**
 * Add a new command line option.
 *
 * This member function is used to add option objects to the command line
 * evaluator. When evaluating a command line, these option objects are
 * compared to the strings of the command line. If no option objects are
 * added, the evaluation call will not deliver any recognized option.
 *
 * @param pCmdOpt               I: The address of the option object to add to
 *                                 the internal list of known command line
 *                                 options. It must not be NULL.
 *
 * @return None.
 *
 * @throws std::invalid_argument
 *                              The parameter was the NULL pointer.
 */

void CCmdLineEvaluator::AddOption
   (CCmdOptBase *pCmdOpt)
{
   if (pCmdOpt == NULL)
   {
      throw std::invalid_argument ("invalid command line option pointer");
   }
   
   m_aOptions.push_back (pCmdOpt);
   
} // CCmdLineEvaluator::AddOption





/**
 * Remove a command line option from the internal list.
 *
 * This member function is used to remove an option object from the
 * internal list of known options. It is usually called by the desctructor
 * of the option objects at program termination.
 *
 * The option is identified by its address. Thus it is not possible to
 * create an additional option object with a specific name to remove an
 * option with this name from our list.
 *
 * @param pCmdOpt               I: The address of the option to remove.
 *
 * @retval None.
 */

void CCmdLineEvaluator::RemoveOption
   (CCmdOptBase *pCmdOpt)
{
   if (pCmdOpt == NULL)
   {
      throw std::invalid_argument ("invalid command line option pointer");
   }
   
   CCmdOptionList::iterator iter =
      std::find (m_aOptions.begin (), m_aOptions.end (), pCmdOpt);
   if (iter == m_aOptions.end ())
   {
      throw std::invalid_argument ("unknown command line option pointer");
   }
   
   (void) m_aOptions.erase (iter);
   
} // CCmdLineEvaluator::RemoveOption





/**
 * Reset options to default values.
 *
 * This function is used to reset all option objects to "not found". If
 * an option was recognized in a former evaluation call and it is defined
 * as unique, the next evaluation call will succeed even if this option
 * is found once more. Besides resetting the "was found" flag the values
 * for options with parameters are set to their default values, specified
 * during construction time of the respective option object.
 *
 * In short words: For all option objects in the internal list their
 * ResetValue() member function is called.
 *
 * @param None.
 *
 * @return None.
 */

void CCmdLineEvaluator::ResetOptions (void)
{
   CCmdOptionList::iterator iter;
   
   for (iter = m_aOptions.begin (); iter != m_aOptions.end (); ++iter)
   {
      (*iter)->ResetValue ();
   }
   
} // CCmdLineEvaluator::ResetOptions





/**
 * Evaluate a command line and store the results locally.
 *
 * The task of this function is the evaluation of the command line, i.e.
 * the real purpose of this class. All the command line strings specified
 * by the pointer to an array of strings, the string count and the
 * starting index are searched for known options. Every string starting
 * with one of the switch characters is a candidate for an option string.
 * The evaluation stops at the first unknown or malformed option or when
 * a string not starting with a switch character is found. Strings
 * consisting only of a single switch character (e.g. "-") or of a double
 * switch character (e.g. "--") are assumed to be non-options. The single
 * switch character will be returned as the next command line string, the
 * double switch character will be overread and the next string is
 * returned.
 *
 * As mentioned before the parameter piEndIdx will be set to the index of
 * the command line string beeing a non-option behind all evaluated
 * options. If an error occurs, the parameter contains the index of the
 * erraneous command line string. This may be used to evaluate all options
 * at the beginning of the command line. Behind the options a program may
 * expect one or more file names that do not start with a switch
 * character.
 *
 * Options consisting of a single character as the option name may be
 * concatenated to a single string just starting with one single switch
 * character. E.g. the options "-a", "-b" and "-c" may be concatenated to
 * "-abc". This is not possible for options with longer names.
 *
 * @param iArgc                 I: The number of strings in the array at
 *                                 papszArgv.
 * @param papszArgv             I: An array of string pointers building up the
 *                                 command line.
 * @param iStartIdx             I: The evaluation of the command line strings at
 *                                 papszArgv starts at this index. This is used
 *                                 to ignore some strings, e.g. the program file
 *                                 name as the first string of every command
 *                                 line.
 * @param piEndIdx              O: The index into papszArgv where the evaluation
 *                                 stopped. If the result is O.K., it addresses
 *                                 the first non-option behind the options. If
 *                                 an error occurs, maybe because of an unknown
 *                                 option on the command line, this parameter
 *                                 points to the erraneous string.
 *
 * @return CLE_OK               The command line evaluation was successful,
 *                              *piEndIdx contains the index of the first
 *                              non-option string.
 * @return CLE_EVALUATION_TERMINATED
 *                              The command line evaluation was terminated
 *                              because the last option handled has the
 *                              termination flag set. All other options handled
 *                              until this point are evaluated correctly,
 *                              *piEndIdx points to the command line argument
 *                              containing the option that caused the
 *                              termination.
 * @return CLE_ERR_UNKNOWN_OPTION
 *                              The command line contains a string starting with
 *                              a switch character, but the string following it
 *                              could not be found in the internal list of known
 *                              options.
 * @return CLE_ERR_INVALID_CONCATENATION
 *                              The current option has a name of at least two
 *                              characters and it is either following another
 *                              option in the same command line string (i.e. it
 *                              is concatenated to the previous option) or it is
 *                              followed by additional characters (provided the
 *                              current option is not defined to have
 *                              parameters).
 * @return CLE_ERR_DUPLICATE_OPTION
 *                              The current option was already previously found
 *                              on the command line and it is defined to be
 *                              unique. The option is also assumed to be found a
 *                              second time if it was found in a former call to
 *                              this function without calling ResetOptions()
 *                              between the two calls.
 * @return CLE_ERR_INVALID_PARAM
 *                              An option is defined to require a parameter but
 *                              none was specified or the parameter value was
 *                              invalid.
 */

CCmdLineEvaluator::CmdLineEvalResult_t CCmdLineEvaluator::EvalCommandLine
   (int    iArgc,
    char **papszArgv,
    int    iStartIdx,
    int   *piEndIdx)
{
   CmdLineEvalResult_t       res;
   int                       i;
   const char               *p;
   const char               *q;
   CCmdOptionList::iterator  iter;
   size_t                    nLenOptName;
   
   // walk through all command line options specified
   for (res = CLE_OK, i = iStartIdx; res == CLE_OK && i < iArgc; ++i)
   {
      // check if the current command line string is really an option
      p = papszArgv [i];
      if (m_strSwitchChars.find (p [0]) == std::string::npos)
      {
         // the current string does not start with a switch character --> end of
         // options
         break;
      }
      if (p [1] == '\0')
      {
         // the current string is only a single switch char --> no option, but
         // maybe this is a special parameter, e.g. a file name specification
         // addressing stdout or stdin, that must be delivered to the caller
         break;
      }
      if (p [0] == p [1] && p [2] == '\0')
      {
         // the current string consists only of two switch characters --> end of
         // options, the current string must be overread
         ++i;
         break;
      }
      
      // evaluate the current command line string, possibly consisting of a
      // concatenation of single character options
      ++p;
      q = p;
      while (*p)
      {
         // for each known option check if it stands at the current position
         for (iter = m_aOptions.begin (); iter != m_aOptions.end (); ++iter)
         {
            if ((*iter)->CompareName (p))
            {
               break;
            }
         }
         
         // if no option was found: abort evaluation
         if (iter == m_aOptions.end ())
         {
            res = CLE_ERR_UNKNOWN_OPTION;
            break;
         }
         // so now iter points to the option found at the current position
         
         // if the current option is a multi-character string and the current
         // position is not at the beginning of a command line string, we have
         // an unallowed concatenation of multi-character options
         nLenOptName = (*iter)->GetName ().length ();
         if (q != p && nLenOptName > 1)
         {
            res = CLE_ERR_INVALID_CONCATENATION;
            break;
         }
         
         // we found an option, check if it is defined as unique and this is not
         // the first occurrence
         if ((*iter)->IsUnique () && (*iter)->WasFound ())
         {
            res = CLE_ERR_DUPLICATE_OPTION;
            break;
         }
         
         // remember the fact that the option was found
         (*iter)->SetFound (true);
         
         // if the option takes a parameter, let the option evaluate it
         if ((*iter)->HasParameter ())
         {
            // if there is a separator character defined, the parameter must be
            // in the current string or no parameter is given on the command
            // line
            if ((*iter)->HasParamSeparator ())
            {
               // check for valid separator character, parameter may be optional
               p += nLenOptName;
               if (m_strParamSepChars.find (p [0]) == std::string::npos)
               {
                  if (! (*iter)->IsParamOptional ())
                  {
                     res = CLE_ERR_INVALID_PARAM;
                     break;
                  }
               }
               else
               {
                  p++;
               }
               
               // let the option evaluate its parameter, even if no one is
               // specified
               res = (*iter)->EvalParam (p);
            }
            // if there is no separator character and the parameter is optional,
            // the parameter must be in the current string or there is no
            // parameter specified on the command line
            else if ((*iter)->IsParamOptional ())
            {
               // just let the option evaluate its parameter, even if the
               // parameter argument is an empty string
               res = (*iter)->EvalParam (&(p [nLenOptName]));
            }
            // if there is no separator character and the parameter is not
            // optional, the parameter may be right behind the option name or in
            // the next argument
            else
            {
               // if the end of the option name is the end of the current
               // command line string, the parameter must be in the next string
               if (p [nLenOptName] == '\0')
               {
                  if (i + 1 >= iArgc)
                  {
                     // no additional argument follows --> let p point to an
                     // empty string
                     p += nLenOptName;
                  }
                  else
                  {
                     ++i;
                     p = papszArgv [i];
                  }
               }
               else
               {
                  p = &(p [nLenOptName]);
               }
               if (p [0] == '\0')
               {
                  // no parameter present
                  res = CLE_ERR_INVALID_PARAM;
                  break;
               }
               
               // let the option evaluate its parameter
               res = (*iter)->EvalParam (p);
            }

            // advance the pointer for the current command line string position,
            // must be the end of the string
            if (res == CLE_OK)
            {
               p += strlen (p);
            }
         }
         else
         {
            // the option has no parameter, advance the read pointer
            p += nLenOptName;
            
            // if the current option has a multi-character name and the current
            // read position is not the end of the string, we have an
            // unallowed concatenation of options
            if (nLenOptName > 1 && *p != '\0')
            {
               res = CLE_ERR_INVALID_CONCATENATION;
               break;
            }
         }
         
         // check if the option handled has the termination flag set
         if ((*iter)->TerminateEvaluation ())
         {
            res = CLE_EVALUATION_TERMINATED;
            break;
         }
      }
   }
   
   if (piEndIdx)
   {
      *piEndIdx = i;
   }
   return (res);
} // CCmdLineEvaluator::EvalCommandLine





/**
 * Copy the local command line option values to public locations.
 *
 * The option objects may be constructed with the address of a memory
 * location to take the value of the option object after command line
 * evaluation. To perform the copy operation from the option objects to
 * these memory locations, this function is called. It should be called
 * after calling EvalCommandLine(), because else the options the options
 * are only set to their default values. And if an option was not found,
 * no copy operation is executed.
 *
 * This feature may be used in conjuction with other configuration
 * methods for a program. An application may first read its configuration
 * from configuration files or from a database. This data may be overriden
 * by command line options. The values for the options are copied to the
 * internal storage of the previously read configuration, so the program
 * only needs to read its configuration data from one location during its
 * run.
 *
 * @param pfnCallback           I: For each option found in a previous call to
 *                                 EvalCommandLine the current value is copied
 *                                 to the memory location specified at its
 *                                 construction time. Before each copy operation
 *                                 a caller provided callback function may be
 *                                 called. So the caller e.g. may print out
 *                                 every command line option and its value as
 *                                 evaluated. If this feature is not needed,
 *                                 this function pointer may be NULL.
 *
 * @retval None.
 */

void CCmdLineEvaluator::TakeCommandLineOptions
   (void (*pfnCallback) (CCmdOptBase *pOption))
{
   CCmdOptionList::iterator iter;
   
   for (iter = m_aOptions.begin (); iter != m_aOptions.end (); ++iter)
   {
      if ((*iter)->WasFound ())
      {
         if (pfnCallback)
         {
            pfnCallback (*iter);
         }

         (*iter)->CopyValue ();
      }
   }
   
} // CCmdLineEvaluator::TakeCommandLineOptions





/**
 * Convert an evaluation result value into a text string.
 *
 * The result values of the EvalCommandLine() member function may be
 * translated into readable strings by calling this function. The result
 * strings are constant and never invalidated.
 *
 * @param resVal                I: The command line evaluation result value to
 *                                 translate into a string.
 *
 * @retval A pointer to the translated string.
 */

const char *CCmdLineEvaluator::ResultMsg
   (CCmdLineEvaluator::CmdLineEvalResult_t resVal)
{
   static char  szBuf [32];
   const char  *psz;
   
#define _CASE( a )      case a: psz = #a; break

   switch (resVal)
   {
      _CASE (CLE_OK);
      _CASE (CLE_ERR_UNKNOWN_OPTION);
      _CASE (CLE_ERR_INVALID_PARAM);
      _CASE (CLE_ERR_INVALID_CONCATENATION);
      
      default:
         snprintf (szBuf, sizeof (szBuf), "Unknown <<%d>>", (int) resVal);
         psz = szBuf;
         break;
   }
   
#undef _CASE

   return (psz);
} // CCmdLineEvaluator::ResultMsg





// --- Base class for all command line options ---------------------------

/**
 * Constructor.
 *
 * The constructor initializes all members of an object. Additionally this
 * object is added to the command line evaluator object specified as the
 * first parameter of this constructor.
 *
 * @param cmdLineEvaluator      I: The command line evaluator object this option
 *                                 object will be a member of. The constructor
 *                                 will call the member function AddOption() of
 *                                 the command line evaluator object with this
 *                                 objects address as the parameter.
 * @param pszName               I: The name of the option.
 * @param iFlags                I: A bitmask of flags controlling the behaviour
 *                                 of this class and of the command line
 *                                 evaluator. It is a combination of the values
 *                                 of the enumeration Flags_t.
 */

CCmdOptBase::CCmdOptBase
   (CCmdLineEvaluator &cmdLineEvaluator,
    const char        *pszName,
    int                iFlags):
   m_oCmdLineEvaluator (cmdLineEvaluator)
{
   if (! pszName || ! pszName [0])
   {
      throw std::invalid_argument ("invalid option name");
   }
   (void) m_strName.assign (pszName);
   m_iFlags = iFlags;
   m_fFound = false;
   
   m_oCmdLineEvaluator.AddOption (this);
   
} // CCmdOptBase::CCmdOptBase





/**
 * Destructor.
 *
 * The only purpose of this destructor is to remove this option object
 * from its command line evaluator object.
 */

CCmdOptBase::~CCmdOptBase (void)
{
   // remove this option object from its command line evaluator object
   m_oCmdLineEvaluator.RemoveOption (this);
   
} // CCmdOptBase::~CCmdOptBase





/**
 * Get the name of this option.
 */

const std::string &CCmdOptBase::GetName (void)
{
   return (m_strName);
} // CCmdOptBase::GetName





/**
 * Compare the name of this option with a given string.
 * Compare the name of this option with a given string.
 *
 * The case significance is taken from the m_iFlags member variable.
 *
 * @param pszStr                I: The string to compare the option name with.
 *                                 It must not be NULL, but the empty string is
 *                                 accepted and will lead to a false result.
 *
 * @retval true                 The string specified is identical (according to
 *                              the case significance setting in the m_iFlags
 *                              member variable).
 * @retval false                The string specified is not equal to the option
 *                              name.
 *
 * @throws std::invalid_argument
 *                              The parameter is the NULL pointer.
 */

bool CCmdOptBase::CompareName
   (const char *pszStr)
{
   if (! pszStr)
   {
      throw std::invalid_argument ("invalid name argument to compare to");
   }
   
   if ((m_iFlags & IGNORE_NAME_CASE) == 0)
   {
      size_t nLen = strlen (pszStr);
      if (nLen < m_strName.length ())
      {
         return (false);
      }
      if (nLen > m_strName.length ())
      {
         nLen = m_strName.length ();
      }
      return (strncmp (m_strName.c_str (), pszStr, nLen) == 0);
   }
   else
   {
      std::string::const_iterator  iter;
      const char                  *p;
      
      for (iter = m_strName.begin (), p = pszStr;
           iter != m_strName.end () && *p;
           ++iter, ++p)
      {
         if (tolower (*iter) != tolower (*p))
         {
            return (false);
         }
      }
      if (iter == m_strName.end ())
      {
         return (true);
      }
      return (false);
   }
   
} // CCmdOptBase::CompareName





/**
 * Set the flag if the option was found to the specified value.
 */

void CCmdOptBase::SetFound
   (bool f)
{
   m_fFound = f;
   
} // CCmdOptBase::SetFound





/**
 * Query if the option was found.
 */

bool CCmdOptBase::WasFound (void)
{
   return (m_fFound);
} // CCmdOptBase::WasFound





/**
 * Query if this option has a parameter.
 */

bool CCmdOptBase::HasParameter (void)
{
   return ((m_iFlags & HAS_PARAMETER) != 0);
} // CCmdOptBase::HasParameter





/**
 * Query if this option has a parameter with a separator from its name.
 */

bool CCmdOptBase::HasParamSeparator (void)
{
   return ((m_iFlags & (HAS_PARAMETER | PARAM_SEPARATOR)) ==
              (HAS_PARAMETER | PARAM_SEPARATOR));
} // CCmdOptBase::HasParamSeparator





/**
 * Query if this option has a parameter that is optional.
 */

bool CCmdOptBase::IsParamOptional (void)
{
   return ((m_iFlags & (HAS_PARAMETER | PARAM_OPTIONAL)) ==
              (HAS_PARAMETER | PARAM_OPTIONAL));
} // CCmdOptBase::IsParamOptional





/**
 * Query if this option must be unique on the command line.
 */

bool CCmdOptBase::IsUnique (void)
{
   return ((m_iFlags & UNIQUE_OPTION) != 0);
} // CCmdOptBase::IsUnique





/**
 * Query if this option shall terminate the command line evaluation
 * process.
 */

bool CCmdOptBase::TerminateEvaluation (void)
{
   return ((m_iFlags & TERMINATE_EVALUATION) != 0);
} // CCmdOptBase::TerminateEvaluation

/**
 * Evaluate a parameter string for this option.
 *
 * This is a dummy implementation that only returns CLE_ERR_INVALID_PARAM.
 * Derived classes supporting parameters must overwrite this member
 * function and implement the functionality needed.
 *
 * @param pszStr                I: The string with the parameter to evaluate. It
 *                                 may be the NULL pointer or the empty string.
 *
 * @retVal CLE_OK               The parameter was evaluated successfully.
 * @retval CLE_ERR_INVALID_PARAM
 *                              The string specified does not represent a valid
 *                              parameter value for this option.
 */

CCmdLineEvaluator::CmdLineEvalResult_t CCmdOptBase::EvalParam
   (const char *pszStr)
{
   // suppress warning
   (void) pszStr;
   
   return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
} // CCmdOptBase::EvalParam





/**
 * Reset the current value to default and option not found.
 *
 * This implementation only resets the current value for the flag "option
 * was found" to "false". Derived classes supporting parameter values
 * shall reset their current internal values to the respective default.
 *
 * @param None.
 *
 * @retval None.
 */

void CCmdOptBase::ResetValue (void)
{
   m_fFound = false;
   
} // CCmdOptBase::ResetValue





// --- Class for a boolean option with or without parameters -------------

/**
 * Constructor.
 *
 * @param cmdLineEvaluator      I: The command line evaluator object this option
 *                                 object will be a member of. It is forwarded
 *                                 to the constructor of the base class.
 * @param pszName               I: The name of the option.
 * @param iFlags                I: A bitmask of flags controlling the behaviour
 *                                 of this class and of the command line
 *                                 evaluator. It is a combination of the values
 *                                 of the enumeration Flags_t.
 * @param pfValueTarget         I: The address of a memory location to copy the
 *                                 current option value to. If this feature is
 *                                 not needed, this parameter may be NULL.
 * @param fDefaultValue         I: The default value for the option if it is
 *                                 found on the command line. This is only
 *                                 relevant if the option has an optional
 *                                 parameter. If this parameter is left out on
 *                                 the command line, this default value will be
 *                                 entered as the current option value.
 * @param pszPosParamStartChars I: This string is used for a boolean option with
 *                                 a parameter. The first character of the
 *                                 parameter specified on the command line will
 *                                 be looked up in this string. If it is found,
 *                                 the current option value will be set to
 *                                 "true".
 * @param pszNegParamStartChars I: This string is used for a boolean option with
 *                                 a parameter. The first character of the
 *                                 parameter specified on the command line will
 *                                 be looked up in this string. If it is found,
 *                                 the current option value will be set to
 *                                 "false".
 * @param pszPosString          I: This parameter is used when GetValueString()
 *                                 is called. If the current option value is
 *                                 "true" this string will be returned, else
 *                                 the string specified for pszNegString.
 * @param pszNegString          I: This parameter is used when GetValueString()
 *                                 is called. If the current option value is
 *                                 "false" this string will be returned, else
 *                                 the string specified for pszPosString.
 */

CBooleanCmdOpt::CBooleanCmdOpt
   (CCmdLineEvaluator &cmdLineEvaluator,
    const char        *pszName,
    int                iFlags,
    bool              *pfValueTarget,
    bool               fDefaultValue,
    const char        *pszPosParamStartChars,
    const char        *pszNegParamStartChars,
    const char        *pszPosString,
    const char        *pszNegString):
   CCmdOptBase (cmdLineEvaluator, pszName, iFlags)
{
   m_pfValueTarget = pfValueTarget;
   m_fDefaultValue = fDefaultValue;
   if ((m_iFlags & HAS_PARAMETER) == 0)
   {
      m_fCurrValue = false;
   }
   else
   {
      m_fCurrValue = fDefaultValue;
   }
   
   (void) m_strPosParamStartChars.assign (pszPosParamStartChars
                                          ? pszPosParamStartChars : "");
   (void) m_strNegParamStartChars.assign (pszNegParamStartChars
                                          ? pszNegParamStartChars : "");

   (void) m_strPosValue.assign (pszPosString ? pszPosString : "");
   (void) m_strNegValue.assign (pszNegString ? pszNegString : "");
   
} // CBooleanCmdOpt::CBooleanCmdOpt





/**
 * Destructor.
 */

CBooleanCmdOpt::~CBooleanCmdOpt (void)
{
   // PC-Lint/FlexLint complains about not releasing or zero'ing
   // m_pfValueTarget
   m_pfValueTarget = NULL;
   
} // CBooleanCmdOpt::~CBooleanCmdOpt





/**
 * Set the flag if the option was found to the specified value.
 */

void CBooleanCmdOpt::SetFound
   (bool f)
{
   CCmdOptBase::SetFound (f);
   if ((m_iFlags & HAS_PARAMETER) == 0)
   {
      m_fCurrValue = f;
   }
   
} // CBooleanCmdOpt::SetFound





/**
 * Evaluate a parameter string for this option.
 */

CCmdLineEvaluator::CmdLineEvalResult_t CBooleanCmdOpt::EvalParam
   (const char *pszStr)
{
   if ((! pszStr || ! pszStr [0]) &&
       (m_iFlags & PARAM_OPTIONAL) == 0)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   
   if (! pszStr || ! pszStr [0])
   {
      m_fCurrValue = m_fDefaultValue;
      return (CCmdLineEvaluator::CLE_OK);
   }
   
   if (m_strPosParamStartChars.find (pszStr [0]) != std::string::npos)
   {
      m_fCurrValue = true;
      return (CCmdLineEvaluator::CLE_OK);
   }
   
   if (m_strNegParamStartChars.find (pszStr [0]) != std::string::npos)
   {
      m_fCurrValue = false;
      return (CCmdLineEvaluator::CLE_OK);
   }

   m_fCurrValue = (strtol (pszStr, NULL, 0) == 0);

   return (CCmdLineEvaluator::CLE_OK);
} // CBooleanCmdOpt::EvalParam





/**
 * Copy the current value to the internally managed address.
 *
 * This function simply copies the current option value to the memory
 * location specified in the constructor call. If it was given as the NULL
 * pointer, no copy operation will take place.
 *
 * @param None.
 *
 * @retval None.
 */

void CBooleanCmdOpt::CopyValue (void)
{
   if (m_pfValueTarget)
   {
      *m_pfValueTarget = m_fCurrValue;
   }
   
} // CBooleanCmdOpt::CopyValue





/**
 * Get a string representation of the current value.
 *
 * This function returns the current value of the member strings
 * m_strPosValue and m_strNegValue respectively, according to the current
 * option value. The two strings may be specified in the constructor or
 * set later by using SetPositiveString() or SetNegativeString().
 *
 * @param None.
 *
 * @result A string representing the current (parameter) value of the
 *         option.
 */

std::string CBooleanCmdOpt::GetValueString (void)
{
   if (m_fCurrValue)
   {
      return (m_strPosValue);
   }
   return (m_strNegValue);
} // CBooleanCmdOpt::GetValueString





/**
 * Get the current value of the option.
 */

bool CBooleanCmdOpt::GetValue (void)
{
   return (m_fCurrValue);
} // CBooleanCmdOpt::GetValue





/**
 * Set the parameter starting characters for positive values.
 */

void CBooleanCmdOpt::SetPositiveParamChars
   (const char *psz)
{
   (void) m_strPosParamStartChars.assign (psz ? psz : "");

} // CBooleanCmdOpt::SetPositiveParamChars





/**
 * Set the parameter starting characters for negative values.
 */

void CBooleanCmdOpt::SetNegativeParamChars
   (const char *psz)
{
   (void) m_strNegParamStartChars.assign (psz ? psz : "");

} // CBooleanCmdOpt::SetNegativeParamChars





/**
 * Set the string representation for positive values.
 */

void CBooleanCmdOpt::SetPositiveString
   (const char *psz)
{
   (void) m_strPosValue.assign (psz ? psz : "");
   
} // CBooleanCmdOpt::SetPositiveString





/**
 * Set the string representation for negative values.
 */

void CBooleanCmdOpt::SetNegativeString
   (const char *psz)
{
   (void) m_strNegValue.assign (psz ? psz : "");
   
} // CBooleanCmdOpt::SetNegativeString





// --- Class for an option with a signed integer parameter ---------------

/**
 * Constructor.
 *
 * @param cmdLineEvaluator      I: The command line evaluator object this option
 *                                 object will be a member of. It is passed to
 *                                 the base constructor.
 * @param pszName               I: The name of the option.
 * @param iFlags                I: A bitmask of flags controlling the behaviour
 *                                 of this class and of the command line
 *                                 evaluator. It is a combination of the values
 *                                 of the enumeration Flags_t.
 * @param piValueTarget         I: A memory location for copying the current
 *                                 option value to when CopyValue() is called.
 *                                 If this feature is not needed, this may be
 *                                 the NULL pointer.
 * @param iDefaultValue         I: The default value for the option. This is
 *                                 normally used for options with optional
 *                                 parameters when the parameter is left out on
 *                                 the command line.
 * @param iMinValue             I: This argument may be specified as the minimum
 *                                 allowed value for the option. If a parameter
 *                                 leads to a value below this limit, this value
 *                                 is not stored into the current option value.
 * @param iMaxValue             I: This argument may be specified as the maximum
 *                                 allowed value for the option. If a parameter
 *                                 leads to a value above this limit, this value
 *                                 is not stored into the current option value.
 */

CIntParamCmdOpt::CIntParamCmdOpt
   (CCmdLineEvaluator &cmdLineEvaluator,
    const char        *pszName,
    int                iFlags,
    int               *piValueTarget,
    int                iDefaultValue,
    int                iMinValue,
    int                iMaxValue):
   CCmdOptBase (cmdLineEvaluator, pszName, iFlags)
{
   m_piValueTarget = piValueTarget;
   m_iDefaultValue = iDefaultValue;
   m_iCurrValue    = iDefaultValue;
   m_iMinimumValue = iMinValue;
   m_iMaximumValue = iMaxValue;
   
} // CIntParamCmdOpt::CIntParamCmdOpt





/**
 * Destructor.
 */

CIntParamCmdOpt::~CIntParamCmdOpt (void)
{
   // PC-Lint/FlexLint complains about not releasing or zero'ing
   // m_piValueTarget
   m_piValueTarget = NULL;
   
} // CIntParamCmdOpt::~CIntParamCmdOpt





/**
 * Evaluate a parameter string for this option.
 *
 * Here the function argument is interpreted as a (signed) integer value.
 * The only checks that will be performed, are the test for the NULL
 * pointer or the empty string for the argument and the tests for the
 * minimum and maximum values for this option. It is not verified that the
 * argument is really numerical. If a text string is passed, the current
 * option value will simply be set to zero (if the minimum and maximum
 * values allow this).
 *
 * @param pszStr                I: The string parameter to evaluate as an
 *                                 integer value.
 *
 * @retval CLE_OK               The parameter could successfully be evaluated to
 *                              a signed integer value.
 * @retval CLE_INVALID_PARAM    The function argument is the NULL pointer or the
 *                              empty string and the parameter is not optional.
 */

CCmdLineEvaluator::CmdLineEvalResult_t CIntParamCmdOpt::EvalParam
   (const char *pszStr)
{
   if ((! pszStr || ! pszStr [0]) &&
       (m_iFlags & PARAM_OPTIONAL) == 0)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   
   if (! pszStr || ! pszStr [0])
   {
      m_iCurrValue = m_iDefaultValue;
      return (CCmdLineEvaluator::CLE_OK);
   }
   
   m_iCurrValue = (int) strtol (pszStr, NULL, 0);
   
   if (m_iCurrValue > m_iMaximumValue)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   
   if (m_iCurrValue < m_iMinimumValue)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }

   return (CCmdLineEvaluator::CLE_OK);
} // CIntParamCmdOpt::EvalParam





/**
 * Reset the current value to default and option not found.
 */

void CIntParamCmdOpt::ResetValue (void)
{
   CCmdOptBase::ResetValue ();
   
   m_iCurrValue = m_iDefaultValue;
   
} // CIntParamCmdOpt::ResetValue





/**
 * Copy the current value to the internally managed address.
 */

void CIntParamCmdOpt::CopyValue (void)
{
   if (m_piValueTarget)
   {
      *m_piValueTarget = m_iCurrValue;
   }
   
} // CIntParamCmdOpt::CopyValue





/**
 * Get a string representation of the current value.
 */

std::string CIntParamCmdOpt::GetValueString (void)
{
   std::ostringstream oss;
   
   oss << m_iCurrValue << std::ends;
   
   return (std::string (oss.str ()));
} // CIntParamCmdOpt::GetValueString





/**
 * Get the current value of the option.
 */

int CIntParamCmdOpt::GetValue (void)
{
   return (m_iCurrValue);
} // CIntParamCmdOpt::GetValue





// --- Class for an option with an unsigned integer parameter ------------

/**
 * Constructor.
 *
 * @param cmdLineEvaluator      I: The command line evaluator object this option
 *                                 object will be a member of. It is passed to
 *                                 the base constructor.
 * @param pszName               I: The name of the option.
 * @param iFlags                I: A bitmask of flags controlling the behaviour
 *                                 of this class and of the command line
 *                                 evaluator. It is a combination of the values
 *                                 of the enumeration Flags_t.
 * @param puValueTarget         I: A memory location for copying the current
 *                                 option value to when CopyValue() is called.
 *                                 If this feature is not needed, this may be
 *                                 the NULL pointer.
 * @param uDefaultValue         I: The default value for the option. This is
 *                                 normally used for options with optional
 *                                 parameters when the parameter is left out on
 *                                 the command line.
 * @param uMinValue             I: This argument may be specified as the minimum
 *                                 allowed value for the option. If a parameter
 *                                 leads to a value below this limit, this value
 *                                 is not stored into the current option value.
 * @param uMaxValue             I: This argument may be specified as the maximum
 *                                 allowed value for the option. If a parameter
 *                                 leads to a value above this limit, this value
 *                                 is not stored into the current option value.
 */

CUIntParamCmdOpt::CUIntParamCmdOpt
   (CCmdLineEvaluator &cmdLineEvaluator,
    const char        *pszName,
    int                iFlags,
    unsigned int      *puValueTarget,
    unsigned int       uDefaultValue,
    unsigned int       uMinValue,
    unsigned int       uMaxValue):
   CCmdOptBase (cmdLineEvaluator, pszName, iFlags)
{
   m_puValueTarget = puValueTarget;
   m_uDefaultValue = uDefaultValue;
   m_uCurrValue    = uDefaultValue;
   m_uMinimumValue = uMinValue;
   m_uMaximumValue = uMaxValue;
   
} // CUIntParamCmdOpt::CUIntParamCmdOpt





/**
 * Destructor.
 */

CUIntParamCmdOpt::~CUIntParamCmdOpt (void)
{
   // PC-Lint/FlexLint complains about not releasing or zero'ing
   // m_puValueTarget
   m_puValueTarget = NULL;
   
} // CUIntParamCmdOpt::~CUIntParamCmdOpt





/**
 * Evaluate a parameter string for this option.
 *
 * Here the function argument is interpreted as a (signed) integer value.
 * The only checks that will be performed, are the test for the NULL
 * pointer or the empty string for the argument and the tests for the
 * minimum and maximum values for this option. It is not verified that the
 * argument is really numerical. If a text string is passed, the current
 * option value will simply be set to zero (if the minimum and maximum
 * values allow this).
 *
 * @param pszStr                I: The string parameter to evaluate as an
 *                                 integer value.
 *
 * @retval CLE_OK               The parameter could successfully be evaluated to
 *                              a signed integer value.
 * @retval CLE_INVALID_PARAM    The function argument is the NULL pointer or the
 *                              empty string and the parameter is not optional.
 */

CCmdLineEvaluator::CmdLineEvalResult_t CUIntParamCmdOpt::EvalParam
   (const char *pszStr)
{
   if ((! pszStr || ! pszStr [0]) &&
       (m_iFlags & PARAM_OPTIONAL) == 0)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   
   if (! pszStr || ! pszStr [0])
   {
      m_uCurrValue = m_uDefaultValue;
      return (CCmdLineEvaluator::CLE_OK);
   }
   
   m_uCurrValue = (unsigned int) strtoul (pszStr, NULL, 0);
   
   if (m_uCurrValue > m_uMaximumValue)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   
   if (m_uCurrValue < m_uMinimumValue)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }

   return (CCmdLineEvaluator::CLE_OK);
} // CUIntParamCmdOpt::EvalParam





/**
 * Reset the current value to default and option not found.
 */

void CUIntParamCmdOpt::ResetValue (void)
{
   CCmdOptBase::ResetValue ();
   
   m_uCurrValue = m_uDefaultValue;
   
} // CUIntParamCmdOpt::ResetValue





/**
 * Copy the current value to the internally managed address.
 */

void CUIntParamCmdOpt::CopyValue (void)
{
   if (m_puValueTarget)
   {
      *m_puValueTarget = m_uCurrValue;
   }
   
} // CUIntParamCmdOpt::CopyValue





/**
 * Get a string representation of the current value.
 */

std::string CUIntParamCmdOpt::GetValueString (void)
{
   std::ostringstream oss;
   char               c;
   std::ios::fmtflags f;
   
   c = oss.fill ();
   f = oss.flags ();
   if ((m_iFlags & UINT_HEX_OUTPUT) != 0)
   {
      /*lint -e534 (Ignoring return value of function 'operator<<(ostream &, const smanip<int> &)') */
      oss << "0x"
          << std::setw (sizeof (unsigned int) * 2)
          << std::setfill ('0')
          << std::setbase (16);
      /*lint +e534 (Ignoring return value of function 'operator<<(ostream &, const smanip<int> &)') */
   }
   oss << m_uCurrValue << std::ends;
   (void) oss.fill (c);
   (void) oss.flags (f);
   
   return (std::string (oss.str ()));
} // CUIntParamCmdOpt::GetValueString





/**
 * Get the current value of the option.
 */

unsigned int CUIntParamCmdOpt::GetValue (void)
{
   return (m_uCurrValue);
} // CUIntParamCmdOpt::GetValue





// --- Class for an option with a string type parameter ------------------

/**
 * Constructor.
 *
 * There are two constructor variants. This version is used to specify the
 * destination for copying the current option value to a memory location
 * as a simple C character array with a fixed maximum length.
 *
 * @param cmdLineEvaluator      I: The command line evaluator object this option
 *                                 object will be a member of. It is passed to
 *                                 the base constructor.
 * @param pszName               I: The name of the option.
 * @param iFlags                I: A bitmask of flags controlling the behaviour
 *                                 of this class and of the command line
 *                                 evaluator. It is a combination of the values
 *                                 of the enumeration Flags_t.
 * @param pszValueTarget        I: The address of a memory location (character
 *                                 vector) to copy the current option value to
 *                                 in member function CopyValue(). If this
 *                                 feature is not needed, this argument may be
 *                                 the NULL pointer.
 * @param nLenValueTarget       I: The size of the memory in bytes at
 *                                 pszValueTarget. Even if pszValueTarget is the
 *                                 NULL pointer, this argument must be set to a
 *                                 reasonable maximum length. This is because
 *                                 this length argument is used to check the
 *                                 parameter length when assigning a string to
 *                                 the current option value.
 * @param pszDefaultValue I:       The default option value if the option
 *                                 parameter is optional.
 */

CStringParamCmdOpt::CStringParamCmdOpt
   (CCmdLineEvaluator &cmdLineEvaluator,
    const char        *pszName,
    int                iFlags,
    char              *pszValueTarget,
    size_t             nLenValueTarget,
    const char        *pszDefaultValue):
   CCmdOptBase (cmdLineEvaluator, pszName, iFlags)
{
   m_fStringValueTarget = false;
   m_pstrValueTarget    = NULL;
   m_pszValueTarget     = pszValueTarget;
   m_nMaxLenValue       = nLenValueTarget;
   
   (void) m_strDefaultValue.assign (pszDefaultValue ? pszDefaultValue : "");
   (void) m_strCurrValue.assign (m_strDefaultValue);
   
} // CStringParamCmdOpt::CStringParamCmdOpt





/**
 * Constructor.
 *
 * There are two constructor variants. This version is used to specify the
 * destination for copying the current option value to a memory location
 * as a STL string object with an optional maximum length.
 *
 * @param cmdLineEvaluator      I: The command line evaluator object this option
 *                                 object will be a member of. It is passed to
 *                                 the base constructor.
 * @param pszName               I: The name of the option.
 * @param iFlags                I: A bitmask of flags controlling the behaviour
 *                                 of this class and of the command line
 *                                 evaluator. It is a combination of the values
 *                                 of the enumeration Flags_t.
 * @param pstrValueTarget       I: The address of a memory location (STL string
 *                                 object) to copy the current option value to
 *                                 in member function CopyValue(). If this
 *                                 feature is not needed, this argument may be
 *                                 the NULL pointer.
 * @param nMaxLenValueTarget    I: The maximum length for the parameter string.
 *                                 This is used to check the parameter string
 *                                 for validity when assigning it to the current
 *                                 option value.
 * @param pszDefaultValue       I: The default option value if the option
 *                                 parameter is optional.
 */

CStringParamCmdOpt::CStringParamCmdOpt
   (CCmdLineEvaluator &cmdLineEvaluator,
    const char        *pszName,
    int                iFlags,
    std::string       *pstrValueTarget,
    size_t             nMaxLenValue,
    const char        *pszDefaultValue):
   CCmdOptBase (cmdLineEvaluator, pszName, iFlags)
{
   m_fStringValueTarget = true;
   m_pstrValueTarget    = pstrValueTarget;
   m_pszValueTarget     = NULL;
   m_nMaxLenValue       = nMaxLenValue;
   
   (void) m_strDefaultValue.assign (pszDefaultValue ? pszDefaultValue : "");
   (void) m_strCurrValue.assign (m_strDefaultValue);
   
} // CStringParamCmdOpt::CStringParamCmdOpt





/**
 * Destructor.
 */

CStringParamCmdOpt::~CStringParamCmdOpt (void)
{
   // PC-Lint/FlexLint complains about not releasing or zero'ing
   // m_pstrValueTarget and m_pszValueTarget
   m_pstrValueTarget = NULL;
   m_pszValueTarget  = NULL;
   
} // CStringParamCmdOpt::~CStringParamCmdOpt





/**
 * Evaluate a parameter string for this option.
 *
 * The parameter evaluation for a string option is very simple. The
 * parameter value is only checked for the NULL pointer or the empty
 * string (if the parameter is not optional) and for the maximum string
 * length desired.
 *
 * @param pszStr                I: The parameter to take as the current option
 *                                 value string.
 *
 * @retval CLE_OK               The parameter could successfully be copied to
 *                              the internal maintained string object.
 * @retval CLE_INVALID_PARAM    The function argument is the NULL pointer or the
 *                              empty string and the parameter is not optional.
 */

CCmdLineEvaluator::CmdLineEvalResult_t CStringParamCmdOpt::EvalParam
   (const char *pszStr)
{
   if ((! pszStr || ! pszStr [0]) &&
       (m_iFlags & PARAM_OPTIONAL) == 0)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   
   (void) m_strCurrValue.assign (pszStr ? pszStr : "");
   
   if (m_strCurrValue.length () > m_nMaxLenValue)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }

   return (CCmdLineEvaluator::CLE_OK);
} // CStringParamCmdOpt::EvalParam





/**
 * Reset the current value to default and option not found.
 */

void CStringParamCmdOpt::ResetValue (void)
{
   CCmdOptBase::ResetValue ();

   m_strCurrValue = m_strDefaultValue;
   
} // CStringParamCmdOpt::ResetValue





/**
 * Copy the current value to the internally managed address.
 */

void CStringParamCmdOpt::CopyValue (void)
{
   if (m_fStringValueTarget)
   {
      if (m_pstrValueTarget)
      {
         *m_pstrValueTarget = m_strCurrValue;
      }
   }
   else
   {
      if (m_pszValueTarget)
      {
         (void) m_strCurrValue.copy (m_pszValueTarget, m_nMaxLenValue);
      }
   }
   
} // CStringParamCmdOpt::CopyValue





/**
 * Get a string representation of the current value.
 */

std::string CStringParamCmdOpt::GetValueString (void)
{
   return (m_strCurrValue);
} // CStringParamCmdOpt::GetValueString





/**
 * Get the current value of the option.
 */

const std::string &CStringParamCmdOpt::GetValue (void)
{
   return (m_strCurrValue);
} // CStringParamCmdOpt::GetValue





// --- Class for an option with an enum parameter derived from a string --

/**
 * Constructor.
 *
 * The constructor initializes all members of an object. Additionally this
 * object is added to the command line evaluator object specified as the
 * first parameter of this constructor.
 *
 * @param cmdLineEvaluator      I: The command line evaluator object this option
 *                                 object will be a member of. It is passed to
 *                                 the base constructor.
 * @param pszName               I: The name of the option.
 * @param iFlags                I: A bitmask of flags controlling the behaviour
 *                                 of this class and of the command line
 *                                 evaluator. It is a combination of the values
 *                                 of the enumeration Flags_t.
 * @param piValueTarget         I: A memory location for copying the current
 *                                 option value to when CopyValue() is called.
 *                                 If this feature is not needed, this may be
 *                                 the NULL pointer.
 * @param paEnumDef             I: The array of enumeration definitions for the
 *                                 relation between integer values and strings.
 *                                 This must not be the NULL pointer.
 * @param nLenEnumDef           I: The number of entries in the array at
 *                                 paEnumDef. This value must not be null, there
 *                                 must be at least one enumeration definition.
 * @param iDefaultValueIdx      I: The index for the default value for this
 *                                 option into the enumeration array. This is
 *                                 normally used for options with optional
 *                                 parameters when the parameter is left out on
 *                                 the command line.
 *
 * @throws std::invalid_argument
 *                              The enumeration array was specified as the NULL
 *                              pointer or the number of entries in this array
 *                              is zero.
 */

CEnumStringParamCmdOpt::CEnumStringParamCmdOpt
   (CCmdLineEvaluator &cmdLineEvaluator,
    const char        *pszName,
    int                iFlags,
    int               *piValueTarget,
    EnumDef_t         *paEnumDef,
    size_t             nLenEnumDef,
    int                iDefaultValueIdx):
   CCmdOptBase (cmdLineEvaluator, pszName, iFlags)
{
   if (! paEnumDef || nLenEnumDef <= 0)
   {
      throw std::invalid_argument ("invalid enum definition array parameter");
   }
   if (iDefaultValueIdx < 0 || (size_t) iDefaultValueIdx >= nLenEnumDef)
   {
      throw std::invalid_argument ("invalid default value index parameter");
   }
   
   m_piValueTarget    = piValueTarget;
   m_paEnumDef        = paEnumDef;
   m_nLenEnumDef      = nLenEnumDef;
   m_iDefaultValueIdx = iDefaultValueIdx;
   m_iCurrValueIdx    = iDefaultValueIdx;
   
} // CEnumStringParamCmdOpt::CEnumStringParamCmdOpt





/**
 * Destructor.
 */

CEnumStringParamCmdOpt::~CEnumStringParamCmdOpt (void)
{
   // PC-Lint/FlexLint complains about not releasing or zero'ing
   // member pointers, but this memory was not allocated in this object
   m_paEnumDef     = NULL;
   m_piValueTarget = NULL;
   
} // CEnumStringParamCmdOpt::~CEnumStringParamCmdOpt





/**
 * Evaluate a parameter string for this option.
 */

CCmdLineEvaluator::CmdLineEvalResult_t CEnumStringParamCmdOpt::EvalParam
   (const char *pszStr)
{
   bool fEq;
   int  i;
   
   if ((! pszStr || ! pszStr [0]) &&
       (m_iFlags & PARAM_OPTIONAL) == 0)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   
   fEq = false;
   for (i = 0; (size_t) i < m_nLenEnumDef; ++i)
   {
      if (pszStr && m_paEnumDef [i].pszString)
      {
         if ((m_iFlags & IGNORE_PARAM_CASE) != 0)
         {
#if defined (WIN32)
            fEq = (stricmp (pszStr, m_paEnumDef [i].pszString) == 0);
#else // WIN32
            fEq = (strcasecmp (pszStr, m_paEnumDef [i].pszString) == 0);
#endif // WIN32
         }
         else
         {
            fEq = (strcmp (pszStr, m_paEnumDef [i].pszString) == 0);
         }
      }
      else if ((! pszStr || ! pszStr [0]) &&
               (! m_paEnumDef [i].pszString || ! m_paEnumDef [i].pszString [0]))
      {
         fEq = true;
      }
      else
      {
         fEq = false;
      }
      if (fEq)
      {
         break;
      }
   }
   if (! fEq)
   {
      return (CCmdLineEvaluator::CLE_ERR_INVALID_PARAM);
   }
   m_iCurrValueIdx = i;
   
   return (CCmdLineEvaluator::CLE_OK);
} // CEnumStringParamCmdOpt::EvalParam





/**
 * Reset the current value to default and option not found.
 */

void CEnumStringParamCmdOpt::ResetValue (void)
{
   CCmdOptBase::ResetValue ();
   
   m_iCurrValueIdx = m_iDefaultValueIdx;
   
} // CEnumStringParamCmdOpt::ResetValue





/**
 * Copy the current value to the internally managed address.
 */

void CEnumStringParamCmdOpt::CopyValue (void)
{
   if (m_piValueTarget)
   {
      *m_piValueTarget = m_paEnumDef [m_iCurrValueIdx].iValue;
   }
   
} // CEnumStringParamCmdOpt::CopyValue





/**
 * Get a string representation of the current value.
 */

std::string CEnumStringParamCmdOpt::GetValueString (void)
{
   return (std::string (m_paEnumDef [m_iCurrValueIdx].pszString));
} // CEnumStringParamCmdOpt::GetValueString





/**
 * Get the current value of the option.
 */

int CEnumStringParamCmdOpt::GetValue (void)
{
   return (m_paEnumDef [m_iCurrValueIdx].iValue);
} // CEnumStringParamCmdOpt::GetValue





// === Definitions for public functions ==================================





// === Definitions for private functions =================================





} // namespace thwutil
